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>
17 * This source code is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Public License as published
19 * by the Free Software Foundation; either version 2 of the License,
20 * or (at your option) any later version.
22 * This source code is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25 * Please refer to the GNU Public License for more details.
27 * You should have received a copy of the GNU Public License along with
28 * this source code; if not, write to:
29 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 /* Much of the code that initializes and closes the device is from Apple
33 * and is released under the license below.
37 * © Copyright 2001 Apple Computer, Inc. All rights reserved.
39 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in
40 * consideration of your agreement to the following terms, and your use, installation,
41 * modification or redistribution of this Apple software constitutes acceptance of these
42 * terms. If you do not agree with these terms, please do not use, install, modify or
43 * redistribute this Apple software.
45 * In consideration of your agreement to abide by the following terms, and subject to these
46 * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this
47 * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute
48 * the Apple Software, with or without modifications, in source and/or binary forms; provided
49 * that if you redistribute the Apple Software in its entirety and without modifications, you
50 * must retain this notice and the following text and disclaimers in all such redistributions
51 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple
52 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software
53 * without specific prior written permission from Apple. Except as expressly stated in this
54 * notice, no other rights or licenses, express or implied, are granted by Apple herein,
55 * including but not limited to any patent rights that may be infringed by your derivative
56 * works or by other works in which the Apple Software may be incorporated.
58 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
59 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
60 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
61 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
63 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
65 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
66 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
67 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
68 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76 #include <mach/mach.h>
77 #include <IOKit/IOCFPlugIn.h>
78 #include <CoreFoundation/CFNumber.h>
80 #import "DataHandler.h"
81 #import "TFUSBController.h"
82 #import "TFUSBControllerCommunicationBlock.h"
83 #import "UIElements.h"
85 static void hexDump(UInt8 *buf, int len);
86 static int doSend(IOUSBDeviceInterface197 **dev,
87 IOUSBInterfaceInterface197 **intf, UInt8 *outBuf, UInt32 len, int type);
88 static int doRecv(IOUSBDeviceInterface197 **dev,
89 IOUSBInterfaceInterface197 **intf, UInt8 *inBuf, UInt32 dataLen, int type);
90 static int dealWithInterface(io_service_t usbInterfaceRef,
91 USBDeviceContext *device);
92 static int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device);
93 static int initDevice(USBDeviceContext *device);
97 static int kBlockSize;
98 static int connectedSpeed;
100 @interface TFUSBController (PrivateMethods)
103 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
104 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
105 careForReturn:(BOOL) careFactor;
107 // Protocol message sequences
108 - (id) getFileListForPath:(NSString*)path;
109 - (void) turnTurboOn:(BOOL) turnOn;
110 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath;
111 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath;
112 - (void) checkUSB:(USBDeviceContext*)device;
115 - (UIElements*) uiElements;
116 - (void) updateProgress:(NSDictionary*) inDict;
117 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds;
121 #define TopfieldVendorID 4571
125 #define TF5kProdID 4096
130 // ---------------------------------------------------------------------------
131 // Functions outside TFUSBController
132 // ---------------------------------------------------------------------------
135 hexDump(UInt8 *buf, int len)
137 int row, col, maxrows;
140 if (len % 16) maxrows++;
141 for (row=0; row< maxrows; row++) {
142 for (col=0; col<16; col++) {
143 if (!(col%2)) printf(" ");
144 printf("%02x", buf[row*16 + col] & 0xff);
147 for (col=0; col<16; col++) {
148 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
149 printf("%c", buf[row*16 + col]);
151 else { printf("."); }
158 doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
159 UInt8 *outBuf, UInt32 len, int type)
165 printf(("sending:\n"));
166 hexDump(outBuf, len);
168 sendLen = ((len/kBlockSize))*kBlockSize;
169 if (len % kBlockSize)
170 sendLen += kBlockSize;
171 if ((sendLen % 0x200) == 0)
172 sendLen += kBlockSize;
174 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
176 printf("write err: %08x\n", err);
183 doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
184 UInt8 *inBuf, UInt32 dataLen, int type)
189 if (dataLen > kMaxXferSize) return 1;
191 len = (dataLen/kBlockSize) * kBlockSize;
192 if (dataLen % kBlockSize)
195 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
197 printf("read err 2: %08x\n", err);
198 printf("resetting\n");
199 err = (*intf)->ClearPipeStallBothEnds(intf, 2);
204 printf(("receiving: \n"));
211 dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
214 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
215 IOUSBInterfaceInterface197 **intf;
216 IOUSBDeviceInterface197 **dev;
218 UInt8 numPipes, confNum, dSpeed;
221 err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
224 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
225 return kUproarDeviceErr;
227 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
228 (*iodev)->Release(iodev); // done with this
232 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
233 return kUproarDeviceErr;
237 err = (*intf)->USBInterfaceOpen(intf);
240 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
241 return kUproarDeviceErr;
243 err = (*intf)->GetNumEndpoints(intf, &numPipes);
246 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
247 return kUproarDeviceErr;
250 printf("dealWithInterface: found %d pipes\n", numPipes);
252 err = (*intf)->GetConfigurationValue(intf, &confNum);
253 err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
259 printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
260 connectedSpeed = dSpeed;
261 return kUproarSuccess;
266 dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
269 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
270 IOUSBDeviceInterface197 **dev;
273 IOUSBConfigurationDescriptorPtr confDesc;
274 IOUSBFindInterfaceRequest interfaceRequest;
275 io_iterator_t iterator;
276 io_service_t usbInterfaceRef;
280 err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
283 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
284 return kUproarDeviceErr;
286 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
287 (*iodev)->Release(iodev);
292 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
293 return kUproarDeviceErr;
295 err = (*dev)->USBDeviceOpen(dev);
298 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
299 return kUproarDeviceErr;
301 err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
304 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
305 return kUproarDeviceErr;
307 printf("dealWithDevice: found %d configurations\n", numConf);
308 err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
311 printf("dealWithDevice:unable to get config descriptor for index 0\n");
312 return kUproarDeviceErr;
314 err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
317 printf("dealWithDevice: unable to set the configuration\n");
318 return kUproarDeviceErr;
321 interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare; // requested class
322 interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
323 interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
324 interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting
326 err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
329 printf("dealWithDevice: unable to create interface iterator\n");
330 return kUproarDeviceErr;
333 while (usbInterfaceRef = IOIteratorNext(iterator))
335 printf("found interface: %p\n", (void*)usbInterfaceRef);
336 err = dealWithInterface(usbInterfaceRef, device);
337 IOObjectRelease(usbInterfaceRef); // no longer need this reference
341 IOObjectRelease(iterator);
343 if ((!found) || (err))
344 return kUproarDeviceErr;
346 return kUproarSuccess;
350 initDevice(USBDeviceContext *device)
352 mach_port_t masterPort = 0;
354 CFMutableDictionaryRef matchingDictionary = 0; // requires <IOKit/IOKitLib.h>
355 short idVendor = TopfieldVendorID;
356 short idProduct = TF5kProdID;
357 CFNumberRef numberRef;
358 io_iterator_t iterator = 0;
359 io_service_t usbDeviceRef;
362 err = IOMasterPort(bootstrap_port, &masterPort);
366 printf("Anchortest: could not create master port, err = %08x\n", err);
369 matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); // requires <IOKit/usb/IOUSBLib.h>
370 if (!matchingDictionary)
372 printf("Anchortest: could not create matching dictionary\n");
375 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
378 printf("Anchortest: could not create CFNumberRef for vendor\n");
381 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
382 CFRelease(numberRef);
384 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
387 printf("Anchortest: could not create CFNumberRef for product\n");
390 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
391 CFRelease(numberRef);
394 err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
395 matchingDictionary = 0; // this was consumed by the above call
397 while (usbDeviceRef = IOIteratorNext(iterator))
399 printf("Found device %p\n", (void*)usbDeviceRef);
400 err = dealWithDevice(usbDeviceRef, device);
401 IOObjectRelease(usbDeviceRef); // no longer need this reference
406 IOObjectRelease(iterator);
408 mach_port_deallocate(mach_task_self(), masterPort);
409 if ((!found) || (err))
410 return kUproarDeviceErr;
412 return kUproarSuccess;
415 // ---------------------------------------------------------------------------
416 // PrivateMethods: Low-level USB
417 // ---------------------------------------------------------------------------
419 @implementation TFUSBController (PrivateMethods)
421 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
422 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
423 careForReturn:(BOOL) careFactor{
425 int cmdLength = [fullyPackagedCommand length];
426 unsigned char outBuffer[cmdLength];
427 memset(outBuffer, 0, cmdLength);
428 // NSLog(@"send: %@", [fullyPackagedCommand description]);
429 [fullyPackagedCommand getBytes:outBuffer];
431 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
433 NSLog(@"sendError: %08x\n");
435 if (! getResponse) return nil;
437 int inLen = 0xFFFF; // i think this is biggest needed?
438 unsigned char inBuf[inLen];
439 memset(inBuf, 0, inLen);
440 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
442 NSLog(@"inError: %08x\n", err);
444 if (! careFactor) return nil;
445 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
446 data = [self swap:data];
447 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
448 return [data subdataWithRange:(NSRange) {0,inLen}];
451 // ---------------------------------------------------------------------------
452 // PrivateMethods: Protocol message sequences
453 // ---------------------------------------------------------------------------
455 - (id) getFileListForPath:(NSString*) path {
456 if (myContext == nil)
459 NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
460 if (hddListCmd == nil)
463 [self checkUSB:myContext]; // sends cancel and waits for response
464 int contiguousErrors = 0;
465 [[dh fileList] removeAllObjects];
466 NSData* response = [self sendCommand:hddListCmd toDevice:myContext
467 expectResponse:YES careForReturn:YES];
468 while (response != nil && contiguousErrors++ < 5) {
469 const UInt32 eCode = [self checkCommunicationBlock:response];
470 if (eCode != USB_OK) {
471 response = [self sendCommand:[self prepareFailWithECode:eCode]
472 toDevice:myContext expectResponse:YES careForReturn:YES];
473 } else switch ([self cmdFromCommunicationBlock:response]) {
475 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
476 [[dh fileList] removeAllObjects];
477 response = [self sendCommand:hddListCmd toDevice:myContext
478 expectResponse:YES careForReturn:YES];
481 contiguousErrors = 0;
483 // Swapping sometimes adds a byte of padding. The following uses
484 // only complete 144-byte structures and so ignores such padding.
485 for (i=0; 8+(i+1)*114 <= [response length]; i++) {
486 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
487 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:typeFile];
488 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
489 [dh convertRawDataToUseful:tfFile];
490 [[dh fileList] addObject:tfFile];
493 response = [self sendCommand:[self prepareSuccess]
494 toDevice:myContext expectResponse:YES careForReturn:YES];
496 case USB_DataHddDirEnd:
497 contiguousErrors = 0;
501 [self checkUSB:myContext]; // cancel whatever is going on
502 [[dh fileList] removeAllObjects];
503 response = [self sendCommand:hddListCmd toDevice:myContext
504 expectResponse:YES careForReturn:YES];
509 [tableView reloadData];
510 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
511 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
515 - (void) turnTurboOn:(BOOL) turnOn {
516 if (![[[self uiElements] isConnected] intValue]) return;
517 [self checkUSB:myContext];
518 NSData* turboCommand = [self prepareCmdTurboWithMode:(turnOn ? 1 : 0)];
519 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
522 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
523 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
524 [self checkUSB:myContext];
525 NSString* oldFname = [self fnameForFile:oldName atPath:currentPath];
526 NSString* newFname = [self fnameForFile:newName atPath:currentPath];
527 NSData* fileRenCmd = [self prepareCmdHddRenameFromFname:oldFname toFname:newFname];
528 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
531 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
532 [self checkUSB:myContext];
533 NSString* fname = [self fnameForFile:newName atPath:currentPath];
534 NSData* newFoldCmd = [self prepareCmdHddCreateDirWithFname:fname];
535 NSData* usbCancel = [self prepareCancel];
536 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
537 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
540 - (void) checkUSB:(USBDeviceContext*)device {
541 NSData* usbCancel = [self prepareCancel];
542 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
543 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
545 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
546 while ((data == nil || [data length] < 8) && i < 8) {
547 NSLog (@"incorrect response");
549 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
552 // tell someone that no longer connected here??
555 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
556 if (![responseWanted isEqualToData:responseToCheck]) {
557 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
561 // ---------------------------------------------------------------------------
562 // PrivateMethods: User interface
563 // ---------------------------------------------------------------------------
565 - (UIElements*) uiElements {
566 return [NSApp delegate];
569 -(void) updateProgress:(NSDictionary*) inDict {
570 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
571 double offset = [[inDict objectForKey:@"offset"] doubleValue];
572 double size = [[inDict objectForKey:@"size"] doubleValue];
573 NSDate* startTime = [inDict objectForKey:@"startTime"];
574 [progressBar setDoubleValue:((double)offset/size*100)];
575 [progressBar displayIfNeeded];
576 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
577 [progressTime displayIfNeeded];
581 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
584 char sp = 20; //padding
585 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
588 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
591 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
594 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
599 // ---------------------------------------------------------------------------
601 // ---------------------------------------------------------------------------
603 @implementation TFUSBController
605 - (void) closeDevice:(USBDeviceContext *)device
607 IOUSBInterfaceInterface197 **intf;
608 IOUSBDeviceInterface197 **dev;
614 err = (*intf)->USBInterfaceClose(intf);
617 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
619 err = (*intf)->Release(intf);
622 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
625 err = (*dev)->USBDeviceClose(dev);
628 printf("dealWithDevice: error closing device - %08x\n", err);
629 (*dev)->Release(dev);
631 err = (*dev)->Release(dev);
634 printf("dealWithDevice: error releasing device - %08x\n", err);
638 - (USBDeviceContext*) initializeUSB {
639 USBDeviceContext *device;
642 device = malloc(sizeof(USBDeviceContext));
643 err = initDevice(device);
645 printf("Could not connect to Topfield\n");
649 printf("Connected to Topfield\n\n");
654 // ---------------------------------------------------------------------------
655 // Protocol message sequences
656 // ---------------------------------------------------------------------------
658 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
659 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
660 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
661 // [progressBar setDoubleValue:0];
662 // [progressTime setDoubleValue:0];
663 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
664 [[[self uiElements] currentlyField] setStringValue:
665 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
666 [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
667 [[[self uiElements] currentlyField] displayIfNeeded];
668 //construct file send request
669 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
671 //prepackage commands to send
672 NSData* fileSendCmd = [self prepareCmdHddFileSendWithDirection:USB_FileToHost
673 fname:[self fnameForFile:nameOnToppy atPath:currentPath]
675 NSData* usbSuccess = [self prepareSuccess];
676 NSData* usbCancel = [self prepareCancel];
681 [self turnTurboOn:YES];
683 [self checkUSB:myContext]; //turbo has a check itself
684 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
686 NSDate* startTime = [NSDate date];
687 startTime = [startTime addTimeInterval:(0-existingTime)];
688 // send start request and get response
689 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
690 const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
691 NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
692 if ([data length] < 16) {
693 NSLog(@"Incorrect Data length");
696 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
697 if (![responseWanted isEqualToData:responseToCheck]) {
698 NSLog(@"Unexpected response from Toppy during download");
702 // clean up data and prepare path to save it
703 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
704 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
706 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
708 [[NSData dataWithBytes:"\0" length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
709 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
710 [outFile seekToFileOffset:offset];
711 [outFile writeData:finalData];
712 if (looping) { // loop for multiple data sends (ie files > 64k)
714 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
716 NSLog(@"large file detected - low GUI update rate");
719 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
720 double amountReceived = 0xfe00 + offset;
721 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
722 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
723 if ([priorityTransferQueue count] != 0) {
724 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
725 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
726 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
727 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
728 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
730 atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
731 // send reset again just to make sure!
732 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
734 //now add the right modification date to the file
735 [[NSFileManager defaultManager]
736 changeFileAttributes:[NSDictionary
737 dictionaryWithObject:[fileInfo objectForKey:@"date"]
738 forKey:@"NSFileModificationDate"]
740 [self turnTurboOn:NO];
744 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
745 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
746 amountReceived += 0xfe00;
747 if (timesAround < 8 || timesAround % updateRate == 0) {
748 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
749 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
750 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
751 fileSize, @"size", startTime, @"startTime", nil]];
753 if ([data length] > 16){ //there is something to read
754 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
755 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
756 [outFile writeData:finalData];
757 [outFile synchronizeFile];
763 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
765 //now add the right modification date to the file
766 [[NSFileManager defaultManager]
767 changeFileAttributes:[NSDictionary
768 dictionaryWithObject:[fileInfo objectForKey:@"date"]
769 forKey:@"NSFileModificationDate"]
771 [self turnTurboOn:NO];
772 if (looping) [[self uiElements] finishTransfer];
776 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
777 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
778 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
779 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
780 USBDeviceContext* dev = myContext;
781 // prepare Send command
782 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
783 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
784 char dir = USB_FileToDevice;
785 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
786 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
787 const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
788 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
789 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
790 [build appendData:d];
791 dir = 0x5c; // 0x5c = "/"
792 [build appendData:[NSData dataWithBytes:&dir length:1]];
793 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
794 if ([fname length] < 95)
795 [build increaseLengthBy:95-[fname length]];
797 [build appendData:[NSData dataWithBytes:&dir length:1]];
798 UInt64 offset_bigendian = EndianU64_NtoB(offset);
799 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
801 // prepackage commands to send
802 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
803 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
804 NSData* fileEndCmd = [self prepareDataHddFileEnd];
807 NSDate* startTime = [NSDate date];
808 startTime = [startTime addTimeInterval:(0-existingTime)];
811 [self turnTurboOn:YES];
814 // now the proper commands
815 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
816 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
817 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
818 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
819 NSData* responseToCheck = nil;
821 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
822 [fileHandle seekToFileOffset:offset];
824 if ([priorityTransferQueue count] != 0) {
825 //break out and create a new transfer to continue it
826 NSLog(@"pausing upload");
827 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
828 fileToUpload,@"filename",
829 [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
830 curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
831 [NSNumber numberWithUnsignedLongLong:offset],@"offset",
832 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
834 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
835 [self turnTurboOn:NO];
838 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
839 offset_bigendian = EndianU64_NtoB(offset);
840 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
841 [fileData appendData:[fileHandle readDataOfLength:rate]];
843 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
844 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
845 if ([data length] >= 8)
846 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
847 else responseToCheck = nil;
848 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
850 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
851 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
852 [NSNumber numberWithDouble:(double)offset], @"offset",
853 [NSNumber numberWithDouble:(double)size], @"size",
854 startTime, @"startTime", nil]];
857 } while (offset < size && [[[self uiElements] isConnected] intValue]);
858 if ([[[self uiElements] isConnected] intValue])
859 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
860 [fileHandle closeFile];
861 [[self uiElements] goToPath:[[self uiElements]currentPath]];
862 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
863 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
864 [self turnTurboOn:NO];
865 [[self uiElements] finishTransfer];
868 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
869 [self checkUSB:myContext];
870 NSString* fname = [self fnameForFile:[fileInfo objectForKey:@"name"]
872 NSData* fileDelCmd = [self prepareCmdHddDelWithFname:fname];
873 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
876 // ---------------------------------------------------------------------------
878 // ---------------------------------------------------------------------------
880 - (void) addPriorityTransfer:(id)newTransfer {
881 [priorityTransferQueue addObject:newTransfer];
882 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
885 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
886 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
887 else [transferQueue addObject:newTransfer];
888 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
891 - (void) clearQueues {
892 [priorityTransferQueue removeAllObjects];
893 [transferQueue removeAllObjects];
894 [pausedQueue removeAllObjects];
901 - (void) transfer:(id)sender {
902 while ([[[self uiElements] isConnected] intValue]) {
903 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
904 if ([priorityTransferQueue count] != 0 ) {
905 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
906 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
907 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
908 [priorityTransferQueue removeObjectAtIndex:0];
909 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
911 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
912 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
913 [priorityTransferQueue removeObjectAtIndex:0];
915 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
916 [self renameFile:[currentTransfer objectForKey:@"oldName"]
917 withName:[currentTransfer objectForKey:@"newName"]
918 atPath:[currentTransfer objectForKey:@"path"]];
919 [priorityTransferQueue removeObjectAtIndex:0];
921 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
922 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
923 [priorityTransferQueue removeObjectAtIndex:0];
925 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
926 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
927 [priorityTransferQueue removeObjectAtIndex:0];
929 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
930 if ([transferQueue count] != 0) {
931 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
932 [NSPredicate predicateWithFormat:@"(filename = %@)",
933 [currentTransfer objectForKey:@"filename"]]];
934 if ([toPause count] > 1) NSLog(@"multiple pauses?");
936 [pausedQueue addObjectsFromArray:toPause];
937 [transferQueue removeObjectsInArray:toPause];
940 [priorityTransferQueue removeObjectAtIndex:0];
942 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
943 if ([pausedQueue count] != 0) {
944 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
945 [NSPredicate predicateWithFormat:@"(filename = %@)",
946 [currentTransfer objectForKey:@"filename"]]];
947 if ([toResume count] > 1) NSLog(@"multiple resumes?");
949 [transferQueue addObjectsFromArray:toResume];
950 [pausedQueue removeObjectsInArray:toResume];
953 [priorityTransferQueue removeObjectAtIndex:0];
956 else if ([transferQueue count] != 0) {
957 id currentTransfer = [transferQueue objectAtIndex:0];
958 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
959 [self getFile:[currentTransfer objectForKey:@"filename"]
960 forPath:[currentTransfer objectForKey:@"path"]
961 toSaveTo:[currentTransfer objectForKey:@"savePath"]
962 beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
963 withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
964 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
965 [transferQueue removeObjectAtIndex:0];
966 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
968 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
969 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
970 [self uploadFile:[currentTransfer objectForKey:@"filename"]
971 ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
972 fromPath:[currentTransfer objectForKey:@"path"]
973 withAttributes:[currentTransfer objectForKey:@"attributes"]
974 atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
975 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
976 [transferQueue removeObjectAtIndex:0];
977 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
985 // ---------------------------------------------------------------------------
987 // ---------------------------------------------------------------------------
989 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
991 progressTime = timeField;
995 - (void) setDH:(id)newDH tableView:(id)tv {
1000 // ---------------------------------------------------------------------------
1002 // ---------------------------------------------------------------------------
1005 if (self = [super init]) {
1008 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1009 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1010 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1016 return connectedSpeed;
1019 -(void) setDebug:(int)mode {
1021 NSLog(@"Debug level: %i", debug);
1024 -(void) setRate:(int) newRate {
1026 NSLog(@"New rate set: %i", rate);
1029 - (BOOL) hasCurrentTransfer {
1030 return [transferQueue count] > 0;
1033 - (id) currentTransferInfo {
1034 if ([transferQueue count] == 0)
1037 // Make a copy of the object so that it can be safely manipulated
1038 // in a different thread from the original.
1039 return [[transferQueue objectAtIndex:0] copy];