#include <unistd.h> in UIElements.m too.
[MacTF.git] / TFUSBController.m
blob8654b6a97ac8491c48feeeefadd5751565cbde88
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 @end
121 #define         TopfieldVendorID        4571
122    //1317 for HC
123    //0x138c for humax
124    //4571 for toppy
125 #define         TF5kProdID      4096
126 //42514 for HC
127 //0x02ad for humax
128 //4096 for toppy
130 // ---------------------------------------------------------------------------
131 // Functions outside TFUSBController
132 // ---------------------------------------------------------------------------
134 static void
135 hexDump(UInt8 *buf, int len)
137         int row, col, maxrows;
138         
139         maxrows = len/16;
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);
145                 }
146                 printf("\t");   
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]);
150                         }
151                         else { printf("."); }
152                 }
153                 printf("\n");
154         }
157 static int
158 doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
159         UInt8 *outBuf, UInt32 len, int type)
161         IOReturn        err;
162         UInt32          sendLen;        
163         
164                 if (debug == 1){
165                         printf(("sending:\n"));
166             hexDump(outBuf, len);
167                 }
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);
175         if (err) {
176                 printf("write err: %08x\n", err);
177                 return err;
178         }
179         return err;
182 static int
183 doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
184         UInt8 *inBuf, UInt32 dataLen, int type)
186         IOReturn        err;
187         UInt32          len;
188         
189         if (dataLen > kMaxXferSize) return 1;
190         
191         len = (dataLen/kBlockSize) * kBlockSize;
192         if (dataLen % kBlockSize)
193                 len += kBlockSize;
195                 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len,  1000, 20000);
196                 if (err) {
197                 printf("read err 2: %08x\n", err);
198                                 printf("resetting\n");
199                                 err = (*intf)->ClearPipeStallBothEnds(intf, 2); 
200                         return err;
201         }
202                 
203         if (debug == 1) {
204           printf(("receiving: \n")); 
205                         hexDump(inBuf, len);
206                 }
207         return err;
210 static int
211 dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
213     IOReturn                            err;
214     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
215     IOUSBInterfaceInterface197          **intf;
216     IOUSBDeviceInterface197             **dev;
217     SInt32                              score;
218         UInt8                           numPipes, confNum, dSpeed;
221     err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
222     if (err || !iodev)
223     {
224         printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
225         return kUproarDeviceErr;
226     }
227     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
228     (*iodev)->Release(iodev);                           // done with this
229     intf = device->intf;
230     if (err || !intf)
231     {
232         printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
233         return kUproarDeviceErr;
234     }
235     
236     dev = device->dev;
237     err = (*intf)->USBInterfaceOpen(intf);
238     if (err)
239     {
240         printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
241         return kUproarDeviceErr;
242     }
243     err = (*intf)->GetNumEndpoints(intf, &numPipes);
244     if (err)
245     {
246         printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
247         return kUproarDeviceErr;
248     }
249     
250     printf("dealWithInterface: found %d pipes\n", numPipes);
252     err = (*intf)->GetConfigurationValue(intf, &confNum);
253     err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
254         if (dSpeed == 2) {
255                 kBlockSize = 0x40;
256         } else {
257                 kBlockSize = 0x4;
258         }
259     printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
260     connectedSpeed = dSpeed;
261     return kUproarSuccess;
265 static int
266 dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
268     IOReturn                            err;
269     IOCFPlugInInterface                 **iodev;                // requires <IOKit/IOCFPlugIn.h>
270     IOUSBDeviceInterface197             **dev;
271     SInt32                              score;
272     UInt8                               numConf;
273     IOUSBConfigurationDescriptorPtr     confDesc;
274     IOUSBFindInterfaceRequest           interfaceRequest;
275     io_iterator_t                       iterator;
276     io_service_t                        usbInterfaceRef;
277     int                                 found=0;
278     
279     
280     err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
281     if (err || !iodev)
282     {
283         printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
284         return kUproarDeviceErr;
285     }
286     err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
287     (*iodev)->Release(iodev);                           
288     
289     dev = device->dev;
290     if (err || !dev)
291     {
292         printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
293         return kUproarDeviceErr;
294     }
295     err = (*dev)->USBDeviceOpen(dev);
296     if (err)
297     {
298         printf("dealWithDevice: unable to open device. ret = %08x\n", err);
299         return kUproarDeviceErr;
300     }
301     err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
302     if (err || !numConf)
303     {
304         printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
305         return kUproarDeviceErr;
306     }
307     printf("dealWithDevice: found %d configurations\n", numConf);
308     err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc);     // get the first config desc (index 0)
309     if (err)
310     {
311         printf("dealWithDevice:unable to get config descriptor for index 0\n");
312         return kUproarDeviceErr;
313     }
314     err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
315     if (err)
316     {
317         printf("dealWithDevice: unable to set the configuration\n");
318         return kUproarDeviceErr;
319     }
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
325     
326     err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
327     if (err)
328     {
329         printf("dealWithDevice: unable to create interface iterator\n");
330         return kUproarDeviceErr;
331     }
332     
333     while (usbInterfaceRef = IOIteratorNext(iterator))
334     {
335         printf("found interface: %p\n", (void*)usbInterfaceRef);
336         err = dealWithInterface(usbInterfaceRef, device);
337         IOObjectRelease(usbInterfaceRef);       // no longer need this reference
338         found = 1;
339     }
340     
341     IOObjectRelease(iterator);
342     iterator = 0;
343     if ((!found) || (err))
344         return kUproarDeviceErr;
345     else
346         return kUproarSuccess;
349 static int
350 initDevice(USBDeviceContext *device)
352     mach_port_t         masterPort = 0;
353     kern_return_t               err;
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;
360     int                         found =0;
361     
362     err = IOMasterPort(bootstrap_port, &masterPort);                    
363     
364     if (err)
365     {
366         printf("Anchortest: could not create master port, err = %08x\n", err);
367         return err;
368     }
369     matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);      // requires <IOKit/usb/IOUSBLib.h>
370     if (!matchingDictionary)
371     {
372         printf("Anchortest: could not create matching dictionary\n");
373         return -1;
374     }
375     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
376     if (!numberRef)
377     {
378         printf("Anchortest: could not create CFNumberRef for vendor\n");
379         return -1;
380     }
381     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
382     CFRelease(numberRef);
383     numberRef = 0;
384     numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
385     if (!numberRef)
386     {
387         printf("Anchortest: could not create CFNumberRef for product\n");
388         return -1;
389     }
390     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
391     CFRelease(numberRef);
392     numberRef = 0;
393     
394     err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
395     matchingDictionary = 0;                     // this was consumed by the above call
396     
397     while (usbDeviceRef = IOIteratorNext(iterator))
398     {
399         printf("Found device %p\n", (void*)usbDeviceRef);
400         err = dealWithDevice(usbDeviceRef, device);
401         IOObjectRelease(usbDeviceRef);                  // no longer need this reference
402         found = 1;
403     }
404     
405     
406     IOObjectRelease(iterator);
407     iterator = 0;
408     mach_port_deallocate(mach_task_self(), masterPort);
409     if ((!found) || (err))
410         return kUproarDeviceErr;
411     else
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{
424         IOReturn        err;
425         int cmdLength = [fullyPackagedCommand length];
426         unsigned char outBuffer[cmdLength];
427         memset(outBuffer, 0, cmdLength);
428 //      NSLog(@"send: %@", [fullyPackagedCommand description]);
429         [fullyPackagedCommand getBytes:outBuffer];
430         
431         err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
432         if (err)
433                 NSLog(@"sendError: %08x\n");
434         
435         if (! getResponse) return nil;
436         
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);
441         if (err)
442                 NSLog(@"inError: %08x\n", err);
443         
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)
457                 return nil;
459         NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
460         if (hddListCmd == nil)
461                 return 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]) {
474                 case USB_Fail:
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];
479                         break;
480                 case USB_DataHddDir:
481                         contiguousErrors = 0;
482                         size_t i;
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];
491                                 }
492                         }
493                         response = [self sendCommand:[self prepareSuccess]
494                                 toDevice:myContext expectResponse:YES careForReturn:YES];
495                         break;
496                 case USB_DataHddDirEnd:
497                         contiguousErrors = 0;
498                         response = nil;
499                         break;
500                 default:
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];
505                         break;
506                 }
507         }
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
512         return nil;
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);
544         int i = 0;
545         NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
546         while ((data == nil || [data length] < 8) && i < 8) {
547                 NSLog (@"incorrect response");
548                 i++;
549                 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
550         }
551         if (!i<=8) {
552                 // tell someone that no longer connected here??
553                 return;
554                 }
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   
558         }
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];
578         [pool release];
581 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
582         char hp = 20;
583         char mp = 20;
584         char sp = 20; //padding
585         int hours = (totalSeconds / 3600);  // returns number of whole hours fitted in totalSecs
586         if (hours < 10)
587                 hp = 48;
588         int minutes = ((totalSeconds / 60) - hours*60);  // Whole minutes
589         if (minutes < 10)
590                 mp = 48;
591         int seconds = ((long) totalSeconds % 60);       // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
592         if (seconds < 10)
593                 sp = 48;
594         return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
597 @end
599 // ---------------------------------------------------------------------------
600 // Low-level USB
601 // ---------------------------------------------------------------------------
603 @implementation TFUSBController
605 - (void) closeDevice:(USBDeviceContext *)device
607     IOUSBInterfaceInterface197  **intf;
608     IOUSBDeviceInterface197     **dev;
609     IOReturn                    err;
610     
611     intf = device->intf;
612     dev = device->dev;
613     
614     err = (*intf)->USBInterfaceClose(intf);
615     if (err)
616     {
617         printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
618     }
619     err = (*intf)->Release(intf);
620     if (err)
621     {
622         printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
623     }
624     
625     err = (*dev)->USBDeviceClose(dev);
626     if (err)
627     {
628         printf("dealWithDevice: error closing device - %08x\n", err);
629         (*dev)->Release(dev);
630     }
631     err = (*dev)->Release(dev);
632     if (err)
633     {
634         printf("dealWithDevice: error releasing device - %08x\n", err);
635     }
638 - (USBDeviceContext*) initializeUSB {
639         USBDeviceContext        *device;
640         int                     err;
641         kBlockSize = 0x08;
642         device = malloc(sizeof(USBDeviceContext));
643         err = initDevice(device);
644         if (err) {
645                 printf("Could not connect to Topfield\n");
646                 return nil;
647         }
648         printf("\n\n");
649         printf("Connected to Topfield\n\n");
650         myContext = device;
651         return device;
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"];
670         
671         //prepackage commands to send
672         NSData* fileSendCmd = [self prepareCmdHddFileSendWithDirection:USB_FileToHost
673                 fname:[self fnameForFile:nameOnToppy atPath:currentPath]
674                 offset:offset];
675         NSData* usbSuccess = [self prepareSuccess];
676         NSData* usbCancel = [self prepareCancel];
677         
678         // send commands
679         
680         if ([turboCB state]) 
681                 [self turnTurboOn:YES];
682         else
683                 [self checkUSB:myContext]; //turbo has a check itself
684         [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
685         //start timer
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");
694                 return 1;
695         }
696         NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
697         if (![responseWanted isEqualToData:responseToCheck]) {
698                 NSLog(@"Unexpected response from Toppy during download");
699                 return 1;
700         }
701         
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}];
705         
706         // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
707         if (offset == 0)
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)
713                 int updateRate = 8; 
714                 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
715                         updateRate = 24;
716                         NSLog(@"large file detected - low GUI update rate");
717                 }
718                 int timesAround = 0;
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",
729                                                 nil]
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];
733                                 [outFile closeFile];
734                                 //now add the right modification date to the file
735                                 [[NSFileManager defaultManager]
736                                         changeFileAttributes:[NSDictionary
737                                                 dictionaryWithObject:[fileInfo objectForKey:@"date"]
738                                                 forKey:@"NSFileModificationDate"]
739                                         atPath:savePath];
740                                 [self turnTurboOn:NO];
741                                 return 0;
742                         }
743                         timesAround ++;
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]];
752                         }
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];
758                         } 
759                         else break;
760                         [pool release];
761                 }
762         }
763         [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again        just to make sure!
764         [outFile closeFile];
765     //now add the right modification date to the file
766         [[NSFileManager defaultManager]
767                 changeFileAttributes:[NSDictionary
768                         dictionaryWithObject:[fileInfo objectForKey:@"date"]
769                         forKey:@"NSFileModificationDate"]
770                 atPath:savePath];
771         [self turnTurboOn:NO];
772         if (looping) [[self uiElements] finishTransfer];
773         return 0;
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]];
796         dir = 0x00;
797         [build appendData:[NSData dataWithBytes:&dir length:1]];
798         UInt64 offset_bigendian = EndianU64_NtoB(offset);
799         [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
800         
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];
806         //start timer
807         NSDate* startTime = [NSDate date];
808         startTime = [startTime addTimeInterval:(0-existingTime)];
809         //send commands
810         if ([turboCB state]) 
811                 [self turnTurboOn:YES];
812         else
813                 [self checkUSB:dev];
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];
823         do {
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",
833                                         nil]
834                                 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
835                         [self turnTurboOn:NO];
836                         break;
837                 } else {
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]];
842                 offset += 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]) 
849                         break;
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]];
855                 [pool release];
856                 }
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"]
871                 atPath:currentPath];
872         NSData* fileDelCmd = [self prepareCmdHddDelWithFname:fname];
873         [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
876 // ---------------------------------------------------------------------------
877 // Queue management
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];
897 - (id) pausedQueue {
898         return pausedQueue;
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]);
910                         }
911                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
912                 //              [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
913                                 [priorityTransferQueue removeObjectAtIndex:0];
914                         }
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];
920                         }
921                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
922                                 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
923                                 [priorityTransferQueue removeObjectAtIndex:0];
924                         }
925                         else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
926                                 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
927                                 [priorityTransferQueue removeObjectAtIndex:0];
928                         }
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?");
935                                         else {
936                                                 [pausedQueue addObjectsFromArray:toPause];
937                                                 [transferQueue removeObjectsInArray:toPause];
938                                         }
939                                 }
940                                 [priorityTransferQueue removeObjectAtIndex:0];
941                         }
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?");
948                                         else {
949                                                 [transferQueue addObjectsFromArray:toResume];
950                                                 [pausedQueue removeObjectsInArray:toResume];
951                                         }
952                                 }
953                                 [priorityTransferQueue removeObjectAtIndex:0];
954                         }
955                 }
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]);
967                         }
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]);
978                         }
979                 }
980                 else usleep(100);
981                 [pool release];
982         }
985 // ---------------------------------------------------------------------------
986 // User interface
987 // ---------------------------------------------------------------------------
989 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
990         progressBar = bar;
991         progressTime = timeField;
992         turboCB = turbo;
995 - (void) setDH:(id)newDH tableView:(id)tv {
996         dh = newDH;
997         tableView = tv;
1000 // ---------------------------------------------------------------------------
1001 // Miscellaneous
1002 // ---------------------------------------------------------------------------
1004 -(id) init {
1005         if (self = [super init]) {
1006                 debug = 0;
1007                 rate = 0xfe00;
1008                 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1009                 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1010                 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1011         }
1012         return self;
1015 - (int) getSpeed {
1016         return connectedSpeed;
1019 -(void) setDebug:(int)mode {
1020         debug = mode;
1021         NSLog(@"Debug level: %i", debug);
1024 -(void) setRate:(int) newRate {
1025         rate = newRate;
1026         NSLog(@"New rate set: %i", rate);
1029 - (BOOL) hasCurrentTransfer {
1030         return [transferQueue count] > 0;
1033 - (id) currentTransferInfo {
1034         if ([transferQueue count] == 0)
1035                 return nil;
1036         else {
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];
1040         }
1043 @end