- added fragmented send support to Socket class
[barry.git] / src / socket.cc
blob2cfa94873f709f5b9242c2bf652a72837a097ac1
1 ///
2 /// \file socket.cc
3 /// Class wrapper to encapsulate the Blackberry USB logical socket
4 ///
6 /*
7 Copyright (C) 2005-2006, 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());
61 // Open
63 /// Open a logical socket on the device.
64 ///
65 /// Both the socket number and the flag are based on the response to the
66 /// SELECT_MODE command. See Controller::SelectMode() for more info
67 /// on this.
68 ///
69 /// The packet sequence is normal for most socket operations.
70 ///
71 /// - Down: command packet with OPEN_SOCKET
72 /// - Up: optional sequence handshake packet
73 /// - Up: command response, which repeats the socket and flag data
74 /// as confirmation
75 ///
76 /// \exception Barry::BError
77 ///
78 void Socket::Open(uint16_t socket, uint8_t flag)
80 if( m_socket != 0 ) {
81 // already open
82 throw BError("Socket: already open");
85 // build open command
86 Barry::Packet packet;
87 packet.socket = 0;
88 packet.size = SB_SOCKET_PACKET_SIZE;
89 packet.command = SB_COMMAND_OPEN_SOCKET;
90 packet.u.socket.socket = socket;
91 packet.u.socket.param = flag;
93 Data send(&packet, packet.size);
94 Data receive;
95 if( !Send(send, receive) ) {
96 eeout(send, receive);
97 throw BError(GetLastStatus(), "Error opening socket");
100 // starting fresh, reset sequence ID
101 CheckSize(receive);
102 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
103 CheckSequence(receive);
105 // still need our ACK
106 Receive(receive);
109 CheckSize(receive, SB_SOCKET_PACKET_SIZE);
110 MAKE_PACKET(rpack, receive);
111 if( rpack->command != SB_COMMAND_OPENED_SOCKET ||
112 rpack->u.socket.socket != socket ||
113 rpack->u.socket.param != flag )
115 eout("Packet:\n" << receive);
116 throw BError("Socket: Bad OPENED packet in Open");
119 // success! save the socket
120 m_socket = socket;
121 m_flag = flag;
125 // Close
127 /// Closes a non-default socket (i.e. non-zero socket number)
129 /// The packet sequence is just like Open(), except the command is
130 /// CLOSE_SOCKET.
132 /// \exception Barry::BError
134 void Socket::Close()
136 if( m_socket != 0 ) {
137 // only close non-default sockets
139 // build close command
140 Barry::Packet packet;
141 packet.socket = 0;
142 packet.size = SB_SOCKET_PACKET_SIZE;
143 packet.command = SB_COMMAND_CLOSE_SOCKET;
144 packet.u.socket.socket = m_socket;
145 packet.u.socket.param = m_flag;
147 Data command(&packet, packet.size);
148 Data response;
149 if( !Send(command, response) ) {
150 // reset so this won't be called again
151 m_socket = 0;
152 m_flag = 0;
154 eeout(command, response);
155 throw BError(GetLastStatus(), "Error closing socket");
158 // starting fresh, reset sequence ID
159 CheckSize(response);
160 if( IS_COMMAND(response, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
161 CheckSequence(response);
163 // still need our ACK
164 Receive(response);
167 CheckSize(response, SB_SOCKET_PACKET_SIZE);
168 MAKE_PACKET(rpack, response);
169 if( rpack->command != SB_COMMAND_CLOSED_SOCKET ||
170 rpack->u.socket.socket != m_socket ||
171 rpack->u.socket.param != m_flag )
173 // reset so this won't be called again
174 m_socket = 0;
175 m_flag = 0;
177 eout("Packet:\n" << response);
178 throw BError("Socket: Bad CLOSED packet in Close");
181 // and finally, there always seems to be an extra read of
182 // an empty packet at the end... just throw it away
183 // Receive(response);
185 // reset socket and flag
186 m_socket = 0;
187 m_flag = 0;
192 // Send
194 /// Sends 'send' data to device, and waits for response, using
195 /// "read first, write second" order observed in capture.
197 /// \returns bool
198 /// - true on success
199 /// - false on failure, use GetLastStatus() for kernel
200 /// URB error code
202 bool Socket::Send(const Data &send, Data &receive)
204 // Special case: it seems that sending packets with a size that's an
205 // exact multiple of 0x40 causes the device to get confused.
207 // To get around that, it is observed in the captures that the size
208 // is sent in a special 3 byte packet before the real packet.
209 // Check for this case here.
211 if( (send.GetSize() % 0x40) == 0 ) {
212 SizePacket packet;
213 packet.size = send.GetSize();
214 packet.buffer[2] = 0; // zero the top byte
215 Data sizeCommand(&packet, 3);
217 IO wr = m_dev.ABulkWrite(m_writeEp, sizeCommand);
218 wr.Wait();
221 IO rd = m_dev.ABulkRead(m_readEp, receive);
222 IO wr = m_dev.ABulkWrite(m_writeEp, send);
224 // wait for response
225 rd.Wait();
226 wr.Wait();
228 m_lastStatus = rd.GetStatus();
229 receive.ReleaseBuffer(m_lastStatus >= 0 ? rd.GetSize() : 0);
230 return m_lastStatus >= 0;
233 bool Socket::Receive(Data &receive)
235 IO rd = m_dev.ABulkRead(m_readEp, receive);
237 rd.Wait();
239 m_lastStatus = rd.GetStatus();
240 receive.ReleaseBuffer(m_lastStatus >= 0 ? rd.GetSize() : 0);
241 return m_lastStatus >= 0;
244 // appends fragment to whole... if whole is empty, simply copies, and
245 // sets command to DATA instead of FRAGMENTED. Always updates the
246 // packet size of whole, to reflect the total size
247 void Socket::AppendFragment(Data &whole, const Data &fragment)
249 if( whole.GetSize() == 0 ) {
250 // empty, so just copy
251 whole = fragment;
253 else {
254 // has some data already, so just append
255 int size = whole.GetSize();
256 unsigned char *buf = whole.GetBuffer(size + fragment.GetSize());
257 MAKE_PACKET(fpack, fragment);
258 int fragsize = fragment.GetSize() - SB_FRAG_HEADER_SIZE;
260 memcpy(buf+size, &fpack->u.db.u.fragment, fragsize);
261 whole.ReleaseBuffer(size + fragsize);
264 // update whole's size and command type for future sanity
265 Barry::Packet *wpack = (Barry::Packet *) whole.GetBuffer();
266 wpack->size = (uint16_t) whole.GetSize();
267 wpack->command = SB_COMMAND_DB_DATA;
268 // don't need to call ReleaseBuffer here, since we're not changing
269 // the real size size
272 // If offset is 0, starts fresh, taking the first fragment packet size chunk
273 // out of whole and creating a sendable packet in fragment. Returns the
274 // next offset if there is still more data, or 0 if finished.
275 unsigned int Socket::MakeNextFragment(const Data &whole, Data &fragment, unsigned int offset)
277 // sanity check
278 if( whole.GetSize() < SB_FRAG_HEADER_SIZE ) {
279 eout("Whole packet too short to fragment: " << whole.GetSize());
280 throw BError("Socket: Whole packet too short to fragment");
283 // calculate size
284 unsigned int todo = whole.GetSize() - SB_FRAG_HEADER_SIZE - offset;
285 unsigned int nextOffset = 0;
286 if( todo > (MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE) ) {
287 todo = MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE;
288 nextOffset = offset + todo;
291 // create fragment header
292 unsigned char *buf = fragment.GetBuffer(SB_FRAG_HEADER_SIZE + todo);
293 memcpy(buf, whole.GetData(), SB_FRAG_HEADER_SIZE);
295 // copy over a fragment size of data
296 memcpy(buf + SB_FRAG_HEADER_SIZE, whole.GetData() + SB_FRAG_HEADER_SIZE + offset, todo);
298 // update fragment's size and command type
299 Barry::Packet *wpack = (Barry::Packet *) buf;
300 wpack->size = (uint16_t) (todo + SB_FRAG_HEADER_SIZE);
301 if( nextOffset )
302 wpack->command = SB_COMMAND_DB_FRAGMENTED;
303 else
304 wpack->command = SB_COMMAND_DB_DATA;
306 // adjust the new fragment size
307 fragment.ReleaseBuffer(SB_FRAG_HEADER_SIZE + todo);
309 // return next round
310 return nextOffset;
313 void Socket::CheckSequence(const Data &seq)
315 // if( m_socket == 0 ) {
316 // // don't do any sequence checking on socket 0
317 // return;
318 // }
320 MAKE_PACKET(spack, seq);
321 if( (unsigned int) seq.GetSize() < SB_SEQUENCE_PACKET_SIZE ) {
322 eout("Short sequence packet:\n" << seq);
323 throw BError("Socket: invalid sequence packet");
326 // we'll cheat here... if the packet's sequence is 0, we'll
327 // silently restart, otherwise, fail
328 uint32_t sequenceId = spack->u.sequence.sequenceId;
329 if( sequenceId == 0 ) {
330 // silently restart (will advance below)
331 m_sequenceId = 0;
333 else {
334 if( sequenceId != m_sequenceId ) {
335 if( m_socket != 0 ) {
336 eout("Socket sequence: " << m_sequenceId
337 << ". Packet sequence: " << sequenceId);
338 throw BError("Socket: out of sequence");
340 else {
341 dout("Bad sequence on socket 0: expected: "
342 << msequenceId
343 << ". Packet sequence: " << sequenceId);
348 // advance!
349 m_sequenceId++;
352 // sends the send packet down to the device, fragmenting if
353 // necessary, and returns the response in receive, defragmenting
354 // if needed
355 // Blocks until response received or timed out in Usb::Device
356 bool Socket::Packet(const Data &send, Data &receive)
359 // FIXME - this might be a good idea someday, or perhaps provide a wrapper
360 // function that forces the socket number to the correct current value,
361 // but putting it here means a copy on every packet.
363 // force socket to our socket
364 Data send = sendorig;
365 Barry::Packet *sspack = (Barry::Packet *)send.GetBuffer(2);
366 sspack->socket = GetSocket();
369 MAKE_PACKET(spack, send);
370 if( send.GetSize() < MIN_PACKET_SIZE ||
371 (spack->command != SB_COMMAND_DB_DATA &&
372 spack->command != SB_COMMAND_DB_DONE) )
374 // we don't do that around here
375 throw std::logic_error("Socket: unknown send data in Packet()");
378 Data inFrag;
379 receive.Zap();
381 if( send.GetSize() <= MAX_PACKET_SIZE ) {
382 // send non-fragmented
383 if( !Send(send, inFrag) )
384 return false;
386 else {
387 // send fragmented
388 unsigned int offset = 0;
389 Data outFrag;
391 do {
392 offset = MakeNextFragment(send, outFrag, offset);
393 if( !Send(outFrag, inFrag) )
394 return false;
396 MAKE_PACKET(rpack, inFrag);
397 // only process sequence handshakes... once we
398 // get to the last fragment, we fall through to normal
399 // processing below
400 if( offset && inFrag.GetSize() > 0 ) {
402 CheckSize(inFrag);
404 switch( rpack->command )
406 case SB_COMMAND_SEQUENCE_HANDSHAKE:
407 CheckSequence(inFrag);
408 break;
410 default:
411 eout("Command: " << rpack->command << inFrag);
412 throw BError("Socket: unhandled packet in Packet()");
413 break;
418 } while( offset > 0 );
421 bool done = false, frag = false;
422 int blankCount = 0;
423 while( !done ) {
424 MAKE_PACKET(rpack, inFrag);
426 // check the packet's validity
427 if( inFrag.GetSize() > 0 ) {
428 blankCount = 0;
430 CheckSize(inFrag);
432 switch( rpack->command )
434 case SB_COMMAND_SEQUENCE_HANDSHAKE:
435 CheckSequence(inFrag);
436 break;
438 case SB_COMMAND_DB_DATA:
439 if( frag ) {
440 AppendFragment(receive, inFrag);
442 else {
443 receive = inFrag;
445 done = true;
446 break;
448 case SB_COMMAND_DB_FRAGMENTED:
449 AppendFragment(receive, inFrag);
450 frag = true;
451 break;
453 case SB_COMMAND_DB_DONE:
454 receive = inFrag;
455 done = true;
456 break;
458 default:
459 eout("Command: " << rpack->command << inFrag);
460 throw BError("Socket: unhandled packet in Packet()");
461 break;
464 else {
465 blankCount++;
466 //std::cerr << "Blank! " << blankCount << std::endl;
467 if( blankCount == 10 ) {
468 // only ask for more data on stalled sockets
469 // for so long
470 throw BError("Socket: 10 blank packets received");
474 if( !done ) {
475 // not done yet, ask for another read
476 if( !Receive(inFrag) )
477 return false;
481 return true;
484 bool Socket::NextRecord(Data &receive)
486 Barry::Packet packet;
487 packet.socket = GetSocket();
488 packet.size = 7;
489 packet.command = SB_COMMAND_DB_DONE;
490 packet.u.db.tableCmd = 0;
491 packet.u.db.u.command.operation = 0;
493 Data command(&packet, packet.size);
494 return Packet(command, receive);
498 } // namespace Barry