#pragma mark in TFUSBController
[MacTF.git] / TFUSBController.m
blob882cbd41ae1e7ec0b0c6f4a990a62222e3526db5
1 //
2 //  TFUSBController.m
3 //  MacTF
4 //
5 //  Created by Nathan Oates on Sun Aug 01 2004.
6 //  Copyright (c) 2004-7 Nathan Oates nathan@noates.com All rights reserved.
7 //
9 /* This file was modified by Kalle Olavi Niemitalo on 2007-10-18.  */
11 // includes code based on uproar, license noted below:
12 //  uproar 0.1
13 //  Copyright (c) 2001 Kasima Tharnpipitchai <me@kasima.org>
15 /* 
16  *
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.
21  *
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.
26  *
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.
30  */
32 /* Much of the code that initializes and closes the device is from Apple
33  * and is released under the license below.
34  */
37  * © Copyright 2001 Apple Computer, Inc. All rights reserved.
38  *
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.
44  *
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.
57  * 
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. 
62  *
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.
69  */                     
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
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);
95 static int debug;
96 static int rate;
97 static int kBlockSize;
98 static int connectedSpeed;
100 @interface TFUSBController (PrivateMethods)
102 // Low-level USB
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;
114 // User interface
115 - (UIElements*) uiElements;
116 - (void) updateProgress:(NSDictionary*) inDict;
117 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds;
119 // Queue management
120 - (BOOL) hasPriorityTransfer;
121 - (NSDictionary*) nextTransferAndQueue:(NSMutableArray**)queue;
122 - (void) removeTransfer:(NSDictionary*)transfer fromQueue:(NSMutableArray*)queue;
124 @end
126 #define         TopfieldVendorID        4571
127    //1317 for HC
128    //0x138c for humax
129    //4571 for toppy
130 #define         TF5kProdID      4096
131 //42514 for HC
132 //0x02ad for humax
133 //4096 for toppy
135 // ---------------------------------------------------------------------------
136 #pragma mark Functions outside TFUSBController
137 // ---------------------------------------------------------------------------
139 static void
140 hexDump(UInt8 *buf, int len)
142         int row, col, maxrows;
143         
144         maxrows = len/16;
145         if (len % 16) maxrows++;
146         for (row=0; row< maxrows; row++) {
147                 for (col=0; col<16; col++) {
148                         if (!(col%2)) printf(" ");
149                         printf("%02x", buf[row*16 + col] & 0xff);
150                 }
151                 printf("\t");   
152                 for (col=0; col<16; col++) {
153                         if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
154                                 printf("%c", buf[row*16 + col]);
155                         }
156                         else { printf("."); }
157                 }
158                 printf("\n");
159         }
162 static int
163 doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
164         UInt8 *outBuf, UInt32 len, int type)
166         IOReturn        err;
167         UInt32          sendLen;        
168         
169                 if (debug == 1){
170                         printf(("sending:\n"));
171             hexDump(outBuf, len);
172                 }
173                 sendLen = ((len/kBlockSize))*kBlockSize;
174         if (len % kBlockSize)
175             sendLen += kBlockSize;
176         if ((sendLen % 0x200) == 0)  
177                 sendLen += kBlockSize;
179         err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
180         if (err) {
181                 printf("write err: %08x\n", err);
182                 return err;
183         }
184         return err;
187 static int
188 doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
189         UInt8 *inBuf, UInt32 dataLen, int type)
191         IOReturn        err;
192         UInt32          len;
193         
194         if (dataLen > kMaxXferSize) return 1;
195         
196         len = (dataLen/kBlockSize) * kBlockSize;
197         if (dataLen % kBlockSize)
198                 len += kBlockSize;
200                 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len,  1000, 20000);
201                 if (err) {
202                 printf("read err 2: %08x\n", err);
203                                 printf("resetting\n");
204                                 err = (*intf)->ClearPipeStallBothEnds(intf, 2); 
205                         return err;
206         }
207                 
208         if (debug == 1) {
209           printf(("receiving: \n")); 
210                         hexDump(inBuf, len);
211                 }
212         return err;
215 static int
216 dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
218     IOReturn                            err;
219     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
220     IOUSBInterfaceInterface197          **intf;
221     IOUSBDeviceInterface197             **dev;
222     SInt32                              score;
223         UInt8                           numPipes, confNum, dSpeed;
226     err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
227     if (err || !iodev)
228     {
229         printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
230         return kUproarDeviceErr;
231     }
232     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
233     (*iodev)->Release(iodev);                           // done with this
234     intf = device->intf;
235     if (err || !intf)
236     {
237         printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
238         return kUproarDeviceErr;
239     }
240     
241     dev = device->dev;
242     err = (*intf)->USBInterfaceOpen(intf);
243     if (err)
244     {
245         printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
246         return kUproarDeviceErr;
247     }
248     err = (*intf)->GetNumEndpoints(intf, &numPipes);
249     if (err)
250     {
251         printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
252         return kUproarDeviceErr;
253     }
254     
255     printf("dealWithInterface: found %d pipes\n", numPipes);
257     err = (*intf)->GetConfigurationValue(intf, &confNum);
258     err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
259         if (dSpeed == 2) {
260                 kBlockSize = 0x40;
261         } else {
262                 kBlockSize = 0x4;
263         }
264     printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
265     connectedSpeed = dSpeed;
266     return kUproarSuccess;
270 static int
271 dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
273     IOReturn                            err;
274     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
275     IOUSBDeviceInterface197             **dev;
276     SInt32                              score;
277     UInt8                               numConf;
278     IOUSBConfigurationDescriptorPtr     confDesc;
279     IOUSBFindInterfaceRequest           interfaceRequest;
280     io_iterator_t                       iterator;
281     io_service_t                        usbInterfaceRef;
282     int                                 found=0;
283     
284     
285     err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
286     if (err || !iodev)
287     {
288         printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
289         return kUproarDeviceErr;
290     }
291     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
292     (*iodev)->Release(iodev);                           
293     
294     dev = device->dev;
295     if (err || !dev)
296     {
297         printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
298         return kUproarDeviceErr;
299     }
300     err = (*dev)->USBDeviceOpen(dev);
301     if (err)
302     {
303         printf("dealWithDevice: unable to open device. ret = %08x\n", err);
304         return kUproarDeviceErr;
305     }
306     err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
307     if (err || !numConf)
308     {
309         printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
310         return kUproarDeviceErr;
311     }
312     printf("dealWithDevice: found %d configurations\n", numConf);
313     err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc);     // get the first config desc (index 0)
314     if (err)
315     {
316         printf("dealWithDevice:unable to get config descriptor for index 0\n");
317         return kUproarDeviceErr;
318     }
319     err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
320     if (err)
321     {
322         printf("dealWithDevice: unable to set the configuration\n");
323         return kUproarDeviceErr;
324     }
326     interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare;             // requested class
327     interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;          // requested subclass
328     interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;          // requested protocol
329     interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;           // requested alt setting
330     
331     err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
332     if (err)
333     {
334         printf("dealWithDevice: unable to create interface iterator\n");
335         return kUproarDeviceErr;
336     }
337     
338     while (usbInterfaceRef = IOIteratorNext(iterator))
339     {
340         printf("found interface: %p\n", (void*)usbInterfaceRef);
341         err = dealWithInterface(usbInterfaceRef, device);
342         IOObjectRelease(usbInterfaceRef);       // no longer need this reference
343         found = 1;
344     }
345     
346     IOObjectRelease(iterator);
347     iterator = 0;
348     if ((!found) || (err))
349         return kUproarDeviceErr;
350     else
351         return kUproarSuccess;
354 static int
355 initDevice(USBDeviceContext *device)
357     mach_port_t         masterPort = 0;
358     kern_return_t               err;
359     CFMutableDictionaryRef      matchingDictionary = 0;         // requires <IOKit/IOKitLib.h>
360     short                       idVendor = TopfieldVendorID;
361     short                       idProduct = TF5kProdID;
362     CFNumberRef                 numberRef;
363     io_iterator_t               iterator = 0;
364     io_service_t                usbDeviceRef;
365     int                         found =0;
366     
367     err = IOMasterPort(bootstrap_port, &masterPort);                    
368     
369     if (err)
370     {
371         printf("Anchortest: could not create master port, err = %08x\n", err);
372         return err;
373     }
374     matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);      // requires <IOKit/usb/IOUSBLib.h>
375     if (!matchingDictionary)
376     {
377         printf("Anchortest: could not create matching dictionary\n");
378         return -1;
379     }
380     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
381     if (!numberRef)
382     {
383         printf("Anchortest: could not create CFNumberRef for vendor\n");
384         return -1;
385     }
386     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
387     CFRelease(numberRef);
388     numberRef = 0;
389     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
390     if (!numberRef)
391     {
392         printf("Anchortest: could not create CFNumberRef for product\n");
393         return -1;
394     }
395     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
396     CFRelease(numberRef);
397     numberRef = 0;
398     
399     err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
400     matchingDictionary = 0;                     // this was consumed by the above call
401     
402     while (usbDeviceRef = IOIteratorNext(iterator))
403     {
404         printf("Found device %p\n", (void*)usbDeviceRef);
405         err = dealWithDevice(usbDeviceRef, device);
406         IOObjectRelease(usbDeviceRef);                  // no longer need this reference
407         found = 1;
408     }
409     
410     
411     IOObjectRelease(iterator);
412     iterator = 0;
413     mach_port_deallocate(mach_task_self(), masterPort);
414     if ((!found) || (err))
415         return kUproarDeviceErr;
416     else
417         return kUproarSuccess;
420 @implementation TFUSBController (PrivateMethods)
422 // ---------------------------------------------------------------------------
423 #pragma mark Low-level USB (PrivateMethods)
424 // ---------------------------------------------------------------------------
426 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
427                 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
428                 careForReturn:(BOOL) careFactor{
429         IOReturn        err;
430         int cmdLength = [fullyPackagedCommand length];
431         unsigned char outBuffer[cmdLength];
432         memset(outBuffer, 0, cmdLength);
433 //      NSLog(@"send: %@", [fullyPackagedCommand description]);
434         [fullyPackagedCommand getBytes:outBuffer];
435         
436         err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
437         if (err)
438                 NSLog(@"sendError: %08x\n");
439         
440         if (! getResponse) return nil;
441         
442         int inLen = 0xFFFF; // i think this is biggest needed?
443         unsigned char inBuf[inLen];
444         memset(inBuf, 0, inLen);
445         err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
446         if (err)
447                 NSLog(@"inError: %08x\n", err);
448         
449         if (! careFactor) return nil;
450         NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
451         data = [self swap:data];
452         inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
453         return [data subdataWithRange:(NSRange) {0,inLen}];
456 // ---------------------------------------------------------------------------
457 #pragma mark Protocol message sequences (PrivateMethods)
458 // ---------------------------------------------------------------------------
460 - (id) getFileListForPath:(NSString*) path {    
461         if (myContext == nil)
462                 return nil;
464         NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
465         if (hddListCmd == nil)
466                 return nil;
468         [self checkUSB:myContext]; // sends cancel and waits for response
469         int contiguousErrors = 0;
470         [[dh fileList] removeAllObjects];
471         NSData* response = [self sendCommand:hddListCmd toDevice:myContext
472                 expectResponse:YES careForReturn:YES];
473         while (response != nil && contiguousErrors++ < 5) {
474                 const UInt32 eCode = [self checkCommunicationBlock:response];
475                 if (eCode != USB_OK) {
476                         response = [self sendCommand:[self prepareFailWithECode:eCode]
477                                 toDevice:myContext expectResponse:YES careForReturn:YES];
478                 } else switch ([self cmdFromCommunicationBlock:response]) {
479                 case USB_Fail:
480 //                      [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
481                         [[dh fileList] removeAllObjects];
482                         response = [self sendCommand:hddListCmd toDevice:myContext
483                                 expectResponse:YES careForReturn:YES];
484                         break;
485                 case USB_DataHddDir:
486                         contiguousErrors = 0;
487                         size_t i;
488                         // Swapping sometimes adds a byte of padding.  The following uses
489                         // only complete 144-byte structures and so ignores such padding.
490                         for (i=0; 8+(i+1)*114 <= [response length]; i++) {
491                                 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
492                                 NSMutableDictionary* tfFile = [dh newTFFileFromSwappedHexData:typeFile];
493                                 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
494                                         [dh convertRawDataToUseful:tfFile];
495                                         [[dh fileList] addObject:tfFile];
496                                 }
497                                 [tfFile release];
498                         }
499                         response = [self sendCommand:[self prepareSuccess]
500                                 toDevice:myContext expectResponse:YES careForReturn:YES];
501                         break;
502                 case USB_DataHddDirEnd:
503                         contiguousErrors = 0;
504                         response = nil;
505                         break;
506                 default:
507                         [self checkUSB:myContext]; // cancel whatever is going on
508                         [[dh fileList] removeAllObjects];
509                         response = [self sendCommand:hddListCmd toDevice:myContext
510                                 expectResponse:YES careForReturn:YES];
511                         break;
512                 }
513         }
515         [tableView reloadData];
516         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
517         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
518         return nil;
521 - (void) turnTurboOn:(BOOL) turnOn {
522         if (![[[self uiElements] isConnected] intValue]) return;
523         [self checkUSB:myContext];
524         NSData* turboCommand = [self prepareCmdTurboWithMode:(turnOn ? 1 : 0)];
525         [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
528 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
529         NSLog(@"%@,%@,%@", oldName, newName, currentPath);
530         [self checkUSB:myContext];
531         NSString* oldFname = [self fnameForFile:oldName atPath:currentPath];
532         NSString* newFname = [self fnameForFile:newName atPath:currentPath];
533         NSData* fileRenCmd = [self prepareCmdHddRenameFromFname:oldFname toFname:newFname];
534         [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
537 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
538         [self checkUSB:myContext];
539         NSString* fname = [self fnameForFile:newName atPath:currentPath];
540         NSData* newFoldCmd = [self prepareCmdHddCreateDirWithFname:fname];
541         NSData* usbCancel = [self prepareCancel];
542         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
543         [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
546 - (void) checkUSB:(USBDeviceContext*)device {
547         NSData* usbCancel = [self prepareCancel];
548         NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
549         const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
550         int i = 0;
551         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
552         while ((data == nil || [data length] < 8) && i < 8) {
553                 NSLog (@"incorrect response");
554                 i++;
555                 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
556         }
557         if (!i<=8) {
558                 // tell someone that no longer connected here??
559                 return;
560                 }
561         NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
562         if (![responseWanted isEqualToData:responseToCheck]) {
563                 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again   
564         }
567 // ---------------------------------------------------------------------------
568 #pragma mark User interface (PrivateMethods)
569 // ---------------------------------------------------------------------------
571 - (UIElements*) uiElements {
572         return [NSApp delegate];
575 -(void) updateProgress:(NSDictionary*) inDict {
576         NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
577         double offset = [[inDict objectForKey:@"offset"] doubleValue];
578         double size = [[inDict objectForKey:@"size"] doubleValue];
579         NSDate* startTime = [inDict objectForKey:@"startTime"];         
580         [progressBar setDoubleValue:((double)offset/size*100)];
581         [progressBar displayIfNeeded];
582         [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
583         [progressTime displayIfNeeded];
584         [pool release];
587 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
588         char hp = 20;
589         char mp = 20;
590         char sp = 20; //padding
591         int hours = (totalSeconds / 3600);  // returns number of whole hours fitted in totalSecs
592         if (hours < 10)
593                 hp = 48;
594         int minutes = ((totalSeconds / 60) - hours*60);  // Whole minutes
595         if (minutes < 10)
596                 mp = 48;
597         int seconds = ((long) totalSeconds % 60);       // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
598         if (seconds < 10)
599                 sp = 48;
600         return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
603 // ---------------------------------------------------------------------------
604 #pragma mark Queue management (PrivateMethods)
605 // ---------------------------------------------------------------------------
607 - (BOOL) hasPriorityTransfer {
608         BOOL ret;
609         @synchronized (self) {
610                 ret = ([priorityTransferQueue count] > 0);
611         }
612         return ret;
615 - (NSDictionary*) nextTransferAndQueue:(NSMutableArray**)queueOut {
616         NSDictionary* transfer = nil;
617         NSMutableArray* queue = nil;
619         @synchronized (self) {
620                 if ([priorityTransferQueue count] > 0) {
621                         queue = priorityTransferQueue;
622                         transfer = [queue objectAtIndex:0];
623                 } else if ([transferQueue count] > 0) {
624                         queue = transferQueue;
625                         transfer = [queue objectAtIndex:0];
626                 }
627                 if (transfer != nil)
628                         [[transfer retain] autorelease];
629         }
631         *queueOut = queue;
632         return transfer;
635 - (void) removeTransfer:(NSDictionary*)transfer
636         fromQueue:(NSMutableArray*)queue
638         @synchronized (self) {
639                 [queue removeObjectIdenticalTo:transfer];
640         }
643 @end
645 @implementation TFUSBController
647 // ---------------------------------------------------------------------------
648 #pragma mark Low-level USB
649 // ---------------------------------------------------------------------------
651 - (void) closeDevice:(USBDeviceContext *)device
653     IOUSBInterfaceInterface197  **intf;
654     IOUSBDeviceInterface197     **dev;
655     IOReturn                    err;
656     
657     intf = device->intf;
658     dev = device->dev;
659     
660     err = (*intf)->USBInterfaceClose(intf);
661     if (err)
662     {
663         printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
664     }
665     err = (*intf)->Release(intf);
666     if (err)
667     {
668         printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
669     }
670     
671     err = (*dev)->USBDeviceClose(dev);
672     if (err)
673     {
674         printf("dealWithDevice: error closing device - %08x\n", err);
675         (*dev)->Release(dev);
676     }
677     err = (*dev)->Release(dev);
678     if (err)
679     {
680         printf("dealWithDevice: error releasing device - %08x\n", err);
681     }
684 - (USBDeviceContext*) initializeUSB {
685         USBDeviceContext        *device;
686         int                     err;
687         kBlockSize = 0x08;
688         device = malloc(sizeof(USBDeviceContext));
689         err = initDevice(device);
690         if (err) {
691                 printf("Could not connect to Topfield\n");
692                 return nil;
693         }
694         printf("\n\n");
695         printf("Connected to Topfield\n\n");
696         myContext = device;
697         return device;
700 // ---------------------------------------------------------------------------
701 #pragma mark Protocol message sequences
702 // ---------------------------------------------------------------------------
704 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
705                 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
706                 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {   
707 //      [progressBar setDoubleValue:0];
708 //      [progressTime setDoubleValue:0];
709         NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
710         [[[self uiElements] currentlyField] setStringValue:
711                 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
712         [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
713         [[[self uiElements] currentlyField] displayIfNeeded];
714         //construct file send request
715         NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
716         
717         //prepackage commands to send
718         NSData* fileSendCmd = [self prepareCmdHddFileSendWithDirection:USB_FileToHost
719                 fname:[self fnameForFile:nameOnToppy atPath:currentPath]
720                 offset:offset];
721         NSData* usbSuccess = [self prepareSuccess];
722         NSData* usbCancel = [self prepareCancel];
723         
724         // send commands
725         
726         if ([turboCB state]) 
727                 [self turnTurboOn:YES];
728         else
729                 [self checkUSB:myContext]; //turbo has a check itself
730         [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
731         //start timer
732         NSDate* startTime = [NSDate date];
733         startTime = [startTime addTimeInterval:(0-existingTime)];
734         // send start request and get response
735         NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
736         const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
737         NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
738         if ([data length] < 16) {
739                 NSLog(@"Incorrect Data length");
740                 return 1;
741         }
742         NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
743         if (![responseWanted isEqualToData:responseToCheck]) {
744                 NSLog(@"Unexpected response from Toppy during download");
745                 return 1;
746         }
747         
748         // clean up data and prepare path to save it
749         NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
750         NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
751         
752         // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
753         if (offset == 0)
754                 [[NSData dataWithBytes:"\0" length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
755         NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
756         [outFile seekToFileOffset:offset];
757         [outFile writeData:finalData];
758         if (looping) {  // loop for multiple data sends (ie files > 64k)
759                 int updateRate = 8; 
760                 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
761                         updateRate = 24;
762                         NSLog(@"large file detected - low GUI update rate");
763                 }
764                 int timesAround = 0;
765                 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
766                 double amountReceived = 0xfe00 + offset;
767                 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
768                 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
769                         if ([self hasPriorityTransfer]) {
770                                 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
771                                                 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
772                                                 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
773                                                 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
774                                                 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
775                                                 nil]
776                                         atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
777                                 // send reset again     just to make sure!
778                                 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
779                                 [outFile closeFile];
780                                 //now add the right modification date to the file
781                                 [[NSFileManager defaultManager]
782                                         changeFileAttributes:[NSDictionary
783                                                 dictionaryWithObject:[fileInfo objectForKey:@"date"]
784                                                 forKey:@"NSFileModificationDate"]
785                                         atPath:savePath];
786                                 [self turnTurboOn:NO];
787                                 return 0;
788                         }
789                         timesAround ++;
790                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];                             
791                         data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
792                         amountReceived += 0xfe00;
793                         if (timesAround < 8 || timesAround % updateRate == 0) {
794                                 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
795                                         withObject:[NSDictionary dictionaryWithObjectsAndKeys:
796                                                 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
797                                                 fileSize, @"size", startTime, @"startTime", nil]];
798                         }
799                         if ([data length] > 16){ //there is something to read 
800                                 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
801                                 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
802                                 [outFile writeData:finalData];
803                                 [outFile synchronizeFile];
804                         } 
805                         else break;
806                         [pool release];
807                 }
808         }
809         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again        just to make sure!
810         [outFile closeFile];
811     //now add the right modification date to the file
812         [[NSFileManager defaultManager]
813                 changeFileAttributes:[NSDictionary
814                         dictionaryWithObject:[fileInfo objectForKey:@"date"]
815                         forKey:@"NSFileModificationDate"]
816                 atPath:savePath];
817         [self turnTurboOn:NO];
818         if (looping) [[self uiElements] finishTransfer];
819         return 0;
822 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
823                 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
824                 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
825         NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
826         USBDeviceContext* dev = myContext;
827         // prepare Send command
828         NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
829         NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
830         char dir = USB_FileToDevice;
831         NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
832         short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
833         const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
834         [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
835         NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
836         [build appendData:d];
837         dir = 0x5c; // 0x5c = "/"
838         [build appendData:[NSData dataWithBytes:&dir length:1]];
839         [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
840         if ([fname length] < 95)
841                 [build increaseLengthBy:95-[fname length]];
842         dir = 0x00;
843         [build appendData:[NSData dataWithBytes:&dir length:1]];
844         UInt64 offset_bigendian = EndianU64_NtoB(offset);
845         [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
846         
847         // prepackage commands to send
848         NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
849         NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
850         NSData* fileEndCmd = [self prepareDataHddFileEnd];
852         //start timer
853         NSDate* startTime = [NSDate date];
854         startTime = [startTime addTimeInterval:(0-existingTime)];
855         //send commands
856         if ([turboCB state]) 
857                 [self turnTurboOn:YES];
858         else
859                 [self checkUSB:dev];
860         // now the proper commands
861         NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
862         data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
863         const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
864         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
865         NSData* responseToCheck = nil;
867         NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
868         [fileHandle seekToFileOffset:offset];
869         do {
870                 if ([self hasPriorityTransfer]) {
871                         //break out and create a new transfer to continue it
872                         NSLog(@"pausing upload");
873                         [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
874                                         fileToUpload,@"filename",
875                                         [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
876                                         curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
877                                         [NSNumber numberWithUnsignedLongLong:offset],@"offset",
878                                         [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
879                                         nil]
880                                 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
881                         [self turnTurboOn:NO];
882                         break;
883                 } else {
884                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
885                 offset_bigendian = EndianU64_NtoB(offset);
886                 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
887                 [fileData appendData:[fileHandle readDataOfLength:rate]];
888                 offset += rate;
889                 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
890                 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
891                 if ([data length] >= 8) 
892                         responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
893                 else responseToCheck = nil;
894                 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck]) 
895                         break;
896                 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
897                         withObject:[NSDictionary dictionaryWithObjectsAndKeys:
898                                 [NSNumber numberWithDouble:(double)offset], @"offset",
899                                 [NSNumber numberWithDouble:(double)size], @"size",
900                                 startTime, @"startTime", nil]];
901                 [pool release];
902                 }
903         } while (offset < size && [[[self uiElements] isConnected] intValue]);
904         if ([[[self uiElements] isConnected] intValue])
905                 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
906         [fileHandle closeFile]; 
907         [[self uiElements] goToPath:[[self uiElements]currentPath]];
908         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
909         [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
910         [self turnTurboOn:NO];
911         [[self uiElements] finishTransfer];
914 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
915         [self checkUSB:myContext];
916         NSString* fname = [self fnameForFile:[fileInfo objectForKey:@"name"]
917                 atPath:currentPath];
918         NSData* fileDelCmd = [self prepareCmdHddDelWithFname:fname];
919         [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
922 // ---------------------------------------------------------------------------
923 #pragma mark Queue management
924 // ---------------------------------------------------------------------------
926 - (void) addPriorityTransfer:(id)newTransfer {
927         @synchronized (self) {
928                 [priorityTransferQueue addObject:newTransfer];
929                 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
930         }
933 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
934         @synchronized (self) {
935                 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
936                 else [transferQueue addObject:newTransfer];
937                 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
938         }
941 - (void) clearQueues {
942         @synchronized (self) {
943                 [priorityTransferQueue removeAllObjects];
944                 [transferQueue removeAllObjects];
945                 [pausedQueue removeAllObjects];
946         }
949 - (BOOL) hasCurrentTransfer {
950         BOOL ret;
951         @synchronized (self) {
952                 ret = ([transferQueue count] > 0);
953         }
954         return ret;
957 - (id) currentTransferInfo {
958         id transfer = nil;
959         @synchronized (self) {
960                 if ([transferQueue count] > 0) {
961                         transfer = [transferQueue objectAtIndex:0];
962                         // Make a copy of the object so that it can be safely manipulated
963                         // in a different thread from the original.
964                         transfer = [[transfer copy] autorelease];
965                 }
966         }
967         return transfer;
970 - (id) firstPausedTransferInfo {
971         id transfer = nil;
972         @synchronized (self) {
973                 if ([pausedQueue count] > 0) {
974                         transfer = [pausedQueue objectAtIndex:0];
975                         // Make a copy of the object so that it can be safely manipulated
976                         // in a different thread from the original.
977                         transfer = [[transfer copy] autorelease];
978                 }
979         }
980         return transfer;
983 - (void) transfer:(id)sender {
984         while ([[[self uiElements] isConnected] intValue]) {
985                 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
986                 NSMutableArray* queue = nil;
987                 NSDictionary* currentTransfer = [self nextTransferAndQueue:&queue];
988                 if (currentTransfer != nil) {
989                         NSString* transferType = [currentTransfer objectForKey:@"transferType"];
990                         if ([transferType isEqualToString:@"fileList"]) {
991                                 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
992                         }
993                         else if ([transferType isEqualToString:@"turbo"]) {
994                 //              [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
995                         }
996                         else if ([transferType isEqualToString:@"rename"]) {
997                                 [self renameFile:[currentTransfer objectForKey:@"oldName"]
998                                         withName:[currentTransfer objectForKey:@"newName"]
999                                         atPath:[currentTransfer objectForKey:@"path"]];
1000                         }
1001                         else if ([transferType isEqualToString:@"newFolder"]) {
1002                                 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
1003                         }
1004                         else if ([transferType isEqualToString:@"delete"]) {
1005                                 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
1006                         }
1007                         else if ([transferType isEqualToString:@"pause"]) {
1008                                 @synchronized (self) {
1009                                         if ([transferQueue count] != 0) {
1010                                                 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
1011                                                         [NSPredicate predicateWithFormat:@"(filename = %@)",
1012                                                                 [currentTransfer objectForKey:@"filename"]]];
1013                                                 if ([toPause count] > 1) NSLog(@"multiple pauses?");
1014                                                 else {
1015                                                         [pausedQueue addObjectsFromArray:toPause];
1016                                                         [transferQueue removeObjectsInArray:toPause];
1017                                                 }
1018                                         }
1019                                 }
1020                         }
1021                         else if ([transferType isEqualToString:@"resume"]) {
1022                                 @synchronized (self) {
1023                                         if ([pausedQueue count] != 0) {
1024                                                 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1025                                                         [NSPredicate predicateWithFormat:@"(filename = %@)",
1026                                                                 [currentTransfer objectForKey:@"filename"]]];
1027                                                 if ([toResume count] > 1) NSLog(@"multiple resumes?");
1028                                                 else {
1029                                                         [transferQueue addObjectsFromArray:toResume];
1030                                                         [pausedQueue removeObjectsInArray:toResume];
1031                                                 }
1032                                         }
1033                                 }
1034                         }
1035                         else if ([transferType isEqualToString:@"download"]) {
1036                                 [self getFile:[currentTransfer objectForKey:@"filename"]
1037                                         forPath:[currentTransfer objectForKey:@"path"]
1038                                         toSaveTo:[currentTransfer objectForKey:@"savePath"]
1039                                         beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1040                                         withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1041                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1042                         }
1043                         else if ([transferType isEqualToString:@"upload"]) {
1044                                 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1045                                         ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1046                                         fromPath:[currentTransfer objectForKey:@"path"]
1047                                         withAttributes:[currentTransfer objectForKey:@"attributes"]
1048                                         atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1049                                         existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1050                         }
1051                         else {
1052                                 NSLog(@"Unrecognized transfer type: %@", transferType);
1053                         }
1054                         [self removeTransfer:currentTransfer fromQueue:queue];
1055                 }
1056                 else usleep(100);
1057                 [pool release];
1058         }
1061 // ---------------------------------------------------------------------------
1062 #pragma mark User interface
1063 // ---------------------------------------------------------------------------
1065 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
1066         progressBar = bar;
1067         progressTime = timeField;
1068         turboCB = turbo;
1071 - (void) setDH:(id)newDH tableView:(id)tv {
1072         dh = newDH;
1073         tableView = tv;
1076 // ---------------------------------------------------------------------------
1077 #pragma mark Miscellaneous
1078 // ---------------------------------------------------------------------------
1080 -(id) init {
1081         if (self = [super init]) {
1082                 debug = 0;
1083                 rate = 0xfe00;
1084                 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1085                 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1086                 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1087         }
1088         return self;
1091 - (int) getSpeed {
1092         return connectedSpeed;
1095 -(void) setDebug:(int)mode {
1096         debug = mode;
1097         NSLog(@"Debug level: %i", debug);
1100 -(void) setRate:(int) newRate {
1101         rate = newRate;
1102         NSLog(@"New rate set: %i", rate);
1105 @end