5 // Created by Nathan Oates on Sun Aug 01 2004.
6 // Copyright (c) 2004-7 Nathan Oates nathan@noates.com All rights reserved.
9 /* This file was modified by Kalle Olavi Niemitalo on 2007-10-18. */
11 // includes code based on uproar, license noted below:
13 // Copyright (c) 2001 Kasima Tharnpipitchai <me@kasima.org>
14 #import "TFUSBController.h"
15 #import "UIElements.h"
17 @implementation TFUSBController
21 * This source code is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Public License as published
23 * by the Free Software Foundation; either version 2 of the License,
24 * or (at your option) any later version.
26 * This source code is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
29 * Please refer to the GNU Public License for more details.
31 * You should have received a copy of the GNU Public License along with
32 * this source code; if not, write to:
33 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38 /* Much of the code that initializes and closes the device is from Apple
39 * and is released under the license below.
43 * © Copyright 2001 Apple Computer, Inc. All rights reserved.
45 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in
46 * consideration of your agreement to the following terms, and your use, installation,
47 * modification or redistribution of this Apple software constitutes acceptance of these
48 * terms. If you do not agree with these terms, please do not use, install, modify or
49 * redistribute this Apple software.
51 * In consideration of your agreement to abide by the following terms, and subject to these
52 * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this
53 * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute
54 * the Apple Software, with or without modifications, in source and/or binary forms; provided
55 * that if you redistribute the Apple Software in its entirety and without modifications, you
56 * must retain this notice and the following text and disclaimers in all such redistributions
57 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple
58 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software
59 * without specific prior written permission from Apple. Except as expressly stated in this
60 * notice, no other rights or licenses, express or implied, are granted by Apple herein,
61 * including but not limited to any patent rights that may be infringed by your derivative
62 * works or by other works in which the Apple Software may be incorporated.
64 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
65 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
66 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
67 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
69 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
70 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
71 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
72 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
73 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
74 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
78 #define TopfieldVendorID 4571
82 #define TF5kProdID 4096
88 if (self = [super init]) {
91 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
92 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
93 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
98 void hexDump(UInt8 *buf, int len) {
99 int row, col, maxrows;
102 if (len % 16) maxrows++;
103 for (row=0; row< maxrows; row++) {
104 for (col=0; col<16; col++) {
105 if (!(col%2)) printf(" ");
106 printf("%02x", buf[row*16 + col] & 0xff);
109 for (col=0; col<16; col++) {
110 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
111 printf("%c", buf[row*16 + col]);
113 else { printf("."); }
121 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
122 UInt8 *outBuf, UInt32 len, int type)
128 printf(("sending:\n"));
129 hexDump(outBuf, len);
131 sendLen = ((len/kBlockSize))*kBlockSize;
132 if (len % kBlockSize)
133 sendLen += kBlockSize;
134 if ((sendLen % 0x200) == 0)
135 sendLen += kBlockSize;
137 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
139 printf("write err: %08x\n", err);
146 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
147 UInt8 *inBuf, UInt32 dataLen, int type) {
151 if (dataLen > kMaxXferSize) return 1;
153 len = (dataLen/kBlockSize) * kBlockSize;
154 if (dataLen % kBlockSize)
157 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
159 printf("read err 2: %08x\n", err);
160 printf("resetting\n");
161 err = (*intf)->ClearPipeStallBothEnds(intf, 2);
166 printf(("receiving: \n"));
176 int dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
179 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
180 IOUSBInterfaceInterface197 **intf;
181 IOUSBDeviceInterface197 **dev;
183 UInt8 numPipes, confNum, dSpeed;
186 err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
189 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
190 return kUproarDeviceErr;
192 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
193 (*iodev)->Release(iodev); // done with this
197 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
198 return kUproarDeviceErr;
202 err = (*intf)->USBInterfaceOpen(intf);
205 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
206 return kUproarDeviceErr;
208 err = (*intf)->GetNumEndpoints(intf, &numPipes);
211 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
212 return kUproarDeviceErr;
215 printf("dealWithInterface: found %d pipes\n", numPipes);
217 err = (*intf)->GetConfigurationValue(intf, &confNum);
218 err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
224 printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
225 connectedSpeed = dSpeed;
226 return kUproarSuccess;
230 int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
233 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
234 IOUSBDeviceInterface197 **dev;
237 IOUSBConfigurationDescriptorPtr confDesc;
238 IOUSBFindInterfaceRequest interfaceRequest;
239 io_iterator_t iterator;
240 io_service_t usbInterfaceRef;
244 err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
247 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
248 return kUproarDeviceErr;
250 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
251 (*iodev)->Release(iodev);
256 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
257 return kUproarDeviceErr;
259 err = (*dev)->USBDeviceOpen(dev);
262 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
263 return kUproarDeviceErr;
265 err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
268 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
269 return kUproarDeviceErr;
271 printf("dealWithDevice: found %d configurations\n", numConf);
272 err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
275 printf("dealWithDevice:unable to get config descriptor for index 0\n");
276 return kUproarDeviceErr;
278 err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
281 printf("dealWithDevice: unable to set the configuration\n");
282 return kUproarDeviceErr;
285 interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare; // requested class
286 interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
287 interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
288 interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting
290 err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
293 printf("dealWithDevice: unable to create interface iterator\n");
294 return kUproarDeviceErr;
297 while (usbInterfaceRef = IOIteratorNext(iterator))
299 printf("found interface: %p\n", (void*)usbInterfaceRef);
300 err = dealWithInterface(usbInterfaceRef, device);
301 IOObjectRelease(usbInterfaceRef); // no longer need this reference
305 IOObjectRelease(iterator);
307 if ((!found) || (err))
308 return kUproarDeviceErr;
310 return kUproarSuccess;
314 int initDevice(USBDeviceContext *device){
315 mach_port_t masterPort = 0;
317 CFMutableDictionaryRef matchingDictionary = 0; // requires <IOKit/IOKitLib.h>
318 short idVendor = TopfieldVendorID;
319 short idProduct = TF5kProdID;
320 CFNumberRef numberRef;
321 io_iterator_t iterator = 0;
322 io_service_t usbDeviceRef;
325 err = IOMasterPort(bootstrap_port, &masterPort);
329 printf("Anchortest: could not create master port, err = %08x\n", err);
332 matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); // requires <IOKit/usb/IOUSBLib.h>
333 if (!matchingDictionary)
335 printf("Anchortest: could not create matching dictionary\n");
338 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
341 printf("Anchortest: could not create CFNumberRef for vendor\n");
344 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
345 CFRelease(numberRef);
347 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
350 printf("Anchortest: could not create CFNumberRef for product\n");
353 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
354 CFRelease(numberRef);
357 err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
358 matchingDictionary = 0; // this was consumed by the above call
360 while (usbDeviceRef = IOIteratorNext(iterator))
362 printf("Found device %p\n", (void*)usbDeviceRef);
363 err = dealWithDevice(usbDeviceRef, device);
364 IOObjectRelease(usbDeviceRef); // no longer need this reference
369 IOObjectRelease(iterator);
371 mach_port_deallocate(mach_task_self(), masterPort);
372 if ((!found) || (err))
373 return kUproarDeviceErr;
375 return kUproarSuccess;
379 - (void) closeDevice:(USBDeviceContext *) device
381 IOUSBInterfaceInterface197 **intf;
382 IOUSBDeviceInterface197 **dev;
388 err = (*intf)->USBInterfaceClose(intf);
391 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
393 err = (*intf)->Release(intf);
396 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
399 err = (*dev)->USBDeviceClose(dev);
402 printf("dealWithDevice: error closing device - %08x\n", err);
403 (*dev)->Release(dev);
405 err = (*dev)->Release(dev);
408 printf("dealWithDevice: error releasing device - %08x\n", err);
412 - (USBDeviceContext*) initializeUSB {
413 USBDeviceContext *device;
416 device = malloc(sizeof(USBDeviceContext));
417 err = initDevice(device);
419 printf("Could not connect to Topfield\n");
423 printf("Connected to Topfield\n\n");
428 - (UIElements*) uiElements {
429 return [NSApp delegate];
432 - (NSData*) prepareFailWithECode:(UInt32)eCode {
433 const UInt32 eCode_bigendian = EndianU32_NtoB(eCode);
434 NSData* data = [NSData dataWithBytes:&eCode_bigendian length:4];
435 if (data == nil) // out of memory presumably
437 return [self prepareCommand:USB_Fail withData:data];
440 // Prepare a communication block that asks the Toppy to list the files
441 // in a directory. Return the communication block, or nil on error.
442 - (NSData*) prepareCmdHddDirWithPath:(NSString*)path {
443 NSData *pathData = [path dataUsingEncoding:NSISOLatin1StringEncoding];
444 if (pathData == nil) // path contains characters unsupported by encoding
446 NSMutableData *cmdData = [NSMutableData dataWithData:pathData];
447 [cmdData increaseLengthBy:1]; // not sure if need to pad here?
448 return [self prepareCommand:USB_CmdHddDir withData:cmdData];
451 // Check the size and CRC of a received communication block.
452 // Return an ECODE to be sent back, or USB_OK.
453 - (UInt32) checkCommunicationBlock:(NSData*)block {
454 if (block == nil) return USB_Err_RunFail;
455 size_t length = [block length];
456 if (length < 8) return USB_Err_Size;
457 UInt16 crc16_bigendian;
458 [block getBytes:&crc16_bigendian range:(NSRange){2,2}];
459 UInt16 crc = EndianU16_BtoN(crc16_bigendian);
460 UInt16 calcCrc = [self findCRC:(const UInt8*)[block bytes]+4 length:length-4];
461 if (crc != calcCrc) return USB_Err_Crc;
465 - (UInt32) cmdFromCommunicationBlock:(NSData*)block {
466 UInt32 cmd_bigendian;
467 [block getBytes:&cmd_bigendian range:(NSRange){4,4}];
468 return EndianU32_BtoN(cmd_bigendian);
471 - (id) getFileListForPath:(NSString*) path {
472 if (myContext == nil)
475 NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
476 if (hddListCmd == nil)
479 [self checkUSB:myContext]; // sends cancel and waits for response
480 int contiguousErrors = 0;
481 [[dh fileList] removeAllObjects];
482 NSData* response = [self sendCommand:hddListCmd toDevice:myContext
483 expectResponse:YES careForReturn:YES];
484 while (response != nil && contiguousErrors++ < 5) {
485 const UInt32 eCode = [self checkCommunicationBlock:response];
486 if (eCode != USB_OK) {
487 response = [self sendCommand:[self prepareFailWithECode:eCode]
488 toDevice:myContext expectResponse:YES careForReturn:YES];
489 } else switch ([self cmdFromCommunicationBlock:response]) {
491 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
492 [[dh fileList] removeAllObjects];
493 response = [self sendCommand:hddListCmd toDevice:myContext
494 expectResponse:YES careForReturn:YES];
497 contiguousErrors = 0;
499 // Swapping sometimes adds a byte of padding. The following uses
500 // only complete 144-byte structures and so ignores such padding.
501 for (i=0; 8+(i+1)*114 <= [response length]; i++) {
502 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
503 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:typeFile];
504 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
505 [dh convertRawDataToUseful:tfFile];
506 [[dh fileList] addObject:tfFile];
509 response = [self sendCommand:[self prepareCommand:USB_Success withData:nil]
510 toDevice:myContext expectResponse:YES careForReturn:YES];
512 case USB_DataHddDirEnd:
513 contiguousErrors = 0;
517 [self checkUSB:myContext]; // cancel whatever is going on
518 [[dh fileList] removeAllObjects];
519 response = [self sendCommand:hddListCmd toDevice:myContext
520 expectResponse:YES careForReturn:YES];
525 [tableView reloadData];
526 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
527 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
531 -(UInt16) findCRC:(const UInt8*)p length:(size_t) n
533 UInt16 m_crc16 = 0x0000;
534 static const UInt16 crc16Tbl[256] = {
535 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
536 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
537 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
538 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
539 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
540 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
541 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
542 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
543 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
544 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
545 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
546 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
547 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
548 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
549 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
550 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
551 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
552 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
553 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
554 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
555 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
556 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
557 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
558 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
559 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
560 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
561 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
562 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
563 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
564 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
565 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
566 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
569 for(i = 0; i < n; i++)
570 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
574 - (NSData*) prepareCommand:(unsigned int)cmd withData:(NSData*) inData {
576 unsigned short int length = 0;
580 unsigned short int l = [inData length];
583 const UInt16 length_bigendian = EndianU16_NtoB(length);
584 NSMutableData* toSend = [NSMutableData dataWithBytes:&length_bigendian length:2];
585 const UInt32 cmd_bigendian = EndianU32_NtoB(cmd);
586 NSMutableData* build = [NSMutableData dataWithBytes:&cmd_bigendian length:4];
588 [build appendData:inData];
591 memset(test, 0, length);
592 [build getBytes:test length:[build length]];
593 unsigned short int crc = [self findCRC:test length:[build length]];
594 const UInt16 crc_bigendian = EndianU16_NtoB(crc);
595 [toSend appendData:[NSMutableData dataWithBytes:&crc_bigendian length:2]];
596 [toSend appendData:build];
598 toSend = [self swap:toSend];
602 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
603 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
604 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
605 // [progressBar setDoubleValue:0];
606 // [progressTime setDoubleValue:0];
607 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
608 [[[self uiElements] currentlyField] setStringValue:
609 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
610 [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
611 [[[self uiElements] currentlyField] displayIfNeeded];
612 //construct file send request
613 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
614 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
615 char dir = USB_FileToHost;
616 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
617 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
618 const SInt16 nsize_bigendian = EndianS16_NtoB(nsize);
619 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
620 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
621 [build appendData:d];
622 dir = 0x5c; // 0x5c = "/"
623 [build appendData:[NSData dataWithBytes:&dir length:1]];
624 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
626 [build appendData:[NSData dataWithBytes:&dir length:1]];
627 //add 8 byte offset here
628 const UInt64 offset_bigendian = EndianU64_NtoB(offset);
629 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
631 //prepackage commands to send
632 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
633 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
634 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
639 [self turnTurboOn:YES];
641 [self checkUSB:myContext]; //turbo has a check itself
642 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
644 NSDate* startTime = [NSDate date];
645 startTime = [startTime addTimeInterval:(0-existingTime)];
646 // send start request and get response
647 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
648 const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
649 NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
650 if ([data length] < 16) {
651 NSLog(@"Incorrect Data length");
654 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
655 if (![responseWanted isEqualToData:responseToCheck]) {
656 NSLog(@"Unexpected response from Toppy during download");
660 // clean up data and prepare path to save it
661 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
662 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
664 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
666 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
667 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
668 [outFile seekToFileOffset:offset];
669 [outFile writeData:finalData];
670 if (looping) { // loop for multiple data sends (ie files > 64k)
672 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
674 NSLog(@"large file detected - low GUI update rate");
677 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
678 double amountReceived = 0xfe00 + offset;
679 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
680 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
681 if ([priorityTransferQueue count] != 0) {
682 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
683 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
684 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
685 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
686 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
688 atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
689 // send reset again just to make sure!
690 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
692 //now add the right modification date to the file
693 [[NSFileManager defaultManager]
694 changeFileAttributes:[NSDictionary
695 dictionaryWithObject:[fileInfo objectForKey:@"date"]
696 forKey:@"NSFileModificationDate"]
698 [self turnTurboOn:NO];
702 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
703 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
704 amountReceived += 0xfe00;
705 if (timesAround < 8 || timesAround % updateRate == 0) {
706 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
707 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
708 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
709 fileSize, @"size", startTime, @"startTime", nil]];
711 if ([data length] > 16){ //there is something to read
712 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
713 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
714 [outFile writeData:finalData];
715 [outFile synchronizeFile];
721 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
723 //now add the right modification date to the file
724 [[NSFileManager defaultManager]
725 changeFileAttributes:[NSDictionary
726 dictionaryWithObject:[fileInfo objectForKey:@"date"]
727 forKey:@"NSFileModificationDate"]
729 [self turnTurboOn:NO];
730 if (looping) [[self uiElements] finishTransfer];
734 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
735 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
736 careForReturn:(BOOL) careFactor{
738 int cmdLength = [fullyPackagedCommand length];
739 unsigned char outBuffer[cmdLength];
740 memset(outBuffer, 0, cmdLength);
741 // NSLog(@"send: %@", [fullyPackagedCommand description]);
742 [fullyPackagedCommand getBytes:outBuffer];
744 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
746 NSLog(@"sendError: %08x\n");
748 if (! getResponse) return nil;
750 int inLen = 0xFFFF; // i think this is biggest needed?
751 unsigned char inBuf[inLen];
752 memset(inBuf, 0, inLen);
753 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
755 NSLog(@"inError: %08x\n", err);
757 if (! careFactor) return nil;
758 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
759 data = [self swap:data];
760 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
761 return [data subdataWithRange:(NSRange) {0,inLen}];
764 -(NSMutableData*) swap: (NSData*) inData {
765 int len = [inData length];
768 uneven = 1; // prepare space for padding
772 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
773 NSLog(@"ERROR: Malloc failed");
774 return [NSMutableData dataWithData:inData];
776 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
777 NSLog(@"ERROR: Malloc failed");
778 return [NSMutableData dataWithData:inData];
780 [inData getBytes:array length:len];
781 swab(array, outarray, len+uneven);
782 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
783 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
789 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
790 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
791 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
792 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
793 USBDeviceContext* dev = myContext;
794 // prepare Send command
795 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
796 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
797 char dir = USB_FileToDevice;
798 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
799 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
800 const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
801 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
802 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
803 [build appendData:d];
804 dir = 0x5c; // 0x5c = "/"
805 [build appendData:[NSData dataWithBytes:&dir length:1]];
806 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
807 if ([fname length] < 95)
808 [build increaseLengthBy:95-[fname length]];
810 [build appendData:[NSData dataWithBytes:&dir length:1]];
811 UInt64 offset_bigendian = EndianU64_NtoB(offset);
812 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
814 // prepackage commands to send
815 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
816 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
817 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
820 NSDate* startTime = [NSDate date];
821 startTime = [startTime addTimeInterval:(0-existingTime)];
824 [self turnTurboOn:YES];
827 // now the proper commands
828 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
829 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
830 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
831 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
832 NSData* responseToCheck = nil;
834 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
835 [fileHandle seekToFileOffset:offset];
837 if ([priorityTransferQueue count] != 0) {
838 //break out and create a new transfer to continue it
839 NSLog(@"pausing upload");
840 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
841 fileToUpload,@"filename",
842 [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
843 curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
844 [NSNumber numberWithUnsignedLongLong:offset],@"offset",
845 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
847 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
848 [self turnTurboOn:NO];
851 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
852 offset_bigendian = EndianU64_NtoB(offset);
853 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
854 [fileData appendData:[fileHandle readDataOfLength:rate]];
856 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
857 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
858 if ([data length] >= 8)
859 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
860 else responseToCheck = nil;
861 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
863 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
864 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
865 [NSNumber numberWithDouble:(double)offset], @"offset",
866 [NSNumber numberWithDouble:(double)size], @"size",
867 startTime, @"startTime", nil]];
870 } while (offset < size && [[[self uiElements] isConnected] intValue]);
871 if ([[[self uiElements] isConnected] intValue])
872 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
873 [fileHandle closeFile];
874 [[self uiElements] goToPath:[[self uiElements]currentPath]];
875 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
876 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
877 [self turnTurboOn:NO];
878 [[self uiElements] finishTransfer];
882 - (void) turnTurboOn:(BOOL) turnOn {
883 if (![[[self uiElements] isConnected] intValue]) return;
884 [self checkUSB:myContext];
888 const SInt32 mode_bigendian = EndianS32_NtoB(mode);
889 NSData* modeData = [NSData dataWithBytes:&mode_bigendian length:4];
890 NSLog([modeData description]); //debugging
891 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
892 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
896 return connectedSpeed;
899 -(void) setDebug:(int)mode {
901 NSLog(@"Debug level: %i", debug);
904 -(void) setRate:(int) newRate {
906 NSLog(@"New rate set: %i", rate);
909 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
911 progressTime = timeField;
915 -(void) updateProgress:(NSDictionary*) inDict {
916 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
917 double offset = [[inDict objectForKey:@"offset"] doubleValue];
918 double size = [[inDict objectForKey:@"size"] doubleValue];
919 NSDate* startTime = [inDict objectForKey:@"startTime"];
920 [progressBar setDoubleValue:((double)offset/size*100)];
921 [progressBar displayIfNeeded];
922 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
923 [progressTime displayIfNeeded];
928 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
931 char sp = 20; //padding
932 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
935 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
938 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
941 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
944 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
945 [self checkUSB:myContext];
946 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
947 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
948 char dir = 0x5c; // 0x5c = "\"
949 [build appendData:[NSData dataWithBytes:&dir length:1]];
950 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
952 [build appendData:[NSData dataWithBytes:&dir length:1]];
953 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
954 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
957 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
958 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
959 [self checkUSB:myContext];
960 unsigned short int i = [oldName length]+[currentPath length]+2;
961 UInt16 i_bigendian = EndianU16_NtoB(i);
962 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
963 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
965 [build appendData:[NSData dataWithBytes:&dir length:1]];
966 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
968 [build appendData:[NSData dataWithBytes:&dir length:1]];
969 i = [newName length]+[currentPath length]+2;
970 i_bigendian = EndianU16_NtoB(i);
971 [build appendData:[NSData dataWithBytes:&i_bigendian length:2]];
972 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
974 [build appendData:[NSData dataWithBytes:&dir length:1]];
975 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
977 [build appendData:[NSData dataWithBytes:&dir length:1]];
978 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
979 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
982 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
983 [self checkUSB:myContext];
984 unsigned short int i = [newName length]+[currentPath length]+2;
985 const UInt64 i_bigendian = EndianU16_NtoB(i);
986 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
987 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
989 [build appendData:[NSData dataWithBytes:&dir length:1]];
990 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
992 [build appendData:[NSData dataWithBytes:&dir length:1]];
993 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
994 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
995 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
996 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
999 - (void) checkUSB:(USBDeviceContext*)device {
1000 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
1001 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
1002 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
1004 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
1005 while ((data == nil || [data length] < 8) && i < 8) {
1006 NSLog (@"incorrect response");
1008 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
1011 // tell someone that no longer connected here??
1014 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
1015 if (![responseWanted isEqualToData:responseToCheck]) {
1016 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
1020 - (void) transfer:(id)sender {
1021 while ([[[self uiElements] isConnected] intValue]) {
1022 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
1023 if ([priorityTransferQueue count] != 0 ) {
1024 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
1025 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
1026 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
1027 [priorityTransferQueue removeObjectAtIndex:0];
1028 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1030 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
1031 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
1032 [priorityTransferQueue removeObjectAtIndex:0];
1034 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
1035 [self renameFile:[currentTransfer objectForKey:@"oldName"]
1036 withName:[currentTransfer objectForKey:@"newName"]
1037 atPath:[currentTransfer objectForKey:@"path"]];
1038 [priorityTransferQueue removeObjectAtIndex:0];
1040 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
1041 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
1042 [priorityTransferQueue removeObjectAtIndex:0];
1044 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
1045 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
1046 [priorityTransferQueue removeObjectAtIndex:0];
1048 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
1049 if ([transferQueue count] != 0) {
1050 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
1051 [NSPredicate predicateWithFormat:@"(filename = %@)",
1052 [currentTransfer objectForKey:@"filename"]]];
1053 if ([toPause count] > 1) NSLog(@"multiple pauses?");
1055 [pausedQueue addObjectsFromArray:toPause];
1056 [transferQueue removeObjectsInArray:toPause];
1059 [priorityTransferQueue removeObjectAtIndex:0];
1061 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
1062 if ([pausedQueue count] != 0) {
1063 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1064 [NSPredicate predicateWithFormat:@"(filename = %@)",
1065 [currentTransfer objectForKey:@"filename"]]];
1066 if ([toResume count] > 1) NSLog(@"multiple resumes?");
1068 [transferQueue addObjectsFromArray:toResume];
1069 [pausedQueue removeObjectsInArray:toResume];
1072 [priorityTransferQueue removeObjectAtIndex:0];
1075 else if ([transferQueue count] != 0) {
1076 id currentTransfer = [transferQueue objectAtIndex:0];
1077 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
1078 [self getFile:[currentTransfer objectForKey:@"filename"]
1079 forPath:[currentTransfer objectForKey:@"path"]
1080 toSaveTo:[currentTransfer objectForKey:@"savePath"]
1081 beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1082 withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1083 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1084 [transferQueue removeObjectAtIndex:0];
1085 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1087 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
1088 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1089 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1090 ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1091 fromPath:[currentTransfer objectForKey:@"path"]
1092 withAttributes:[currentTransfer objectForKey:@"attributes"]
1093 atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1094 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1095 [transferQueue removeObjectAtIndex:0];
1096 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1104 - (void) addPriorityTransfer:(id)newTransfer {
1105 [priorityTransferQueue addObject:newTransfer];
1106 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
1109 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
1110 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
1111 else [transferQueue addObject:newTransfer];
1112 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1115 - (void) setDH:(id)newDH tableView:(id)tv {
1120 - (void) clearQueues {
1121 [priorityTransferQueue removeAllObjects];
1122 [transferQueue removeAllObjects];
1123 [pausedQueue removeAllObjects];
1126 - (id) transferQueue {
1127 return transferQueue;
1130 - (id) pausedQueue {