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 - (id) getFileListForPath:(NSString*) path {
433 if (myContext == nil)
436 // turn path into data
437 NSMutableData *pathData = [NSMutableData dataWithData:[path dataUsingEncoding:NSISOLatin1StringEncoding]];
438 [pathData increaseLengthBy:1]; // not sure if need to pad here?
440 //prepackage any commands needed
441 NSData* hddListCmd = [self prepareCommand:USB_CmdHddDir withData:pathData];
442 [self checkUSB:myContext];
443 NSData* hddFileData = [self sendCommand:hddListCmd toDevice:myContext expectResponse:YES careForReturn:YES];
444 // deal with data recieved - in this case parse it and update display
445 if ([hddFileData length] < 8) return nil;
446 NSData* checkForError = [hddFileData subdataWithRange:(NSRange){4,4}];
447 const UInt32 check_bigendian = EndianU32_NtoB(USB_Fail);
448 const UInt32 check2_bigendian = EndianU32_NtoB(USB_DataHddDir);
449 if ([checkForError isEqualToData:[NSData dataWithBytes:&check_bigendian length:4]]) {
450 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
453 if (! [checkForError isEqualToData:[NSData dataWithBytes:&check2_bigendian length:4]]) {
454 [self getFileListForPath:path]; //try again - hmm since move this could be recursive!
457 hddFileData = [hddFileData subdataWithRange:(NSRange) {8, [hddFileData length]-8}]; // cut off header and cmd
459 [[dh fileList] removeAllObjects];
460 for (i=0; i*114 < [hddFileData length]-4; i++) { // 4 is there cause swapping sometimes adds a byte of padding
461 NSData* temp = [hddFileData subdataWithRange:(NSRange) {i*114,114}];
462 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:temp];
463 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
464 [dh convertRawDataToUseful:tfFile];
465 [[dh fileList] addObject:tfFile];
468 [tableView reloadData];
469 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
470 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
474 -(UInt16) findCRC:(const UInt8*)p length:(size_t) n
476 UInt16 m_crc16 = 0x0000;
477 static const UInt16 crc16Tbl[256] = {
478 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
479 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
480 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
481 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
482 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
483 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
484 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
485 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
486 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
487 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
488 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
489 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
490 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
491 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
492 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
493 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
494 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
495 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
496 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
497 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
498 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
499 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
500 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
501 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
502 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
503 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
504 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
505 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
506 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
507 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
508 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
509 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
512 for(i = 0; i < n; i++)
513 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
517 - (NSData*) prepareCommand:(unsigned int)cmd withData:(NSData*) inData {
519 unsigned short int length = 0;
523 unsigned short int l = [inData length];
526 const UInt16 length_bigendian = EndianU16_NtoB(length);
527 NSMutableData* toSend = [NSMutableData dataWithBytes:&length_bigendian length:2];
528 const UInt32 cmd_bigendian = EndianU32_NtoB(cmd);
529 NSMutableData* build = [NSMutableData dataWithBytes:&cmd_bigendian length:4];
531 [build appendData:inData];
534 memset(test, 0, length);
535 [build getBytes:test length:[build length]];
536 unsigned short int crc = [self findCRC:test length:[build length]];
537 const UInt16 crc_bigendian = EndianU16_NtoB(crc);
538 [toSend appendData:[NSMutableData dataWithBytes:&crc_bigendian length:2]];
539 [toSend appendData:build];
541 toSend = [self swap:toSend];
545 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
546 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
547 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
548 // [progressBar setDoubleValue:0];
549 // [progressTime setDoubleValue:0];
550 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
551 [[[self uiElements] currentlyField] setStringValue:
552 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
553 [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
554 [[[self uiElements] currentlyField] displayIfNeeded];
555 //construct file send request
556 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
557 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
558 char dir = USB_FileToHost;
559 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
560 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
561 const SInt16 nsize_bigendian = EndianS16_NtoB(nsize);
562 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
563 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
564 [build appendData:d];
565 dir = 0x5c; // 0x5c = "/"
566 [build appendData:[NSData dataWithBytes:&dir length:1]];
567 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
569 [build appendData:[NSData dataWithBytes:&dir length:1]];
570 //add 8 byte offset here
571 const UInt64 offset_bigendian = EndianU64_NtoB(offset);
572 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
574 //prepackage commands to send
575 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
576 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
577 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
582 [self turnTurboOn:YES];
584 [self checkUSB:myContext]; //turbo has a check itself
585 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
587 NSDate* startTime = [NSDate date];
588 startTime = [startTime addTimeInterval:(0-existingTime)];
589 // send start request and get response
590 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
591 const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
592 NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
593 if ([data length] < 16) {
594 NSLog(@"Incorrect Data length");
597 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
598 if (![responseWanted isEqualToData:responseToCheck]) {
599 NSLog(@"Unexpected response from Toppy during download");
603 // clean up data and prepare path to save it
604 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
605 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
607 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
609 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
610 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
611 [outFile seekToFileOffset:offset];
612 [outFile writeData:finalData];
613 if (looping) { // loop for multiple data sends (ie files > 64k)
615 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
617 NSLog(@"large file detected - low GUI update rate");
620 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
621 double amountReceived = 0xfe00 + offset;
622 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
623 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
624 if ([priorityTransferQueue count] != 0) {
625 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
626 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
627 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
628 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
629 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
631 atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
632 // send reset again just to make sure!
633 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
635 //now add the right modification date to the file
636 [[NSFileManager defaultManager]
637 changeFileAttributes:[NSDictionary
638 dictionaryWithObject:[fileInfo objectForKey:@"date"]
639 forKey:@"NSFileModificationDate"]
641 [self turnTurboOn:NO];
645 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
646 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
647 amountReceived += 0xfe00;
648 if (timesAround < 8 || timesAround % updateRate == 0) {
649 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
650 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
651 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
652 fileSize, @"size", startTime, @"startTime", nil]];
654 if ([data length] > 16){ //there is something to read
655 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
656 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
657 [outFile writeData:finalData];
658 [outFile synchronizeFile];
664 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
666 //now add the right modification date to the file
667 [[NSFileManager defaultManager]
668 changeFileAttributes:[NSDictionary
669 dictionaryWithObject:[fileInfo objectForKey:@"date"]
670 forKey:@"NSFileModificationDate"]
672 [self turnTurboOn:NO];
673 if (looping) [[self uiElements] finishTransfer];
677 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
678 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
679 careForReturn:(BOOL) careFactor{
681 int cmdLength = [fullyPackagedCommand length];
682 unsigned char outBuffer[cmdLength];
683 memset(outBuffer, 0, cmdLength);
684 // NSLog(@"send: %@", [fullyPackagedCommand description]);
685 [fullyPackagedCommand getBytes:outBuffer];
687 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
689 NSLog(@"sendError: %08x\n");
691 if (! getResponse) return nil;
693 int inLen = 0xFFFF; // i think this is biggest needed?
694 unsigned char inBuf[inLen];
695 memset(inBuf, 0, inLen);
696 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
698 NSLog(@"inError: %08x\n", err);
700 if (! careFactor) return nil;
701 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
702 data = [self swap:data];
703 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
704 return [data subdataWithRange:(NSRange) {0,inLen}];
707 -(NSMutableData*) swap: (NSData*) inData {
708 int len = [inData length];
711 uneven = 1; // prepare space for padding
715 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
716 NSLog(@"ERROR: Malloc failed");
717 return [NSMutableData dataWithData:inData];
719 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
720 NSLog(@"ERROR: Malloc failed");
721 return [NSMutableData dataWithData:inData];
723 [inData getBytes:array length:len];
724 swab(array, outarray, len+uneven);
725 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
726 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
732 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
733 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
734 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
735 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
736 USBDeviceContext* dev = myContext;
737 // prepare Send command
738 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
739 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
740 char dir = USB_FileToDevice;
741 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
742 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
743 const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
744 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
745 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
746 [build appendData:d];
747 dir = 0x5c; // 0x5c = "/"
748 [build appendData:[NSData dataWithBytes:&dir length:1]];
749 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
750 if ([fname length] < 95)
751 [build increaseLengthBy:95-[fname length]];
753 [build appendData:[NSData dataWithBytes:&dir length:1]];
754 UInt64 offset_bigendian = EndianU64_NtoB(offset);
755 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
757 // prepackage commands to send
758 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
759 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
760 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
763 NSDate* startTime = [NSDate date];
764 startTime = [startTime addTimeInterval:(0-existingTime)];
767 [self turnTurboOn:YES];
770 // now the proper commands
771 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
772 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
773 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
774 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
775 NSData* responseToCheck = nil;
777 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
778 [fileHandle seekToFileOffset:offset];
780 if ([priorityTransferQueue count] != 0) {
781 //break out and create a new transfer to continue it
782 NSLog(@"pausing upload");
783 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
784 fileToUpload,@"filename",
785 [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
786 curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
787 [NSNumber numberWithUnsignedLongLong:offset],@"offset",
788 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
790 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
791 [self turnTurboOn:NO];
794 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
795 offset_bigendian = EndianU64_NtoB(offset);
796 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
797 [fileData appendData:[fileHandle readDataOfLength:rate]];
799 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
800 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
801 if ([data length] >= 8)
802 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
803 else responseToCheck = nil;
804 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
806 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
807 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
808 [NSNumber numberWithDouble:(double)offset], @"offset",
809 [NSNumber numberWithDouble:(double)size], @"size",
810 startTime, @"startTime", nil]];
813 } while (offset < size && [[[self uiElements] isConnected] intValue]);
814 if ([[[self uiElements] isConnected] intValue])
815 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
816 [fileHandle closeFile];
817 [[self uiElements] goToPath:[[self uiElements]currentPath]];
818 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
819 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
820 [self turnTurboOn:NO];
821 [[self uiElements] finishTransfer];
825 - (void) turnTurboOn:(BOOL) turnOn {
826 if (![[[self uiElements] isConnected] intValue]) return;
827 [self checkUSB:myContext];
831 const SInt32 mode_bigendian = EndianS32_NtoB(mode);
832 NSData* modeData = [NSData dataWithBytes:&mode_bigendian length:4];
833 NSLog([modeData description]); //debugging
834 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
835 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
839 return connectedSpeed;
842 -(void) setDebug:(int)mode {
844 NSLog(@"Debug level: %i", debug);
847 -(void) setRate:(int) newRate {
849 NSLog(@"New rate set: %i", rate);
852 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
854 progressTime = timeField;
858 -(void) updateProgress:(NSDictionary*) inDict {
859 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
860 double offset = [[inDict objectForKey:@"offset"] doubleValue];
861 double size = [[inDict objectForKey:@"size"] doubleValue];
862 NSDate* startTime = [inDict objectForKey:@"startTime"];
863 [progressBar setDoubleValue:((double)offset/size*100)];
864 [progressBar displayIfNeeded];
865 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
866 [progressTime displayIfNeeded];
871 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
874 char sp = 20; //padding
875 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
878 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
881 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
884 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
887 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
888 [self checkUSB:myContext];
889 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
890 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
891 char dir = 0x5c; // 0x5c = "\"
892 [build appendData:[NSData dataWithBytes:&dir length:1]];
893 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
895 [build appendData:[NSData dataWithBytes:&dir length:1]];
896 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
897 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
900 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
901 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
902 [self checkUSB:myContext];
903 unsigned short int i = [oldName length]+[currentPath length]+2;
904 UInt16 i_bigendian = EndianU16_NtoB(i);
905 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
906 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
908 [build appendData:[NSData dataWithBytes:&dir length:1]];
909 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
911 [build appendData:[NSData dataWithBytes:&dir length:1]];
912 i = [newName length]+[currentPath length]+2;
913 i_bigendian = EndianU16_NtoB(i);
914 [build appendData:[NSData dataWithBytes:&i_bigendian length:2]];
915 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
917 [build appendData:[NSData dataWithBytes:&dir length:1]];
918 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
920 [build appendData:[NSData dataWithBytes:&dir length:1]];
921 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
922 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
925 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
926 [self checkUSB:myContext];
927 unsigned short int i = [newName length]+[currentPath length]+2;
928 const UInt64 i_bigendian = EndianU16_NtoB(i);
929 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
930 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
932 [build appendData:[NSData dataWithBytes:&dir length:1]];
933 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
935 [build appendData:[NSData dataWithBytes:&dir length:1]];
936 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
937 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
938 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
939 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
942 - (void) checkUSB:(USBDeviceContext*)device {
943 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
944 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
945 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
947 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
948 while ((data == nil || [data length] < 8) && i < 8) {
949 NSLog (@"incorrect response");
951 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
954 // tell someone that no longer connected here??
957 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
958 if (![responseWanted isEqualToData:responseToCheck]) {
959 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
963 - (void) transfer:(id)sender {
964 while ([[[self uiElements] isConnected] intValue]) {
965 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
966 if ([priorityTransferQueue count] != 0 ) {
967 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
968 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
969 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
970 [priorityTransferQueue removeObjectAtIndex:0];
971 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
973 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
974 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
975 [priorityTransferQueue removeObjectAtIndex:0];
977 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
978 [self renameFile:[currentTransfer objectForKey:@"oldName"]
979 withName:[currentTransfer objectForKey:@"newName"]
980 atPath:[currentTransfer objectForKey:@"path"]];
981 [priorityTransferQueue removeObjectAtIndex:0];
983 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
984 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
985 [priorityTransferQueue removeObjectAtIndex:0];
987 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
988 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
989 [priorityTransferQueue removeObjectAtIndex:0];
991 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
992 if ([transferQueue count] != 0) {
993 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
994 [NSPredicate predicateWithFormat:@"(filename = %@)",
995 [currentTransfer objectForKey:@"filename"]]];
996 if ([toPause count] > 1) NSLog(@"multiple pauses?");
998 [pausedQueue addObjectsFromArray:toPause];
999 [transferQueue removeObjectsInArray:toPause];
1002 [priorityTransferQueue removeObjectAtIndex:0];
1004 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
1005 if ([pausedQueue count] != 0) {
1006 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1007 [NSPredicate predicateWithFormat:@"(filename = %@)",
1008 [currentTransfer objectForKey:@"filename"]]];
1009 if ([toResume count] > 1) NSLog(@"multiple resumes?");
1011 [transferQueue addObjectsFromArray:toResume];
1012 [pausedQueue removeObjectsInArray:toResume];
1015 [priorityTransferQueue removeObjectAtIndex:0];
1018 else if ([transferQueue count] != 0) {
1019 id currentTransfer = [transferQueue objectAtIndex:0];
1020 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
1021 [self getFile:[currentTransfer objectForKey:@"filename"]
1022 forPath:[currentTransfer objectForKey:@"path"]
1023 toSaveTo:[currentTransfer objectForKey:@"savePath"]
1024 beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1025 withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1026 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1027 [transferQueue removeObjectAtIndex:0];
1028 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1030 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
1031 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1032 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1033 ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1034 fromPath:[currentTransfer objectForKey:@"path"]
1035 withAttributes:[currentTransfer objectForKey:@"attributes"]
1036 atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1037 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1038 [transferQueue removeObjectAtIndex:0];
1039 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1047 - (void) addPriorityTransfer:(id)newTransfer {
1048 [priorityTransferQueue addObject:newTransfer];
1049 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
1052 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
1053 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
1054 else [transferQueue addObject:newTransfer];
1055 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1058 - (void) setDH:(id)newDH tableView:(id)tv {
1063 - (void) clearQueues {
1064 [priorityTransferQueue removeAllObjects];
1065 [transferQueue removeAllObjects];
1066 [pausedQueue removeAllObjects];
1069 - (id) transferQueue {
1070 return transferQueue;
1073 - (id) pausedQueue {