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"
16 @implementation TFUSBController
20 * This source code is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Public License as published
22 * by the Free Software Foundation; either version 2 of the License,
23 * or (at your option) any later version.
25 * This source code is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
28 * Please refer to the GNU Public License for more details.
30 * You should have received a copy of the GNU Public License along with
31 * this source code; if not, write to:
32 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
37 /* Much of the code that initializes and closes the device is from Apple
38 * and is released under the license below.
42 * © Copyright 2001 Apple Computer, Inc. All rights reserved.
44 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in
45 * consideration of your agreement to the following terms, and your use, installation,
46 * modification or redistribution of this Apple software constitutes acceptance of these
47 * terms. If you do not agree with these terms, please do not use, install, modify or
48 * redistribute this Apple software.
50 * In consideration of your agreement to abide by the following terms, and subject to these
51 * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this
52 * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute
53 * the Apple Software, with or without modifications, in source and/or binary forms; provided
54 * that if you redistribute the Apple Software in its entirety and without modifications, you
55 * must retain this notice and the following text and disclaimers in all such redistributions
56 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple
57 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software
58 * without specific prior written permission from Apple. Except as expressly stated in this
59 * notice, no other rights or licenses, express or implied, are granted by Apple herein,
60 * including but not limited to any patent rights that may be infringed by your derivative
61 * works or by other works in which the Apple Software may be incorporated.
63 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
64 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
65 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
66 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
68 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
69 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
70 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
71 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
72 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
73 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
77 #define TopfieldVendorID 4571
81 #define TF5kProdID 4096
87 id ret = [super init];
90 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
91 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
92 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
96 void hexDump(UInt8 *buf, int len) {
97 int row, col, maxrows;
100 if (len % 16) maxrows++;
101 for (row=0; row< maxrows; row++) {
102 for (col=0; col<16; col++) {
103 if (!(col%2)) printf(" ");
104 printf("%02x", buf[row*16 + col] & 0xff);
107 for (col=0; col<16; col++) {
108 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
109 printf("%c", buf[row*16 + col]);
111 else { printf("."); }
119 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
120 char *outBuf, UInt32 len, int type)
126 printf(("sending:\n"));
127 hexDump(outBuf, len);
129 sendLen = ((len/kBlockSize))*kBlockSize;
130 if (len % kBlockSize)
131 sendLen += kBlockSize;
132 if ((sendLen % 0x200) == 0)
133 sendLen += kBlockSize;
135 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
137 printf("write err: %08x\n", err);
144 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
145 UInt8 *inBuf, UInt32 dataLen, int type) {
149 if (dataLen > kMaxXferSize) return 1;
151 len = (dataLen/kBlockSize) * kBlockSize;
152 if (dataLen % kBlockSize)
155 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
157 printf("read err 2: %08x\n", err);
158 printf("resetting\n");
159 err = (*intf)->ClearPipeStallBothEnds(intf, 2);
164 printf(("receiving: \n"));
174 int dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
177 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
178 IOUSBInterfaceInterface197 **intf;
179 IOUSBDeviceInterface197 **dev;
181 UInt8 numPipes, confNum, dSpeed;
184 err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
187 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
188 return kUproarDeviceErr;
190 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
191 (*iodev)->Release(iodev); // done with this
194 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
195 return kUproarDeviceErr;
200 err = (*intf)->USBInterfaceOpen(intf);
203 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
204 return kUproarDeviceErr;
206 err = (*intf)->GetNumEndpoints(intf, &numPipes);
209 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
210 return kUproarDeviceErr;
213 printf("dealWithInterface: found %d pipes\n", numPipes);
215 err = (*intf)->GetConfigurationValue(intf, &confNum);
216 err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
222 printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
223 connectedSpeed = dSpeed;
224 return kUproarSuccess;
228 int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
231 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
232 IOUSBDeviceInterface197 **dev;
235 IOUSBConfigurationDescriptorPtr confDesc;
236 IOUSBFindInterfaceRequest interfaceRequest;
237 io_iterator_t iterator;
238 io_service_t usbInterfaceRef;
242 err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
245 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
246 return kUproarDeviceErr;
248 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
249 (*iodev)->Release(iodev);
254 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
255 return kUproarDeviceErr;
257 err = (*dev)->USBDeviceOpen(dev);
260 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
261 return kUproarDeviceErr;
263 err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
266 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
267 return kUproarDeviceErr;
269 printf("dealWithDevice: found %d configurations\n", numConf);
270 err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
273 printf("dealWithDevice:unable to get config descriptor for index 0\n");
274 return kUproarDeviceErr;
276 err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
279 printf("dealWithDevice: unable to set the configuration\n");
280 return kUproarDeviceErr;
283 interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare; // requested class
284 interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
285 interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
286 interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting
288 err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
291 printf("dealWithDevice: unable to create interface iterator\n");
292 return kUproarDeviceErr;
295 while (usbInterfaceRef = IOIteratorNext(iterator))
297 printf("found interface: %p\n", (void*)usbInterfaceRef);
298 err = dealWithInterface(usbInterfaceRef, device);
299 IOObjectRelease(usbInterfaceRef); // no longer need this reference
303 IOObjectRelease(iterator);
305 if ((!found) || (err))
306 return kUproarDeviceErr;
308 return kUproarSuccess;
312 int initDevice(USBDeviceContext *device){
313 mach_port_t masterPort = 0;
315 CFMutableDictionaryRef matchingDictionary = 0; // requires <IOKit/IOKitLib.h>
316 short idVendor = TopfieldVendorID;
317 short idProduct = TF5kProdID;
318 CFNumberRef numberRef;
319 io_iterator_t iterator = 0;
320 io_service_t usbDeviceRef;
323 err = IOMasterPort(bootstrap_port, &masterPort);
327 printf("Anchortest: could not create master port, err = %08x\n", err);
330 matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); // requires <IOKit/usb/IOUSBLib.h>
331 if (!matchingDictionary)
333 printf("Anchortest: could not create matching dictionary\n");
336 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
339 printf("Anchortest: could not create CFNumberRef for vendor\n");
342 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
343 CFRelease(numberRef);
345 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
348 printf("Anchortest: could not create CFNumberRef for product\n");
351 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
352 CFRelease(numberRef);
355 err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
356 matchingDictionary = 0; // this was consumed by the above call
358 while (usbDeviceRef = IOIteratorNext(iterator))
360 printf("Found device %p\n", (void*)usbDeviceRef);
361 err = dealWithDevice(usbDeviceRef, device);
362 IOObjectRelease(usbDeviceRef); // no longer need this reference
367 IOObjectRelease(iterator);
369 mach_port_deallocate(mach_task_self(), masterPort);
370 if ((!found) || (err))
371 return kUproarDeviceErr;
373 return kUproarSuccess;
377 - (void) closeDevice:(USBDeviceContext *) device
379 IOUSBInterfaceInterface197 **intf;
380 IOUSBDeviceInterface197 **dev;
386 err = (*intf)->USBInterfaceClose(intf);
389 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
391 err = (*intf)->Release(intf);
394 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
397 err = (*dev)->USBDeviceClose(dev);
400 printf("dealWithDevice: error closing device - %08x\n", err);
401 (*dev)->Release(dev);
403 err = (*dev)->Release(dev);
406 printf("dealWithDevice: error releasing device - %08x\n", err);
410 - (USBDeviceContext*) initializeUSB {
411 USBDeviceContext *device;
414 device = malloc(sizeof(USBDeviceContext));
415 err = initDevice(device);
417 printf("Could not connect to Topfield\n");
421 printf("Connected to Topfield\n\n");
426 - (id) getFileListForPath:(NSString*) path {
427 if (myContext == nil)
430 // turn path into data
431 NSMutableData *pathData = [NSMutableData dataWithData:[path dataUsingEncoding:NSISOLatin1StringEncoding]];
432 [pathData increaseLengthBy:1]; // not sure if need to pad here?
434 //prepackage any commands needed
435 NSData* hddListCmd = [self prepareCommand:USB_CmdHddDir withData:pathData];
436 [self checkUSB:myContext];
437 NSData* hddFileData = [self sendCommand:hddListCmd toDevice:myContext expectResponse:YES careForReturn:YES];
438 // deal with data recieved - in this case parse it and update display
439 if ([hddFileData length] < 8) return nil;
440 NSData* checkForError = [hddFileData subdataWithRange:(NSRange){4,4}];
441 const UInt32 check_bigendian = EndianU32_NtoB(USB_Fail);
442 const UInt32 check2_bigendian = EndianU32_NtoB(USB_DataHddDir);
443 if ([checkForError isEqualToData:[NSData dataWithBytes:&check_bigendian length:4]]) {
444 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
447 if (! [checkForError isEqualToData:[NSData dataWithBytes:&check2_bigendian length:4]]) {
448 [self getFileListForPath:path]; //try again - hmm since move this could be recursive!
451 hddFileData = [hddFileData subdataWithRange:(NSRange) {8, [hddFileData length]-8}]; // cut off header and cmd
453 [[dh fileList] removeAllObjects];
454 for (i=0; i*114 < [hddFileData length]-4; i++) { // 4 is there cause swapping sometimes adds a byte of padding
455 NSData* temp = [hddFileData subdataWithRange:(NSRange) {i*114,114}];
456 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:temp];
457 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
458 [dh convertRawDataToUseful:tfFile];
459 [[dh fileList] addObject:tfFile];
462 [tableView reloadData];
463 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]];
464 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]]; //twice so get the same sort as before
468 -(word) findCRC:(byte*)p length:(dword) n
470 word m_crc16 = 0x0000;
471 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, };
473 for(i = 0; i < n; i++)
474 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
478 - (NSData*) prepareCommand:(unsigned int)cmd withData:(NSData*) inData {
480 unsigned short int length = 0;
484 unsigned short int l = [inData length];
487 const UInt16 length_bigendian = EndianU16_NtoB(length);
488 NSMutableData* toSend = [NSMutableData dataWithBytes:&length_bigendian length:2];
489 const UInt32 cmd_bigendian = EndianU32_NtoB(cmd);
490 NSMutableData* build = [NSMutableData dataWithBytes:&cmd_bigendian length:4];
492 [build appendData:inData];
495 memset(test, 0, length);
496 [build getBytes:test length:[build length]];
497 unsigned short int crc = [self findCRC:test length:[build length]];
498 const UInt16 crc_bigendian = EndianU16_NtoB(crc);
499 [toSend appendData:[NSMutableData dataWithBytes:&crc_bigendian length:2]];
500 [toSend appendData:build];
502 toSend = [self swap:toSend];
506 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
507 // [progressBar setDoubleValue:0];
508 // [progressTime setDoubleValue:0];
509 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
510 [[[NSApp delegate] currentlyField] setStringValue:[NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
511 [[[NSApp delegate] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
512 [[[NSApp delegate] currentlyField] displayIfNeeded];
513 //construct file send request
514 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
515 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
516 char dir = USB_FileToHost;
517 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
518 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
519 const SInt16 nsize_bigendian = EndianS16_NtoB(nsize);
520 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
521 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
522 [build appendData:d];
523 dir = 0x5c; // 0x5c = "/"
524 [build appendData:[NSData dataWithBytes:&dir length:1]];
525 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
527 [build appendData:[NSData dataWithBytes:&dir length:1]];
528 //add 8 byte offset here
529 const UInt64 offset_bigendian = EndianU64_NtoB(offset);
530 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
532 //prepackage commands to send
533 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
534 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
535 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
540 [self turnTurboOn:YES];
542 [self checkUSB:myContext]; //turbo has a check itself
543 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
545 NSDate* startTime = [NSDate date];
546 startTime = [startTime addTimeInterval:(0-existingTime)];
547 // send start request and get response
548 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
549 const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
550 NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
551 if ([data length] < 16) {
552 NSLog(@"Incorrect Data length");
555 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
556 if (![responseWanted isEqualToData:responseToCheck]) {
557 NSLog(@"Unexpected response from Toppy during download");
561 // clean up data and prepare path to save it
562 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
563 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
565 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
567 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
568 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
569 [outFile seekToFileOffset:offset];
570 [outFile writeData:finalData];
571 if (looping) { // loop for multiple data sends (ie files > 64k)
573 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
575 NSLog(@"large file detected - low GUI update rate");
578 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
579 double amountReceived = 0xfe00 + offset;
580 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
581 while ([header isEqualToData:testData] && [[[NSApp delegate] isConnected] intValue]) {
582 if ([priorityTransferQueue count] != 0) {
583 [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
584 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
586 //now add the right modification date to the file
587 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
588 [self turnTurboOn:NO];
592 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
593 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
594 amountReceived += 0xfe00;
595 if (timesAround < 8 || timesAround % updateRate == 0) {
596 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)amountReceived], @"offset", fileSize, @"size", startTime, @"startTime", nil]];
598 if ([data length] > 16){ //there is something to read
599 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
600 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
601 [outFile writeData:finalData];
602 [outFile synchronizeFile];
608 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
610 //now add the right modification date to the file
611 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
612 [self turnTurboOn:NO];
613 if (looping) [[NSApp delegate] finishTransfer];
617 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse careForReturn:(BOOL) careFactor{
619 int cmdLength = [fullyPackagedCommand length];
620 unsigned char outBuffer[cmdLength];
621 memset(outBuffer, 0, cmdLength);
622 // NSLog(@"send: %@", [fullyPackagedCommand description]);
623 [fullyPackagedCommand getBytes:outBuffer];
625 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
627 NSLog(@"sendError: %08x\n");
629 if (! getResponse) return nil;
631 int inLen = 0xFFFF; // i think this is biggest needed?
632 unsigned char inBuf[inLen];
633 memset(inBuf, 0, inLen);
634 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
636 NSLog(@"inError: %08x\n", err);
638 if (! careFactor) return nil;
639 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
640 data = [self swap:data];
641 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
642 return [data subdataWithRange:(NSRange) {0,inLen}];
645 -(NSMutableData*) swap: (NSData*) inData {
646 int len = [inData length];
649 uneven = 1; // prepare space for padding
653 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
654 NSLog(@"ERROR: Malloc failed");
655 return [NSMutableData dataWithData:inData];
657 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
658 NSLog(@"ERROR: Malloc failed");
659 return [NSMutableData dataWithData:inData];
661 [inData getBytes:array length:len];
662 swab(array, outarray, len+uneven);
663 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
664 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
670 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
671 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
672 USBDeviceContext* dev = myContext;
673 // prepare Send command
674 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
675 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
676 char dir = USB_FileToDevice;
677 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
678 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
679 const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
680 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
681 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
682 [build appendData:d];
683 dir = 0x5c; // 0x5c = "/"
684 [build appendData:[NSData dataWithBytes:&dir length:1]];
685 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
686 if ([fname length] < 95)
687 [build increaseLengthBy:95-[fname length]];
689 [build appendData:[NSData dataWithBytes:&dir length:1]];
690 UInt64 offset_bigendian = EndianU64_NtoB(offset);
691 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
693 // prepackage commands to send
694 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
695 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
696 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
699 NSDate* startTime = [NSDate date];
700 startTime = [startTime addTimeInterval:(0-existingTime)];
703 [self turnTurboOn:YES];
706 // now the proper commands
707 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
708 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
709 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
710 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
711 NSData* responseToCheck = nil;
713 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
714 [fileHandle seekToFileOffset:offset];
716 if ([priorityTransferQueue count] != 0) {
717 //break out and create a new transfer to continue it
718 NSLog(@"pausing upload");
719 [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
720 [self turnTurboOn:NO];
723 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
724 offset_bigendian = EndianU64_NtoB(offset);
725 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
726 [fileData appendData:[fileHandle readDataOfLength:rate]];
728 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
729 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
730 if ([data length] >= 8)
731 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
732 else responseToCheck = nil;
733 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
735 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)offset], @"offset", [NSNumber numberWithDouble:(double)size], @"size", startTime, @"startTime", nil]];
738 } while (offset < size && [[[NSApp delegate] isConnected] intValue]);
739 if ([[[NSApp delegate] isConnected] intValue])
740 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
741 [fileHandle closeFile];
742 [[NSApp delegate] goToPath:[[NSApp delegate]currentPath]];
743 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]];
744 [[NSApp delegate] tableView:tableView didClickTableColumn:[[NSApp delegate]selectedColumn]]; //twice so get the same sort as before
745 [self turnTurboOn:NO];
746 [[NSApp delegate] finishTransfer];
750 - (void) turnTurboOn:(BOOL) turnOn {
751 if (![[[NSApp delegate] isConnected] intValue]) return;
752 [self checkUSB:myContext];
756 const SInt32 mode_bigendian = EndianS32_NtoB(mode);
757 NSData* modeData = [NSData dataWithBytes:&mode_bigendian length:4];
758 NSLog([modeData description]); //debugging
759 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
760 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
764 return connectedSpeed;
767 -(void) setDebug:(int)mode {
769 NSLog(@"Debug level: %i", debug);
772 -(void) setRate:(int) newRate {
774 NSLog(@"New rate set: %i", rate);
777 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
779 progressTime = timeField;
783 -(void) updateProgress:(NSDictionary*) inDict {
784 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
785 double offset = [[inDict objectForKey:@"offset"] doubleValue];
786 double size = [[inDict objectForKey:@"size"] doubleValue];
787 NSDate* startTime = [inDict objectForKey:@"startTime"];
788 [progressBar setDoubleValue:((double)offset/size*100)];
789 [progressBar displayIfNeeded];
790 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
791 [progressTime displayIfNeeded];
796 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
799 char sp = 20; //padding
800 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
803 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
806 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
809 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
812 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
813 [self checkUSB:myContext];
814 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
815 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
816 char dir = 0x5c; // 0x5c = "\"
817 [build appendData:[NSData dataWithBytes:&dir length:1]];
818 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
820 [build appendData:[NSData dataWithBytes:&dir length:1]];
821 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
822 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
825 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
826 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
827 [self checkUSB:myContext];
828 unsigned short int i = [oldName length]+[currentPath length]+2;
829 UInt16 i_bigendian = EndianU16_NtoB(i);
830 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
831 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
833 [build appendData:[NSData dataWithBytes:&dir length:1]];
834 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
836 [build appendData:[NSData dataWithBytes:&dir length:1]];
837 i = [newName length]+[currentPath length]+2;
838 i_bigendian = EndianU16_NtoB(i);
839 [build appendData:[NSData dataWithBytes:&i_bigendian length:2]];
840 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
842 [build appendData:[NSData dataWithBytes:&dir length:1]];
843 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
845 [build appendData:[NSData dataWithBytes:&dir length:1]];
846 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
847 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
850 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
851 [self checkUSB:myContext];
852 unsigned short int i = [newName length]+[currentPath length]+2;
853 const UInt64 i_bigendian = EndianU16_NtoB(i);
854 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
855 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
857 [build appendData:[NSData dataWithBytes:&dir length:1]];
858 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
860 [build appendData:[NSData dataWithBytes:&dir length:1]];
861 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
862 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
863 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
864 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
867 - (void) checkUSB:(USBDeviceContext*)device {
868 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
869 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
870 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
872 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
873 while ((data == nil || [data length] < 8) && i < 8) {
874 NSLog (@"incorrect response");
876 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
879 // tell someone that no longer connected here??
882 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
883 if (![responseWanted isEqualToData:responseToCheck]) {
884 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
888 - (void) transfer:(id)sender {
889 while ([[[NSApp delegate] isConnected] intValue]) {
890 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
891 if ([priorityTransferQueue count] != 0 ) {
892 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
893 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
894 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
895 [priorityTransferQueue removeObjectAtIndex:0];
896 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
898 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
899 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
900 [priorityTransferQueue removeObjectAtIndex:0];
902 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
903 [self renameFile:[currentTransfer objectForKey:@"oldName"] withName:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
904 [priorityTransferQueue removeObjectAtIndex:0];
906 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
907 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
908 [priorityTransferQueue removeObjectAtIndex:0];
910 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
911 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
912 [priorityTransferQueue removeObjectAtIndex:0];
914 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
915 if ([transferQueue count] != 0) {
916 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(filename = %@)", [currentTransfer objectForKey:@"filename"]]];
917 if ([toPause count] > 1) NSLog(@"multiple pauses?");
919 [pausedQueue addObjectsFromArray:toPause];
920 [transferQueue removeObjectsInArray:toPause];
923 [priorityTransferQueue removeObjectAtIndex:0];
925 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
926 if ([pausedQueue count] != 0) {
927 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(filename = %@)", [currentTransfer objectForKey:@"filename"]]];
928 if ([toResume count] > 1) NSLog(@"multiple resumes?");
930 [transferQueue addObjectsFromArray:toResume];
931 [pausedQueue removeObjectsInArray:toResume];
934 [priorityTransferQueue removeObjectAtIndex:0];
937 else if ([transferQueue count] != 0) {
938 id currentTransfer = [transferQueue objectAtIndex:0];
939 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
940 [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]];
941 [transferQueue removeObjectAtIndex:0];
942 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
944 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
945 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
946 [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]];
947 [transferQueue removeObjectAtIndex:0];
948 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
956 - (void) addPriorityTransfer:(id)newTransfer {
957 [priorityTransferQueue addObject:newTransfer];
958 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
961 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
962 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
963 else [transferQueue addObject:newTransfer];
964 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
967 - (void) setDH:(id)newDH tableView:(id)tv {
972 - (void) clearQueues {
973 [priorityTransferQueue removeAllObjects];
974 [transferQueue removeAllObjects];
975 [pausedQueue removeAllObjects];
978 - (id) transferQueue {
979 return transferQueue;