- changed headers so that any low level protocol-specific sizes and
[barry.git] / src / socket.cc
blobe6c06b4143ce37383fa81a73e099eb66cee64f53
1 ///
2 /// \file socket.cc
3 /// Class wrapper to encapsulate the Blackberry USB logical socket
4 ///
6 /*
7 Copyright (C) 2005, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "socket.h"
23 #include "usbwrap.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "debug.h"
27 #include "data.h"
28 #include "error.h"
31 using namespace Usb;
34 namespace Barry {
36 Socket::Socket(Device &dev, int writeEndpoint, int readEndpoint)
37 : m_dev(dev),
38 m_writeEp(writeEndpoint),
39 m_readEp(readEndpoint),
40 m_socket(0),
41 m_flag(0),
42 m_sequenceId(0),
43 m_lastStatus(0)
47 Socket::~Socket()
49 // trap exceptions in the destructor
50 try {
51 // a non-default socket has been opened, close it
52 Close();
54 catch( std::runtime_error &re ) {
55 // do nothing... log it?
56 dout("Exception caught in ~Socket: " << re.what());
60 void Socket::Open(uint16_t socket, uint8_t flag)
62 if( m_socket != 0 ) {
63 // already open
64 throw BError("Socket: already open");
67 // build open command
68 Barry::Packet packet;
69 packet.socket = 0;
70 packet.size = SB_SOCKET_PACKET_SIZE;
71 packet.command = SB_COMMAND_OPEN_SOCKET;
72 packet.data.socket.socket = socket;
73 packet.data.socket.param = flag;
75 Data send(&packet, packet.size);
76 Data receive;
77 if( !Send(send, receive) ) {
78 eeout(send, receive);
79 throw BError(GetLastStatus(), "Error opening socket");
82 // starting fresh, reset sequence ID
83 CheckSize(receive);
84 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
85 CheckSequence(receive);
87 // still need our ACK
88 Receive(receive);
91 CheckSize(receive, SB_SOCKET_PACKET_SIZE);
92 MAKE_PACKET(rpack, receive);
93 if( rpack->command != SB_COMMAND_OPENED_SOCKET ||
94 rpack->data.socket.socket != socket ||
95 rpack->data.socket.param != flag )
97 eout("Packet:\n" << receive);
98 throw BError("Socket: Bad OPENED packet in Open");
101 // success! save the socket
102 m_socket = socket;
103 m_flag = flag;
106 void Socket::Close()
108 if( m_socket != 0 ) {
109 // only close non-default sockets
111 // build close command
112 Barry::Packet packet;
113 packet.socket = 0;
114 packet.size = SB_SOCKET_PACKET_SIZE;
115 packet.command = SB_COMMAND_CLOSE_SOCKET;
116 packet.data.socket.socket = m_socket;
117 packet.data.socket.param = m_flag;
119 Data command(&packet, packet.size);
120 Data response;
121 if( !Send(command, response) ) {
122 // reset so this won't be called again
123 m_socket = 0;
124 m_flag = 0;
126 eeout(command, response);
127 throw BError(GetLastStatus(), "Error closing socket");
130 // starting fresh, reset sequence ID
131 CheckSize(response);
132 if( IS_COMMAND(response, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
133 CheckSequence(response);
135 // still need our ACK
136 Receive(response);
139 CheckSize(response, SB_SOCKET_PACKET_SIZE);
140 MAKE_PACKET(rpack, response);
141 if( rpack->command != SB_COMMAND_CLOSED_SOCKET ||
142 rpack->data.socket.socket != m_socket ||
143 rpack->data.socket.param != m_flag )
145 // reset so this won't be called again
146 m_socket = 0;
147 m_flag = 0;
149 eout("Packet:\n" << response);
150 throw BError("Socket: Bad CLOSED packet in Close");
153 // and finally, there always seems to be an extra read of
154 // an empty packet at the end... just throw it away
155 // Receive(response);
157 // reset socket and flag
158 m_socket = 0;
159 m_flag = 0;
163 // sends 'send' data to device, and waits for response, using
164 // "read first, write second" order observed in capture
166 // returns true on success, on failure, use GetLastStatus() for kernel
167 // URB error code
168 bool Socket::Send(const Data &send, Data &receive)
170 IO rd = m_dev.ABulkRead(m_readEp, receive);
171 IO wr = m_dev.ABulkWrite(m_writeEp, send);
173 // wait for response
174 rd.Wait();
175 wr.Wait();
177 m_lastStatus = rd.GetStatus();
178 receive.ReleaseBuffer(m_lastStatus >= 0 ? rd.GetSize() : 0);
179 return m_lastStatus >= 0;
182 bool Socket::Receive(Data &receive)
184 IO rd = m_dev.ABulkRead(m_readEp, receive);
186 rd.Wait();
188 m_lastStatus = rd.GetStatus();
189 receive.ReleaseBuffer(m_lastStatus >= 0 ? rd.GetSize() : 0);
190 return m_lastStatus >= 0;
193 // appends fragment to whole... if whole is empty, simply copies, and
194 // sets command to DATA instead of FRAGMENTED. Always updates the
195 // packet size of whole, to reflect the total size
196 void Socket::AppendFragment(Data &whole, const Data &fragment)
198 if( whole.GetSize() == 0 ) {
199 // empty, so just copy
200 whole = fragment;
202 else {
203 // has some data already, so just append
204 int size = whole.GetSize();
205 unsigned char *buf = whole.GetBuffer(size + fragment.GetSize());
206 MAKE_PACKET(fpack, fragment);
207 int fragsize = fragment.GetSize() - SB_FRAG_HEADER_SIZE;
209 memcpy(buf+size, &fpack->data.db.data.fragment, fragsize);
210 whole.ReleaseBuffer(size + fragsize);
213 // update whole's size and command type for future sanity
214 Barry::Packet *wpack = (Barry::Packet *) whole.GetBuffer();
215 wpack->size = (uint16_t) whole.GetSize();
216 wpack->command = SB_COMMAND_DB_DATA;
217 // don't need to call ReleaseBuffer here, since we're not changing
218 // the real size size
221 void Socket::CheckSequence(const Data &seq)
223 // if( m_socket == 0 ) {
224 // // don't do any sequence checking on socket 0
225 // return;
226 // }
228 MAKE_PACKET(spack, seq);
229 if( (unsigned int) seq.GetSize() < SB_SEQUENCE_PACKET_SIZE ) {
230 eout("Short sequence packet:\n" << seq);
231 throw BError("Socket: invalid sequence packet");
234 // we'll cheat here... if the packet's sequence is 0, we'll
235 // silently restart, otherwise, fail
236 uint32_t sequenceId = spack->data.sequence.sequenceId;
237 if( sequenceId == 0 ) {
238 // silently restart (will advance below)
239 m_sequenceId = 0;
241 else {
242 if( sequenceId != m_sequenceId ) {
243 if( m_socket != 0 ) {
244 eout("Socket sequence: " << m_sequenceId
245 << ". Packet sequence: " << sequenceId);
246 throw BError("Socket: out of sequence");
248 else {
249 dout("Bad sequence on socket 0: expected: "
250 << msequenceId
251 << ". Packet sequence: " << sequenceId);
256 // advance!
257 m_sequenceId++;
260 // sends the send packet down to the device, fragmenting if
261 // necessary, and returns the response in receive, defragmenting
262 // if needed
263 // Blocks until response received or timed out in Usb::Device
264 bool Socket::Packet(const Data &send, Data &receive)
267 // FIXME - this might be a good idea someday, or perhaps provide a wrapper
268 // function that forces the socket number to the correct current value,
269 // but putting it here means a copy on every packet.
271 // force socket to our socket
272 Data send = sendorig;
273 Barry::Packet *sspack = (Barry::Packet *)send.GetBuffer(2);
274 sspack->socket = GetSocket();
277 MAKE_PACKET(spack, send);
278 if( send.GetSize() < MIN_PACKET_SIZE ||
279 (spack->command != SB_COMMAND_DB_DATA &&
280 spack->command != SB_COMMAND_DB_DONE) )
282 // we don't do that around here
283 throw std::logic_error("Socket: unknown send data in Packet()");
286 if( send.GetSize() > MAX_PACKET_SIZE ) {
287 // not yet implemented
288 throw std::logic_error("Socket: fragmented sends not implemented");
291 Data inFrag;
292 receive.Zap();
294 // send command
295 if( !Send(send, inFrag) )
296 return false;
298 bool done = false, frag = false;
299 int blankCount = 0;
300 while( !done ) {
301 MAKE_PACKET(rpack, inFrag);
303 // check the packet's validity
304 if( inFrag.GetSize() > 0 ) {
305 blankCount = 0;
307 CheckSize(inFrag);
309 switch( rpack->command )
311 case SB_COMMAND_SEQUENCE_HANDSHAKE:
312 CheckSequence(inFrag);
313 break;
315 case SB_COMMAND_DB_DATA:
316 if( frag ) {
317 AppendFragment(receive, inFrag);
319 else {
320 receive = inFrag;
322 done = true;
323 break;
325 case SB_COMMAND_DB_FRAGMENTED:
326 AppendFragment(receive, inFrag);
327 frag = true;
328 break;
330 case SB_COMMAND_DB_DONE:
331 receive = inFrag;
332 done = true;
333 break;
335 default:
336 eout("Command: " << rpack->command << inFrag);
337 throw BError("Socket: unhandled packet in Packet()");
338 break;
341 else {
342 blankCount++;
343 //std::cerr << "Blank! " << blankCount << std::endl;
344 if( blankCount == 10 ) {
345 // only ask for more data on stalled sockets
346 // for so long
347 throw BError("Socket: 10 blank packets received");
351 if( !done ) {
352 // not done yet, ask for another read
353 if( !Receive(inFrag) )
354 return false;
358 return true;
361 bool Socket::NextRecord(Data &receive)
363 Barry::Packet packet;
364 packet.socket = GetSocket();
365 packet.size = 7;
366 packet.command = SB_COMMAND_DB_DONE;
367 packet.data.db.tableCmd = 0;
368 packet.data.db.data.db.operation = 0;
370 Data command(&packet, packet.size);
371 return Packet(command, receive);
375 } // namespace Barry