5 // Created by Nathan Oates on Sun Aug 01 2004.
6 // Copyright (c) 2004 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 "USBFunctions.h"
14 @implementation USBFunctions
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.
79 #define kTopfieldVendorID 4571
83 #define kTF5kProdID 4096
88 id ret = [super init];
95 void hexDump(UInt8 *buf, int len) {
96 int row, col, maxrows;
99 if (len % 16) maxrows++;
100 for (row=0; row< maxrows; row++) {
101 for (col=0; col<16; col++) {
102 if (!(col%2)) printf(" ");
103 printf("%02x", buf[row*16 + col] & 0xff);
106 for (col=0; col<16; col++) {
107 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
108 printf("%c", buf[row*16 + col]);
110 else { printf("."); }
118 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
119 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;
134 // if (type == kFin) // err = beginInitiateRequest(dev); // else // err = beginCmdSendRequest(dev);
135 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
137 printf("write err: %08x\n", err);
140 // err = endRequest(dev);
145 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
146 UInt8 *inBuf, UInt32 dataLen, int type) {
150 if (dataLen > kMaxXferSize) return 1;
152 len = (dataLen/kBlockSize) * kBlockSize;
153 if (dataLen % kBlockSize)
156 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
158 printf("read err 2: %08x\n", err);
159 printf("resetting\n");
160 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
196 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
197 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 = kTopfieldVendorID;
319 short idProduct = kTF5kProdID;
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 - (NSData*) getHDDSize:(USBDeviceContext*)device {
430 UInt8 outBuf[8], inBuf[64];
432 [self checkUSB:device];
433 memset(outBuf, 0, 8);
434 memset(inBuf, 0, 64);
435 outBuf[0] = 0x08; //08000dc000000010 = request for HDD size info
442 err = doSend(device->dev, device->intf, outBuf, 8, kDat);
444 err = doRecv(device->dev, device->intf, inBuf, len, kDat);
446 NSData* data = [NSData dataWithBytes:inBuf length:16];
447 data = [self swap:data];
452 - (NSData*) getFileList:(USBDeviceContext*)device forPath:(NSString*) path {
456 // turn path into data
457 NSMutableData *pathData = [NSMutableData dataWithData:[path dataUsingEncoding:NSISOLatin1StringEncoding]];
458 unsigned int padding = 0x00; // not sure if need to pad here?
459 [pathData appendData:[NSData dataWithBytes:&padding length:1]];
461 //prepackage any commands needed
462 NSData* hddListCmd = [self prepareCommand:USB_CmdHddDir withData:pathData];
463 // NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
464 [self checkUSB:device];
466 //reset first of all to make sure all is fine
467 /* NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
468 unsigned int rW = 0x00000002;
469 NSData* responseWanted = [NSData dataWithBytes:&rW length:4];
470 while (data == nil || [data length] < 8) {
471 NSLog (@"incorrect response");
472 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
474 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
475 if (![responseWanted isEqualToData:responseToCheck]) {
476 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
479 NSData* data = [self sendCommand:hddListCmd toDevice:device expectResponse:YES careForReturn:YES];
480 // deal with data recieved - in this case pass it back up for parsing
484 -(word) findCRC:(byte*)p length:(dword) n
486 word m_crc16 = 0x0000;
487 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, };
489 for(i = 0; i < n; i++)
490 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
494 - (NSData*) prepareCommand:(unsigned int)cmd withData:(NSData*) inData {
496 unsigned short int length = 0;
500 unsigned short int l = [inData length];
503 NSMutableData* toSend = [NSMutableData dataWithBytes:&length length:2];
504 NSMutableData* build = [NSMutableData dataWithBytes:&cmd length:4];
506 [build appendData:inData];
509 memset(test, 0, length);
510 [build getBytes:test length:[build length]];
511 unsigned short int crc = [self findCRC:test length:[build length]];
512 [toSend appendData:[NSMutableData dataWithBytes:&crc length:2]];
513 [toSend appendData:build];
515 toSend = [self swap:toSend];
519 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*) currentPath toSaveTo:(NSString*) savePath withLoop:(BOOL)looping {
520 //construct file send request
521 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
522 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
523 char dir = USB_FileToHost;
524 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
525 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
526 [build appendData:[NSData dataWithBytes:&nsize length:2]];
527 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
528 [build appendData:d];
529 dir = 0x5c; // 0x5c = "/"
530 [build appendData:[NSData dataWithBytes:&dir length:1]];
531 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
533 [build appendData:[NSData dataWithBytes:&dir length:1]];
535 //prepackage commands to send
536 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
537 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
538 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
541 [self checkUSB:myContext];
542 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
544 NSDate* startTime = [NSDate date];
545 // send start request and get response
546 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
547 unsigned int rW = 0x0000100a;
548 NSData *responseWanted = [NSData dataWithBytes:&rW length:4];
549 if ([data length] < 16) {
550 NSLog(@"Incorrect Data length");
553 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
554 if (![responseWanted isEqualToData:responseToCheck]) {
555 NSLog(@"Unexpected response from Toppy during download");
559 // clean up data and prepare path to save it
560 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
561 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
563 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
564 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
565 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
566 [outFile writeData:finalData];
567 if (looping) { // loop for multiple data sends (ie files > 64k)
569 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
571 NSLog(@"large file detected - low GUI update rate");
574 int test = USB_DataHddFileData;
575 double amountReceived = 0xfe00;
576 NSData* testData = [NSData dataWithBytes:&test length:4];
577 while ([header isEqualToData:testData] && [[[NSApp delegate] isConnected] intValue]) {
579 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
580 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
581 amountReceived += 0xfe00;
582 if (timesAround < 8 || timesAround % updateRate == 0) {
583 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)amountReceived], @"offset", fileSize, @"size", startTime, @"startTime", nil]];
585 if ([data length] > 16){ //there is something to read
586 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
587 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
588 [outFile writeData:finalData];
589 [outFile synchronizeFile];
595 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
597 //now add the right modification date to the file
598 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
602 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*) currentPath toSaveTo:(NSString*) savePath atOffset:(unsigned long long) offset withLoop:(BOOL)looping {
603 //construct file send request
604 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
605 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
606 char dir = USB_FileToHost;
607 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
608 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
609 [build appendData:[NSData dataWithBytes:&nsize length:2]];
610 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
611 [build appendData:d];
612 dir = 0x5c; // 0x5c = "/"
613 [build appendData:[NSData dataWithBytes:&dir length:1]];
614 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
616 [build appendData:[NSData dataWithBytes:&dir length:1]];
617 //add 8 byte offset here
618 NSLog(@"%qu", offset);
619 [build appendData:[NSData dataWithBytes:&offset length:8]];
620 NSLog([build description]);
622 //prepackage commands to send
623 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
624 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
625 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
628 [self checkUSB:myContext];
629 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
631 NSDate* startTime = [NSDate date];
632 // send start request and get response
633 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
634 unsigned int rW = 0x0000100a;
635 NSData *responseWanted = [NSData dataWithBytes:&rW length:4];
636 if ([data length] < 16) {
637 NSLog(@"Incorrect Data length");
640 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
641 if (![responseWanted isEqualToData:responseToCheck]) {
642 NSLog(@"Unexpected response from Toppy during download");
646 // clean up data and prepare path to save it
647 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
648 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
650 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
651 // [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
652 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
653 [outFile seekToFileOffset:offset];
654 [outFile writeData:finalData];
655 if (looping) { // loop for multiple data sends (ie files > 64k)
657 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
659 NSLog(@"large file detected - low GUI update rate");
662 int test = USB_DataHddFileData;
663 double amountReceived = 0xfe00;
664 NSData* testData = [NSData dataWithBytes:&test length:4];
665 while ([header isEqualToData:testData] && [[[NSApp delegate] isConnected] intValue]) {
667 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
668 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
669 amountReceived += 0xfe00;
670 if (timesAround < 8 || timesAround % updateRate == 0) {
671 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)amountReceived], @"offset", fileSize, @"size", startTime, @"startTime", nil]];
673 if ([data length] > 16){ //there is something to read
674 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
675 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
676 [outFile writeData:finalData];
677 [outFile synchronizeFile];
683 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
685 //now add the right modification date to the file
686 [[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObject:[fileInfo objectForKey:@"date"] forKey:@"NSFileModificationDate"] atPath:savePath];
690 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse careForReturn:(BOOL) careFactor{
692 int cmdLength = [fullyPackagedCommand length];
693 unsigned char outBuffer[cmdLength];
694 memset(outBuffer, 0, cmdLength);
695 // NSLog(@"send: %@", [fullyPackagedCommand description]);
696 [fullyPackagedCommand getBytes:outBuffer];
698 err = doSend(device->dev, device->intf, outBuffer, cmdLength, kDat);
700 NSLog(@"sendError: %08x\n");
702 if (! getResponse) return nil;
704 int inLen = 0xFFFF; // i think this is biggest needed?
705 unsigned char inBuf[inLen];
706 memset(inBuf, 0, inLen);
707 err = doRecv(device->dev, device->intf, inBuf, inLen, kDat);
709 NSLog(@"inError: %08x\n", err);
711 if (! careFactor) return nil;
712 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
713 data = [self swap:data];
714 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
715 return [data subdataWithRange:(NSRange) {0,inLen}];
718 -(NSMutableData*) swap: (NSData*) inData {
719 // NSLog(@"Entering swap");
720 // if ([inData length] > 500)
721 // NSLog(@"cut:%@",[[inData subdataWithRange:(NSRange){0,500}] description]);
723 // NSLog([inData description]);
724 int len = [inData length];
727 uneven = 1; // prepare space for padding
729 // NSLog(@"Uneven: %i", uneven);
730 // char array[len + uneven];
731 // char outarray[len + uneven];
732 // memset(array, 0 , len+uneven);
733 // memset(outarray, 0 , len+uneven);
736 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
737 NSLog(@"ERROR: Malloc failed");
738 return [NSMutableData dataWithData:inData];
740 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
741 NSLog(@"ERROR: Malloc failed");
742 return [NSMutableData dataWithData:inData];
744 // NSLog(@"memset complete");
745 [inData getBytes:array length:len];
746 swab(array, outarray, len+uneven);
747 // NSLog(@"swab over");
748 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
749 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
750 // NSLog(@"outdata prepared");
756 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile toDevice:(USBDeviceContext*)dev {
757 // prepare Send command
758 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
759 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
760 char dir = USB_FileToDevice;
761 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
762 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
763 [build appendData:[NSData dataWithBytes:&nsize length:2]];
764 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
765 [build appendData:d];
766 dir = 0x5c; // 0x5c = "/"
767 [build appendData:[NSData dataWithBytes:&dir length:1]];
768 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
769 int f = 0x0000000000;
771 while (i < 95 -[fname length]){
772 [build appendData:[NSData dataWithBytes:&f length:1]];
776 [build appendData:[NSData dataWithBytes:&dir length:1]];
778 // prepackage commands to send
779 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
780 // NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
781 // NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
782 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
783 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
786 NSDate* startTime = [NSDate date];
790 // now the proper commands
791 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
792 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
793 unsigned int rW = 0x00000002;
794 NSData* responseWanted = [NSData dataWithBytes:&rW length:4];
795 NSData* responseToCheck = nil;
797 unsigned long long offset = 0x0000000000000000;
798 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
800 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
801 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset length:8];
802 [fileData appendData:[fileHandle readDataOfLength:rate]];
804 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
805 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
806 if ([data length] >= 8)
807 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
808 else responseToCheck = nil;
809 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
811 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:(double)offset], @"offset", [NSNumber numberWithDouble:(double)size], @"size", startTime, @"startTime", nil]];
813 } while (offset < size && [[[NSApp delegate] isConnected] intValue]);
814 if ([[[NSApp delegate] isConnected] intValue])
815 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
816 [fileHandle closeFile];
820 - (void) turnTurboOn:(BOOL) turnOn forDevice:(USBDeviceContext*)device withReset:(BOOL)reset {
821 if (![[[NSApp delegate] isConnected] intValue]) return;
822 if (reset) [self checkUSB:device];
826 NSData* modeData = [NSData dataWithBytes:&mode length:4];
827 NSLog([modeData description]); //debugging
828 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
829 [self sendCommand:turboCommand toDevice:device expectResponse:YES careForReturn:NO];
833 return connectedSpeed;
836 -(void) setDebug:(int)mode {
838 NSLog(@"Debug level: %i", debug);
841 -(void) setRate:(int) newRate {
843 NSLog(@"New rate set: %i", rate);
846 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField {
848 progressTime = timeField;
851 -(void) updateProgress:(NSDictionary*) inDict {
852 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
853 double offset = [[inDict objectForKey:@"offset"] doubleValue];
854 double size = [[inDict objectForKey:@"size"] doubleValue];
855 NSDate* startTime = [inDict objectForKey:@"startTime"];
856 [progressBar setDoubleValue:((double)offset/size*100)];
857 [progressBar displayIfNeeded];
858 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
859 [progressTime displayIfNeeded];
864 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
867 char sp = 20; //padding
868 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
871 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
874 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
877 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
880 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath onDevice:(USBDeviceContext*)device {
881 [self checkUSB:device];
882 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
883 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
884 char dir = 0x5c; // 0x5c = "\"
885 [build appendData:[NSData dataWithBytes:&dir length:1]];
886 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
888 [build appendData:[NSData dataWithBytes:&dir length:1]];
889 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
890 [self sendCommand:fileDelCmd toDevice:device expectResponse:YES careForReturn:NO];
893 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath onDevice:(USBDeviceContext*)device {
894 [self checkUSB:device];
895 unsigned short int i = [oldName length]+[currentPath length]+2;
896 NSMutableData* build = [NSMutableData dataWithBytes:&i length:2];
897 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
899 [build appendData:[NSData dataWithBytes:&dir length:1]];
900 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
902 [build appendData:[NSData dataWithBytes:&dir length:1]];
903 i = [newName length]+[currentPath length]+2;
904 [build appendData:[NSData dataWithBytes:&i length:2]];
905 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
907 [build appendData:[NSData dataWithBytes:&dir length:1]];
908 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
910 [build appendData:[NSData dataWithBytes:&dir length:1]];
911 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
912 [self sendCommand:fileRenCmd toDevice:device expectResponse:YES careForReturn:NO];
915 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath onDevice:(USBDeviceContext*)device {
916 [self checkUSB:device];
917 unsigned short int i = [newName length]+[currentPath length]+2;
918 NSMutableData* build = [NSMutableData dataWithBytes:&i length:2];
919 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
921 [build appendData:[NSData dataWithBytes:&dir length:1]];
922 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
924 [build appendData:[NSData dataWithBytes:&dir length:1]];
925 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
926 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
927 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO];
928 [self sendCommand:newFoldCmd toDevice:device expectResponse:YES careForReturn:NO];
931 - (void) checkUSB:(USBDeviceContext*)device {
932 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
933 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
934 unsigned int rW = 0x00000002;
936 NSData* responseWanted = [NSData dataWithBytes:&rW length:4];
937 while ((data == nil || [data length] < 8) && i < 8) {
938 NSLog (@"incorrect response");
940 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
943 // tell someone that no longer connected here??
946 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
947 if (![responseWanted isEqualToData:responseToCheck]) {
948 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again