5 // Created by Nathan Oates on Sun Aug 01 2004.
6 // Copyright (c) 2004-7 Nathan Oates nathan@noates.com All rights reserved.
9 // includes code based on uproar, license noted below:
11 // Copyright (c) 2001 Kasima Tharnpipitchai <me@kasima.org>
12 #import "TFUSBController.h"
14 @implementation TFUSBController
18 * This source code is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU Public License as published
20 * by the Free Software Foundation; either version 2 of the License,
21 * or (at your option) any later version.
23 * This source code is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26 * Please refer to the GNU Public License for more details.
28 * You should have received a copy of the GNU Public License along with
29 * this source code; if not, write to:
30 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 /* Much of the code that initializes and closes the device is from Apple
36 * and is released under the license below.
40 * © Copyright 2001 Apple Computer, Inc. All rights reserved.
42 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in
43 * consideration of your agreement to the following terms, and your use, installation,
44 * modification or redistribution of this Apple software constitutes acceptance of these
45 * terms. If you do not agree with these terms, please do not use, install, modify or
46 * redistribute this Apple software.
48 * In consideration of your agreement to abide by the following terms, and subject to these
49 * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this
50 * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute
51 * the Apple Software, with or without modifications, in source and/or binary forms; provided
52 * that if you redistribute the Apple Software in its entirety and without modifications, you
53 * must retain this notice and the following text and disclaimers in all such redistributions
54 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple
55 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software
56 * without specific prior written permission from Apple. Except as expressly stated in this
57 * notice, no other rights or licenses, express or implied, are granted by Apple herein,
58 * including but not limited to any patent rights that may be infringed by your derivative
59 * works or by other works in which the Apple Software may be incorporated.
61 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
62 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
63 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
64 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
66 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
68 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
69 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
70 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
71 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 #define TopfieldVendorID 4571
79 #define TF5kProdID 4096
85 id ret = [super init];
88 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
89 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
90 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
94 void hexDump(UInt8 *buf, int len) {
95 int row, col, maxrows;
98 if (len % 16) maxrows++;
99 for (row=0; row< maxrows; row++) {
100 for (col=0; col<16; col++) {
101 if (!(col%2)) printf(" ");
102 printf("%02x", buf[row*16 + col] & 0xff);
105 for (col=0; col<16; col++) {
106 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
107 printf("%c", buf[row*16 + col]);
109 else { printf("."); }
117 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
118 char *outBuf, UInt32 len, int type)
124 printf(("sending:\n"));
125 hexDump(outBuf, len);
127 sendLen = ((len/kBlockSize))*kBlockSize;
128 if (len % kBlockSize)
129 sendLen += kBlockSize;
130 if ((sendLen % 0x200) == 0)
131 sendLen += kBlockSize;
133 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
135 printf("write err: %08x\n", err);
142 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
143 UInt8 *inBuf, UInt32 dataLen, int type) {
147 if (dataLen > kMaxXferSize) return 1;
149 len = (dataLen/kBlockSize) * kBlockSize;
150 if (dataLen % kBlockSize)
153 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
155 printf("read err 2: %08x\n", err);
156 printf("resetting\n");
157 err = (*intf)->ClearPipeStallBothEnds(intf, 2);
162 printf(("receiving: \n"));
172 int dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
175 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
176 IOUSBInterfaceInterface197 **intf;
177 IOUSBDeviceInterface197 **dev;
179 UInt8 numPipes, confNum, dSpeed;
182 err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
185 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
186 return kUproarDeviceErr;
188 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
189 (*iodev)->Release(iodev); // done with this
192 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
193 return kUproarDeviceErr;
198 err = (*intf)->USBInterfaceOpen(intf);
201 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
202 return kUproarDeviceErr;
204 err = (*intf)->GetNumEndpoints(intf, &numPipes);
207 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
208 return kUproarDeviceErr;
211 printf("dealWithInterface: found %d pipes\n", numPipes);
213 err = (*intf)->GetConfigurationValue(intf, &confNum);
214 err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
220 printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
221 connectedSpeed = dSpeed;
222 return kUproarSuccess;
226 int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
229 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
230 IOUSBDeviceInterface197 **dev;
233 IOUSBConfigurationDescriptorPtr confDesc;
234 IOUSBFindInterfaceRequest interfaceRequest;
235 io_iterator_t iterator;
236 io_service_t usbInterfaceRef;
240 err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
243 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
244 return kUproarDeviceErr;
246 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
247 (*iodev)->Release(iodev);
252 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
253 return kUproarDeviceErr;
255 err = (*dev)->USBDeviceOpen(dev);
258 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
259 return kUproarDeviceErr;
261 err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
264 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
265 return kUproarDeviceErr;
267 printf("dealWithDevice: found %d configurations\n", numConf);
268 err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
271 printf("dealWithDevice:unable to get config descriptor for index 0\n");
272 return kUproarDeviceErr;
274 err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
277 printf("dealWithDevice: unable to set the configuration\n");
278 return kUproarDeviceErr;
281 interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare; // requested class
282 interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
283 interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
284 interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting
286 err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
289 printf("dealWithDevice: unable to create interface iterator\n");
290 return kUproarDeviceErr;
293 while (usbInterfaceRef = IOIteratorNext(iterator))
295 printf("found interface: %p\n", (void*)usbInterfaceRef);
296 err = dealWithInterface(usbInterfaceRef, device);
297 IOObjectRelease(usbInterfaceRef); // no longer need this reference
301 IOObjectRelease(iterator);
303 if ((!found) || (err))
304 return kUproarDeviceErr;
306 return kUproarSuccess;
310 int initDevice(USBDeviceContext *device){
311 mach_port_t masterPort = 0;
313 CFMutableDictionaryRef matchingDictionary = 0; // requires <IOKit/IOKitLib.h>
314 short idVendor = TopfieldVendorID;
315 short idProduct = TF5kProdID;
316 CFNumberRef numberRef;
317 io_iterator_t iterator = 0;
318 io_service_t usbDeviceRef;
321 err = IOMasterPort(bootstrap_port, &masterPort);
325 printf("Anchortest: could not create master port, err = %08x\n", err);
328 matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); // requires <IOKit/usb/IOUSBLib.h>
329 if (!matchingDictionary)
331 printf("Anchortest: could not create matching dictionary\n");
334 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
337 printf("Anchortest: could not create CFNumberRef for vendor\n");
340 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
341 CFRelease(numberRef);
343 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
346 printf("Anchortest: could not create CFNumberRef for product\n");
349 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
350 CFRelease(numberRef);
353 err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
354 matchingDictionary = 0; // this was consumed by the above call
356 while (usbDeviceRef = IOIteratorNext(iterator))
358 printf("Found device %p\n", (void*)usbDeviceRef);
359 err = dealWithDevice(usbDeviceRef, device);
360 IOObjectRelease(usbDeviceRef); // no longer need this reference
365 IOObjectRelease(iterator);
367 mach_port_deallocate(mach_task_self(), masterPort);
368 if ((!found) || (err))
369 return kUproarDeviceErr;
371 return kUproarSuccess;
375 - (void) closeDevice:(USBDeviceContext *) device
377 IOUSBInterfaceInterface197 **intf;
378 IOUSBDeviceInterface197 **dev;
384 err = (*intf)->USBInterfaceClose(intf);
387 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
389 err = (*intf)->Release(intf);
392 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
395 err = (*dev)->USBDeviceClose(dev);
398 printf("dealWithDevice: error closing device - %08x\n", err);
399 (*dev)->Release(dev);
401 err = (*dev)->Release(dev);
404 printf("dealWithDevice: error releasing device - %08x\n", err);
408 - (USBDeviceContext*) initializeUSB {
409 USBDeviceContext *device;
412 device = malloc(sizeof(USBDeviceContext));
413 err = initDevice(device);
415 printf("Could not connect to Topfield\n");
419 printf("Connected to Topfield\n\n");
424 - (id) getFileListForPath:(NSString*) path {
425 if (myContext == nil)
428 // turn path into data
429 NSMutableData *pathData = [NSMutableData dataWithData:[path dataUsingEncoding:NSISOLatin1StringEncoding]];
430 unsigned int padding = 0x00; // not sure if need to pad here?
431 [pathData appendData:[NSData dataWithBytes:&padding length:1]];
433 //prepackage any commands needed
434 NSData* hddListCmd = [self prepareCommand:USB_CmdHddDir withData:pathData];
435 [self checkUSB:myContext];
436 NSData* hddFileData = [self sendCommand:hddListCmd toDevice:myContext expectResponse:YES careForReturn:YES];
437 // deal with data recieved - in this case parse it and update display
438 if ([hddFileData length] < 8) return nil;
439 NSData* checkForError = [hddFileData subdataWithRange:(NSRange){4,4}];
440 int check = 0x00000001;
441 int check2 = 0x00001003;
442 if ([checkForError isEqualToData:[NSData dataWithBytes:&check length:4]]) {
443 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
446 if (! [checkForError isEqualToData:[NSData dataWithBytes:&check2 length:4]]) {
447 [self getFileListForPath:path]; //try again - hmm since move this could be recursive!
450 hddFileData = [hddFileData subdataWithRange:(NSRange) {8, [hddFileData length]-8}]; // cut off header and cmd
452 [[dh fileList] removeAllObjects];
453 for (i=0; i*114 < [hddFileData length]-4; i++) { // 4 is there cause swapping sometimes adds a byte of padding
454 NSData* temp = [hddFileData subdataWithRange:(NSRange) {i*114,114}];
455 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:temp];
456 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
457 [dh convertRawDataToUseful:tfFile];
458 [[dh fileList] addObject:tfFile];
461 [tableView reloadData];
462 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]];
463 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]]; //twice so get the same sort as before
467 -(word) findCRC:(byte*)p length:(dword) n
469 word m_crc16 = 0x0000;
470 const word crc16Tbl[256] = { 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, };
472 for(i = 0; i < n; i++)
473 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
477 - (NSData*) prepareCommand:(unsigned int)cmd withData:(NSData*) inData {
479 unsigned short int length = 0;
483 unsigned short int l = [inData length];
486 NSMutableData* toSend = [NSMutableData dataWithBytes:&length length:2];
487 NSMutableData* build = [NSMutableData dataWithBytes:&cmd length:4];
489 [build appendData:inData];
492 memset(test, 0, length);
493 [build getBytes:test length:[build length]];
494 unsigned short int crc = [self findCRC:test length:[build length]];
495 [toSend appendData:[NSMutableData dataWithBytes:&crc length:2]];
496 [toSend appendData:build];
498 toSend = [self swap:toSend];
502 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
503 // [progressBar setDoubleValue:0];
504 // [progressTime setDoubleValue:0];
505 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
506 [[[NSApp delegate] currentlyField] setStringValue:[NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
507 [[[NSApp delegate] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
508 [[[NSApp delegate] currentlyField] displayIfNeeded];
509 //construct file send request
510 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
511 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
512 char dir = USB_FileToHost;
513 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
514 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
515 [build appendData:[NSData dataWithBytes:&nsize length:2]];
516 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
517 [build appendData:d];
518 dir = 0x5c; // 0x5c = "/"
519 [build appendData:[NSData dataWithBytes:&dir length:1]];
520 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
522 [build appendData:[NSData dataWithBytes:&dir length:1]];
523 //add 8 byte offset here
524 [build appendData:[NSData dataWithBytes:&offset length:8]];
526 //prepackage commands to send
527 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
528 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
529 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
534 [self turnTurboOn:YES];
536 [self checkUSB:myContext]; //turbo has a check itself
537 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
539 NSDate* startTime = [NSDate date];
540 startTime = [startTime addTimeInterval:(0-existingTime)];
541 // send start request and get response
542 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
543 unsigned int rW = 0x0000100a;
544 NSData *responseWanted = [NSData dataWithBytes:&rW length:4];
545 if ([data length] < 16) {
546 NSLog(@"Incorrect Data length");
549 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
550 if (![responseWanted isEqualToData:responseToCheck]) {
551 NSLog(@"Unexpected response from Toppy during download");
555 // clean up data and prepare path to save it
556 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
557 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
559 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
561 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
562 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
563 [outFile seekToFileOffset:offset];
564 [outFile writeData:finalData];
565 if (looping) { // loop for multiple data sends (ie files > 64k)
567 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
569 NSLog(@"large file detected - low GUI update rate");
572 int test = USB_DataHddFileData;
573 double amountReceived = 0xfe00 + offset;
574 NSData* testData = [NSData dataWithBytes:&test length:4];
575 while ([header isEqualToData:testData] && [[[NSApp delegate] isConnected] intValue]) {
576 if ([priorityTransferQueue count] != 0) {
577 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:fileInfo,@"filename",currentPath,@"path",savePath,@"savePath",[NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",@"download",@"transferType",[NSNumber numberWithBool:YES],@"looping",[NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",nil] atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
578 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
580 //now add the right modification date to the file
581 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
582 [self turnTurboOn:NO];
586 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
587 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
588 amountReceived += 0xfe00;
589 if (timesAround < 8 || timesAround % updateRate == 0) {
590 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)amountReceived], @"offset", fileSize, @"size", startTime, @"startTime", nil]];
592 if ([data length] > 16){ //there is something to read
593 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
594 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
595 [outFile writeData:finalData];
596 [outFile synchronizeFile];
602 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
604 //now add the right modification date to the file
605 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
606 [self turnTurboOn:NO];
607 if (looping) [[NSApp delegate] finishTransfer];
611 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse careForReturn:(BOOL) careFactor{
613 int cmdLength = [fullyPackagedCommand length];
614 unsigned char outBuffer[cmdLength];
615 memset(outBuffer, 0, cmdLength);
616 // NSLog(@"send: %@", [fullyPackagedCommand description]);
617 [fullyPackagedCommand getBytes:outBuffer];
619 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
621 NSLog(@"sendError: %08x\n");
623 if (! getResponse) return nil;
625 int inLen = 0xFFFF; // i think this is biggest needed?
626 unsigned char inBuf[inLen];
627 memset(inBuf, 0, inLen);
628 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
630 NSLog(@"inError: %08x\n", err);
632 if (! careFactor) return nil;
633 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
634 data = [self swap:data];
635 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
636 return [data subdataWithRange:(NSRange) {0,inLen}];
639 -(NSMutableData*) swap: (NSData*) inData {
640 int len = [inData length];
643 uneven = 1; // prepare space for padding
647 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
648 NSLog(@"ERROR: Malloc failed");
649 return [NSMutableData dataWithData:inData];
651 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
652 NSLog(@"ERROR: Malloc failed");
653 return [NSMutableData dataWithData:inData];
655 [inData getBytes:array length:len];
656 swab(array, outarray, len+uneven);
657 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
658 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
664 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
665 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
666 USBDeviceContext* dev = myContext;
667 // prepare Send command
668 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
669 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
670 char dir = USB_FileToDevice;
671 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
672 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
673 [build appendData:[NSData dataWithBytes:&nsize length:2]];
674 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
675 [build appendData:d];
676 dir = 0x5c; // 0x5c = "/"
677 [build appendData:[NSData dataWithBytes:&dir length:1]];
678 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
679 int f = 0x0000000000;
681 while (i < 95 -[fname length]){
682 [build appendData:[NSData dataWithBytes:&f length:1]];
686 [build appendData:[NSData dataWithBytes:&dir length:1]];
687 [build appendData:[NSData dataWithBytes:&offset length:8]];
689 // prepackage commands to send
690 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
691 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
692 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
695 NSDate* startTime = [NSDate date];
696 startTime = [startTime addTimeInterval:(0-existingTime)];
699 [self turnTurboOn:YES];
702 // now the proper commands
703 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
704 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
705 unsigned int rW = 0x00000002;
706 NSData* responseWanted = [NSData dataWithBytes:&rW length:4];
707 NSData* responseToCheck = nil;
709 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
710 [fileHandle seekToFileOffset:offset];
712 if ([priorityTransferQueue count] != 0) {
713 //break out and create a new transfer to continue it
714 NSLog(@"pausing upload");
715 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:fileToUpload,@"filename",[NSNumber numberWithUnsignedLongLong:size],@"fileSize",curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",[NSNumber numberWithUnsignedLongLong:offset],@"offset",[NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",nil] atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
716 [self turnTurboOn:NO];
719 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
720 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset length:8];
721 [fileData appendData:[fileHandle readDataOfLength:rate]];
723 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
724 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
725 if ([data length] >= 8)
726 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
727 else responseToCheck = nil;
728 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
730 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)offset], @"offset", [NSNumber numberWithDouble:(double)size], @"size", startTime, @"startTime", nil]];
733 } while (offset < size && [[[NSApp delegate] isConnected] intValue]);
734 if ([[[NSApp delegate] isConnected] intValue])
735 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
736 [fileHandle closeFile];
737 [[NSApp delegate] goToPath:[[NSApp delegate]currentPath]];
738 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]];
739 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]]; //twice so get the same sort as before
740 [self turnTurboOn:NO];
741 [[NSApp delegate] finishTransfer];
745 - (void) turnTurboOn:(BOOL) turnOn {
746 if (![[[NSApp delegate] isConnected] intValue]) return;
747 [self checkUSB:myContext];
751 NSData* modeData = [NSData dataWithBytes:&mode length:4];
752 NSLog([modeData description]); //debugging
753 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
754 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
758 return connectedSpeed;
761 -(void) setDebug:(int)mode {
763 NSLog(@"Debug level: %i", debug);
766 -(void) setRate:(int) newRate {
768 NSLog(@"New rate set: %i", rate);
771 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
773 progressTime = timeField;
777 -(void) updateProgress:(NSDictionary*) inDict {
778 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
779 double offset = [[inDict objectForKey:@"offset"] doubleValue];
780 double size = [[inDict objectForKey:@"size"] doubleValue];
781 NSDate* startTime = [inDict objectForKey:@"startTime"];
782 [progressBar setDoubleValue:((double)offset/size*100)];
783 [progressBar displayIfNeeded];
784 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
785 [progressTime displayIfNeeded];
790 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
793 char sp = 20; //padding
794 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
797 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
800 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
803 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
806 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
807 [self checkUSB:myContext];
808 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
809 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
810 char dir = 0x5c; // 0x5c = "\"
811 [build appendData:[NSData dataWithBytes:&dir length:1]];
812 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
814 [build appendData:[NSData dataWithBytes:&dir length:1]];
815 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
816 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
819 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
820 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
821 [self checkUSB:myContext];
822 unsigned short int i = [oldName length]+[currentPath length]+2;
823 NSMutableData* build = [NSMutableData dataWithBytes:&i length:2];
824 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
826 [build appendData:[NSData dataWithBytes:&dir length:1]];
827 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
829 [build appendData:[NSData dataWithBytes:&dir length:1]];
830 i = [newName length]+[currentPath length]+2;
831 [build appendData:[NSData dataWithBytes:&i length:2]];
832 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
834 [build appendData:[NSData dataWithBytes:&dir length:1]];
835 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
837 [build appendData:[NSData dataWithBytes:&dir length:1]];
838 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
839 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
842 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
843 [self checkUSB:myContext];
844 unsigned short int i = [newName length]+[currentPath length]+2;
845 NSMutableData* build = [NSMutableData dataWithBytes:&i length:2];
846 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
848 [build appendData:[NSData dataWithBytes:&dir length:1]];
849 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
851 [build appendData:[NSData dataWithBytes:&dir length:1]];
852 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
853 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
854 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
855 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
858 - (void) checkUSB:(USBDeviceContext*)device {
859 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
860 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
861 unsigned int rW = 0x00000002;
863 NSData* responseWanted = [NSData dataWithBytes:&rW length:4];
864 while ((data == nil || [data length] < 8) && i < 8) {
865 NSLog (@"incorrect response");
867 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
870 // tell someone that no longer connected here??
873 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
874 if (![responseWanted isEqualToData:responseToCheck]) {
875 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
879 - (void) transfer:(id)sender {
880 while ([[[NSApp delegate] isConnected] intValue]) {
881 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
882 if ([priorityTransferQueue count] != 0 ) {
883 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
884 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
885 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
886 [priorityTransferQueue removeObjectAtIndex:0];
887 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
889 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
890 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
891 [priorityTransferQueue removeObjectAtIndex:0];
893 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
894 [self renameFile:[currentTransfer objectForKey:@"oldName"] withName:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
895 [priorityTransferQueue removeObjectAtIndex:0];
897 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
898 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
899 [priorityTransferQueue removeObjectAtIndex:0];
901 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
902 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
903 [priorityTransferQueue removeObjectAtIndex:0];
905 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
906 if ([transferQueue count] != 0) {
907 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(filename = %@)", [currentTransfer objectForKey:@"filename"]]];
908 if ([toPause count] > 1) NSLog(@"multiple pauses?");
910 [pausedQueue addObjectsFromArray:toPause];
911 [transferQueue removeObjectsInArray:toPause];
914 [priorityTransferQueue removeObjectAtIndex:0];
916 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
917 if ([pausedQueue count] != 0) {
918 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(filename = %@)", [currentTransfer objectForKey:@"filename"]]];
919 if ([toResume count] > 1) NSLog(@"multiple resumes?");
921 [transferQueue addObjectsFromArray:toResume];
922 [pausedQueue removeObjectsInArray:toResume];
925 [priorityTransferQueue removeObjectAtIndex:0];
928 else if ([transferQueue count] != 0) {
929 id currentTransfer = [transferQueue objectAtIndex:0];
930 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
931 [self getFile:[currentTransfer objectForKey:@"filename"] forPath:[currentTransfer objectForKey:@"path"] toSaveTo:[currentTransfer objectForKey:@"savePath"] beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue] withLooping:[[currentTransfer objectForKey:@"looping"] boolValue] existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
932 [transferQueue removeObjectAtIndex:0];
933 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
935 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
936 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
937 [self uploadFile:[currentTransfer objectForKey:@"filename"] ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue] fromPath:[currentTransfer objectForKey:@"path"] withAttributes:[currentTransfer objectForKey:@"attributes"] atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue] existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
938 [transferQueue removeObjectAtIndex:0];
939 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
947 - (void) addPriorityTransfer:(id)newTransfer {
948 [priorityTransferQueue addObject:newTransfer];
949 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
952 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
953 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
954 else [transferQueue addObject:newTransfer];
955 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
958 - (void) setDH:(id)newDH tableView:(id)tv {
963 - (void) clearQueues {
964 [priorityTransferQueue removeAllObjects];
965 [transferQueue removeAllObjects];
966 [pausedQueue removeAllObjects];
969 - (id) transferQueue {
970 return transferQueue;