5 // Created by Nathan Oates on Sun Aug 01 2004.
6 // Copyright (c) 2004-7 Nathan Oates nathan@noates.com All rights reserved.
9 /* This file was modified by Kalle Olavi Niemitalo on 2007-10-18. */
11 // includes code based on uproar, license noted below:
13 // Copyright (c) 2001 Kasima Tharnpipitchai <me@kasima.org>
14 #import "TFUSBController.h"
15 #import "UIElements.h"
17 @interface TFUSBController (PrivateMethods)
18 - (UInt32) checkCommunicationBlock:(NSData*)block;
19 - (UInt32) cmdFromCommunicationBlock:(NSData*)block;
20 - (NSData*) prepareFailWithECode:(UInt32)eCode;
21 - (NSData*) prepareCmdHddDirWithPath:(NSString*)path;
22 - (UIElements*) uiElements;
25 @implementation TFUSBController
30 * This source code is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Public License as published
32 * by the Free Software Foundation; either version 2 of the License,
33 * or (at your option) any later version.
35 * This source code is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
38 * Please refer to the GNU Public License for more details.
40 * You should have received a copy of the GNU Public License along with
41 * this source code; if not, write to:
42 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
47 /* Much of the code that initializes and closes the device is from Apple
48 * and is released under the license below.
52 * © Copyright 2001 Apple Computer, Inc. All rights reserved.
54 * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) in
55 * consideration of your agreement to the following terms, and your use, installation,
56 * modification or redistribution of this Apple software constitutes acceptance of these
57 * terms. If you do not agree with these terms, please do not use, install, modify or
58 * redistribute this Apple software.
60 * In consideration of your agreement to abide by the following terms, and subject to these
61 * terms, Apple grants you a personal, non exclusive license, under Apple’s copyrights in this
62 * original Apple software (the “Apple Software”), to use, reproduce, modify and redistribute
63 * the Apple Software, with or without modifications, in source and/or binary forms; provided
64 * that if you redistribute the Apple Software in its entirety and without modifications, you
65 * must retain this notice and the following text and disclaimers in all such redistributions
66 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple
67 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software
68 * without specific prior written permission from Apple. Except as expressly stated in this
69 * notice, no other rights or licenses, express or implied, are granted by Apple herein,
70 * including but not limited to any patent rights that may be infringed by your derivative
71 * works or by other works in which the Apple Software may be incorporated.
73 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
74 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
75 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
76 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
78 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
79 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
80 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
81 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
82 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
83 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87 #define TopfieldVendorID 4571
91 #define TF5kProdID 4096
96 // ---------------------------------------------------------------------------
98 // ---------------------------------------------------------------------------
100 int doSend(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
101 UInt8 *outBuf, UInt32 len, int type)
107 printf(("sending:\n"));
108 hexDump(outBuf, len);
110 sendLen = ((len/kBlockSize))*kBlockSize;
111 if (len % kBlockSize)
112 sendLen += kBlockSize;
113 if ((sendLen % 0x200) == 0)
114 sendLen += kBlockSize;
116 err = (*intf)->WritePipeTO(intf, 1, outBuf, sendLen, 1000, 20000);
118 printf("write err: %08x\n", err);
124 int doRecv(IOUSBDeviceInterface197 **dev, IOUSBInterfaceInterface197 **intf,
125 UInt8 *inBuf, UInt32 dataLen, int type) {
129 if (dataLen > kMaxXferSize) return 1;
131 len = (dataLen/kBlockSize) * kBlockSize;
132 if (dataLen % kBlockSize)
135 err = (*intf)->ReadPipeTO(intf, 2, (void *)inBuf, &len, 1000, 20000);
137 printf("read err 2: %08x\n", err);
138 printf("resetting\n");
139 err = (*intf)->ClearPipeStallBothEnds(intf, 2);
144 printf(("receiving: \n"));
150 int dealWithInterface(io_service_t usbInterfaceRef, USBDeviceContext *device)
153 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
154 IOUSBInterfaceInterface197 **intf;
155 IOUSBDeviceInterface197 **dev;
157 UInt8 numPipes, confNum, dSpeed;
160 err = IOCreatePlugInInterfaceForService(usbInterfaceRef, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
163 printf("dealWithInterface: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
164 return kUproarDeviceErr;
166 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)&(device->intf));
167 (*iodev)->Release(iodev); // done with this
171 printf("dealWithInterface: unable to create a device interface. ret = %08x, intf = %p\n", err, intf);
172 return kUproarDeviceErr;
176 err = (*intf)->USBInterfaceOpen(intf);
179 printf("dealWithInterface: unable to open interface. ret = %08x\n", err);
180 return kUproarDeviceErr;
182 err = (*intf)->GetNumEndpoints(intf, &numPipes);
185 printf("dealWithInterface: unable to get number of endpoints. ret = %08x\n", err);
186 return kUproarDeviceErr;
189 printf("dealWithInterface: found %d pipes\n", numPipes);
191 err = (*intf)->GetConfigurationValue(intf, &confNum);
192 err = (*dev)->GetDeviceSpeed(dev, &dSpeed);
198 printf("confnum: %08x, dspeed: %08x, blockS:%i\n", confNum, dSpeed, kBlockSize);
199 connectedSpeed = dSpeed;
200 return kUproarSuccess;
204 int dealWithDevice(io_service_t usbDeviceRef, USBDeviceContext *device)
207 IOCFPlugInInterface **iodev; // requires <IOKit/IOCFPlugIn.h>
208 IOUSBDeviceInterface197 **dev;
211 IOUSBConfigurationDescriptorPtr confDesc;
212 IOUSBFindInterfaceRequest interfaceRequest;
213 io_iterator_t iterator;
214 io_service_t usbInterfaceRef;
218 err = IOCreatePlugInInterfaceForService(usbDeviceRef, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
221 printf("dealWithDevice: unable to create plugin. ret = %08x, iodev = %p\n", err, iodev);
222 return kUproarDeviceErr;
224 err = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&(device->dev));
225 (*iodev)->Release(iodev);
230 printf("dealWithDevice: unable to create a device interface. ret = %08x, dev = %p\n", err, dev);
231 return kUproarDeviceErr;
233 err = (*dev)->USBDeviceOpen(dev);
236 printf("dealWithDevice: unable to open device. ret = %08x\n", err);
237 return kUproarDeviceErr;
239 err = (*dev)->GetNumberOfConfigurations(dev, &numConf);
242 printf("dealWithDevice: unable to obtain the number of configurations. ret = %08x\n", err);
243 return kUproarDeviceErr;
245 printf("dealWithDevice: found %d configurations\n", numConf);
246 err = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &confDesc); // get the first config desc (index 0)
249 printf("dealWithDevice:unable to get config descriptor for index 0\n");
250 return kUproarDeviceErr;
252 err = (*dev)->SetConfiguration(dev, confDesc->bConfigurationValue);
255 printf("dealWithDevice: unable to set the configuration\n");
256 return kUproarDeviceErr;
259 interfaceRequest.bInterfaceClass = kIOUSBFindInterfaceDontCare; // requested class
260 interfaceRequest.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; // requested subclass
261 interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol
262 interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting
264 err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator);
267 printf("dealWithDevice: unable to create interface iterator\n");
268 return kUproarDeviceErr;
271 while (usbInterfaceRef = IOIteratorNext(iterator))
273 printf("found interface: %p\n", (void*)usbInterfaceRef);
274 err = dealWithInterface(usbInterfaceRef, device);
275 IOObjectRelease(usbInterfaceRef); // no longer need this reference
279 IOObjectRelease(iterator);
281 if ((!found) || (err))
282 return kUproarDeviceErr;
284 return kUproarSuccess;
288 int initDevice(USBDeviceContext *device){
289 mach_port_t masterPort = 0;
291 CFMutableDictionaryRef matchingDictionary = 0; // requires <IOKit/IOKitLib.h>
292 short idVendor = TopfieldVendorID;
293 short idProduct = TF5kProdID;
294 CFNumberRef numberRef;
295 io_iterator_t iterator = 0;
296 io_service_t usbDeviceRef;
299 err = IOMasterPort(bootstrap_port, &masterPort);
303 printf("Anchortest: could not create master port, err = %08x\n", err);
306 matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); // requires <IOKit/usb/IOUSBLib.h>
307 if (!matchingDictionary)
309 printf("Anchortest: could not create matching dictionary\n");
312 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idVendor);
315 printf("Anchortest: could not create CFNumberRef for vendor\n");
318 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorName), numberRef);
319 CFRelease(numberRef);
321 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &idProduct);
324 printf("Anchortest: could not create CFNumberRef for product\n");
327 CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductName), numberRef);
328 CFRelease(numberRef);
331 err = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
332 matchingDictionary = 0; // this was consumed by the above call
334 while (usbDeviceRef = IOIteratorNext(iterator))
336 printf("Found device %p\n", (void*)usbDeviceRef);
337 err = dealWithDevice(usbDeviceRef, device);
338 IOObjectRelease(usbDeviceRef); // no longer need this reference
343 IOObjectRelease(iterator);
345 mach_port_deallocate(mach_task_self(), masterPort);
346 if ((!found) || (err))
347 return kUproarDeviceErr;
349 return kUproarSuccess;
353 - (void) closeDevice:(USBDeviceContext *) device
355 IOUSBInterfaceInterface197 **intf;
356 IOUSBDeviceInterface197 **dev;
362 err = (*intf)->USBInterfaceClose(intf);
365 printf("dealWithInterface: unable to close interface. ret = %08x\n", err);
367 err = (*intf)->Release(intf);
370 printf("dealWithInterface: unable to release interface. ret = %08x\n", err);
373 err = (*dev)->USBDeviceClose(dev);
376 printf("dealWithDevice: error closing device - %08x\n", err);
377 (*dev)->Release(dev);
379 err = (*dev)->Release(dev);
382 printf("dealWithDevice: error releasing device - %08x\n", err);
386 - (USBDeviceContext*) initializeUSB {
387 USBDeviceContext *device;
390 device = malloc(sizeof(USBDeviceContext));
391 err = initDevice(device);
393 printf("Could not connect to Topfield\n");
397 printf("Connected to Topfield\n\n");
402 - (NSData*) sendCommand:(NSData*) fullyPackagedCommand
403 toDevice:(USBDeviceContext*)device expectResponse:(BOOL) getResponse
404 careForReturn:(BOOL) careFactor{
406 int cmdLength = [fullyPackagedCommand length];
407 unsigned char outBuffer[cmdLength];
408 memset(outBuffer, 0, cmdLength);
409 // NSLog(@"send: %@", [fullyPackagedCommand description]);
410 [fullyPackagedCommand getBytes:outBuffer];
412 err = doSend(device->dev, device->intf, outBuffer, cmdLength, 2);
414 NSLog(@"sendError: %08x\n");
416 if (! getResponse) return nil;
418 int inLen = 0xFFFF; // i think this is biggest needed?
419 unsigned char inBuf[inLen];
420 memset(inBuf, 0, inLen);
421 err = doRecv(device->dev, device->intf, inBuf, inLen, 2);
423 NSLog(@"inError: %08x\n", err);
425 if (! careFactor) return nil;
426 NSMutableData* data = [NSMutableData dataWithBytes:inBuf length:inLen];
427 data = [self swap:data];
428 inLen = inBuf[1]*256 + inBuf[0]; // work out how long the response really is. NB data is flipped, but inBuf still isn't
429 return [data subdataWithRange:(NSRange) {0,inLen}];
432 // ---------------------------------------------------------------------------
433 // Framing of communication blocks
434 // ---------------------------------------------------------------------------
436 -(UInt16) findCRC:(const UInt8*)p length:(size_t) n
438 UInt16 m_crc16 = 0x0000;
439 static const UInt16 crc16Tbl[256] = {
440 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
441 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
442 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
443 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
444 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
445 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
446 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
447 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
448 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
449 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
450 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
451 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
452 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
453 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
454 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
455 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
456 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
457 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
458 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
459 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
460 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
461 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
462 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
463 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
464 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
465 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
466 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
467 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
468 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
469 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
470 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
471 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
474 for(i = 0; i < n; i++)
475 m_crc16 = (m_crc16 >> 8) ^ crc16Tbl[*p++ ^ (m_crc16 & 0xFF)];
479 -(NSMutableData*) swap: (NSData*) inData {
480 int len = [inData length];
483 uneven = 1; // prepare space for padding
487 if ((array = calloc((len + uneven), sizeof(char))) == NULL) {
488 NSLog(@"ERROR: Malloc failed");
489 return [NSMutableData dataWithData:inData];
491 if ((outarray = calloc((len + uneven), sizeof(char))) == NULL) {
492 NSLog(@"ERROR: Malloc failed");
493 return [NSMutableData dataWithData:inData];
495 [inData getBytes:array length:len];
496 swab(array, outarray, len+uneven);
497 NSMutableData* ret = [NSMutableData dataWithCapacity:len+uneven];
498 [ret setData:[NSData dataWithBytes:outarray length:len+uneven]];
504 - (NSData*) prepareCommand:(UInt32)cmd withData:(NSData*) inData {
505 NSMutableData* block = [NSMutableData dataWithLength:8];
507 [block appendData:inData];
508 size_t blockLength = [block length];
509 if (blockLength > 0xFFFF)
510 return nil; // wouldn't fit in UInt16
512 UInt32 cmd_bigendian = EndianU32_NtoB(cmd);
513 [block replaceBytesInRange:(NSRange){4,4} withBytes:&cmd_bigendian];
515 UInt16 crc = [self findCRC:(const UInt8*)[block bytes]+4 length:blockLength-4];
516 UInt16 crc_bigendian = EndianU16_NtoB(crc);
517 [block replaceBytesInRange:(NSRange){2,2} withBytes:&crc_bigendian];
519 UInt16 blockLength_bigendian = EndianU16_NtoB(blockLength);
520 [block replaceBytesInRange:(NSRange){0,2} withBytes:&blockLength_bigendian];
522 return [self swap:block];
525 // Check the size and CRC of a received communication block.
526 // Return an ECODE to be sent back, or USB_OK.
527 - (UInt32) checkCommunicationBlock:(NSData*)block {
528 if (block == nil) return USB_Err_RunFail;
529 size_t length = [block length];
530 if (length < 8) return USB_Err_Size;
531 UInt16 crc16_bigendian;
532 [block getBytes:&crc16_bigendian range:(NSRange){2,2}];
533 UInt16 crc = EndianU16_BtoN(crc16_bigendian);
534 UInt16 calcCrc = [self findCRC:(const UInt8*)[block bytes]+4 length:length-4];
535 if (crc != calcCrc) return USB_Err_Crc;
539 - (UInt32) cmdFromCommunicationBlock:(NSData*)block {
540 UInt32 cmd_bigendian;
541 [block getBytes:&cmd_bigendian range:(NSRange){4,4}];
542 return EndianU32_BtoN(cmd_bigendian);
545 // ---------------------------------------------------------------------------
546 // Preparing communication blocks for various commands
547 // ---------------------------------------------------------------------------
549 - (NSData*) prepareFailWithECode:(UInt32)eCode {
550 const UInt32 eCode_bigendian = EndianU32_NtoB(eCode);
551 NSData* data = [NSData dataWithBytes:&eCode_bigendian length:4];
552 if (data == nil) // out of memory presumably
554 return [self prepareCommand:USB_Fail withData:data];
557 // Prepare a communication block that asks the Toppy to list the files
558 // in a directory. Return the communication block, or nil on error.
559 - (NSData*) prepareCmdHddDirWithPath:(NSString*)path {
560 NSData *pathData = [path dataUsingEncoding:NSISOLatin1StringEncoding];
561 if (pathData == nil) // path contains characters unsupported by encoding
563 NSMutableData *cmdData = [NSMutableData dataWithData:pathData];
564 [cmdData increaseLengthBy:1]; // not sure if need to pad here?
565 return [self prepareCommand:USB_CmdHddDir withData:cmdData];
568 // ---------------------------------------------------------------------------
569 // Protocol message sequences
570 // ---------------------------------------------------------------------------
572 - (id) getFileListForPath:(NSString*) path {
573 if (myContext == nil)
576 NSData* hddListCmd = [self prepareCmdHddDirWithPath:path];
577 if (hddListCmd == nil)
580 [self checkUSB:myContext]; // sends cancel and waits for response
581 int contiguousErrors = 0;
582 [[dh fileList] removeAllObjects];
583 NSData* response = [self sendCommand:hddListCmd toDevice:myContext
584 expectResponse:YES careForReturn:YES];
585 while (response != nil && contiguousErrors++ < 5) {
586 const UInt32 eCode = [self checkCommunicationBlock:response];
587 if (eCode != USB_OK) {
588 response = [self sendCommand:[self prepareFailWithECode:eCode]
589 toDevice:myContext expectResponse:YES careForReturn:YES];
590 } else switch ([self cmdFromCommunicationBlock:response]) {
592 // [statusField setStringValue:NSLocalizedString(@"LAST_ERROR", @"Error on last command.")];
593 [[dh fileList] removeAllObjects];
594 response = [self sendCommand:hddListCmd toDevice:myContext
595 expectResponse:YES careForReturn:YES];
598 contiguousErrors = 0;
600 // Swapping sometimes adds a byte of padding. The following uses
601 // only complete 144-byte structures and so ignores such padding.
602 for (i=0; 8+(i+1)*114 <= [response length]; i++) {
603 NSData* typeFile = [response subdataWithRange:(NSRange) {8+i*114,114}];
604 NSMutableDictionary* tfFile = [dh getTFFileFromSwappedHexData:typeFile];
605 if (![[tfFile objectForKey:@"name"] isEqualToString:@".."]) {
606 [dh convertRawDataToUseful:tfFile];
607 [[dh fileList] addObject:tfFile];
610 response = [self sendCommand:[self prepareCommand:USB_Success withData:nil]
611 toDevice:myContext expectResponse:YES careForReturn:YES];
613 case USB_DataHddDirEnd:
614 contiguousErrors = 0;
618 [self checkUSB:myContext]; // cancel whatever is going on
619 [[dh fileList] removeAllObjects];
620 response = [self sendCommand:hddListCmd toDevice:myContext
621 expectResponse:YES careForReturn:YES];
626 [tableView reloadData];
627 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
628 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
632 - (int) getFile:(NSDictionary*)fileInfo forPath:(NSString*)currentPath
633 toSaveTo:(NSString*)savePath beginAtOffset:(unsigned long long) offset
634 withLooping:(BOOL)looping existingTime:(NSTimeInterval)existingTime {
635 // [progressBar setDoubleValue:0];
636 // [progressTime setDoubleValue:0];
637 NSString* nameOnToppy = [fileInfo objectForKey:@"name"];
638 [[[self uiElements] currentlyField] setStringValue:
639 [NSLocalizedString(@"DOWNLOADING", @"Downloading: ") stringByAppendingString:nameOnToppy]];
640 [[[self uiElements] connectLight] setImage:[NSImage imageNamed:@"blink.tiff"]];
641 [[[self uiElements] currentlyField] displayIfNeeded];
642 //construct file send request
643 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
644 NSNumber* fileSize = [fileInfo objectForKey:@"fileSize"];
645 char dir = USB_FileToHost;
646 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
647 short int nsize = [fname length]+[currentPath length]+2;// one for slash, one for padding 0x00
648 const SInt16 nsize_bigendian = EndianS16_NtoB(nsize);
649 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
650 NSData* d = [currentPath dataUsingEncoding:NSISOLatin1StringEncoding];
651 [build appendData:d];
652 dir = 0x5c; // 0x5c = "/"
653 [build appendData:[NSData dataWithBytes:&dir length:1]];
654 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
656 [build appendData:[NSData dataWithBytes:&dir length:1]];
657 //add 8 byte offset here
658 const UInt64 offset_bigendian = EndianU64_NtoB(offset);
659 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
661 //prepackage commands to send
662 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
663 NSData* usbSuccess = [self prepareCommand:USB_Success withData:nil];
664 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
669 [self turnTurboOn:YES];
671 [self checkUSB:myContext]; //turbo has a check itself
672 [self sendCommand:fileSendCmd toDevice:myContext expectResponse:YES careForReturn:NO];
674 NSDate* startTime = [NSDate date];
675 startTime = [startTime addTimeInterval:(0-existingTime)];
676 // send start request and get response
677 NSData *data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
678 const UInt32 rW_bigendian = EndianU32_NtoB(USB_DataHddFileData);
679 NSData *responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
680 if ([data length] < 16) {
681 NSLog(@"Incorrect Data length");
684 NSData *responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
685 if (![responseWanted isEqualToData:responseToCheck]) {
686 NSLog(@"Unexpected response from Toppy during download");
690 // clean up data and prepare path to save it
691 NSData* header = [data subdataWithRange:(NSRange) {4,4}]; //cmd data
692 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
694 // first initialize a file of the right name, as NSFileHandle requires an existing file to work on
696 [[NSData dataWithBytes:&dir length:1] writeToFile:savePath atomically:NO]; // write 0x00 to initialize (overwritten later)
697 NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:savePath];
698 [outFile seekToFileOffset:offset];
699 [outFile writeData:finalData];
700 if (looping) { // loop for multiple data sends (ie files > 64k)
702 if ([fileSize isGreaterThan:[NSNumber numberWithDouble:1048576]]) {
704 NSLog(@"large file detected - low GUI update rate");
707 const UInt32 test_bigendian = EndianU32_NtoB(USB_DataHddFileData);
708 double amountReceived = 0xfe00 + offset;
709 NSData* testData = [NSData dataWithBytes:&test_bigendian length:4];
710 while ([header isEqualToData:testData] && [[[self uiElements] isConnected] intValue]) {
711 if ([priorityTransferQueue count] != 0) {
712 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
713 fileInfo,@"filename", currentPath,@"path", savePath,@"savePath",
714 [NSNumber numberWithUnsignedLongLong:amountReceived],@"offset",
715 @"download",@"transferType", [NSNumber numberWithBool:YES],@"looping",
716 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
718 atIndex:1]; // nb adding to index 1 as the current transfer lies at 0 and will be deleted soon
719 // send reset again just to make sure!
720 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
722 //now add the right modification date to the file
723 [[NSFileManager defaultManager]
724 changeFileAttributes:[NSDictionary
725 dictionaryWithObject:[fileInfo objectForKey:@"date"]
726 forKey:@"NSFileModificationDate"]
728 [self turnTurboOn:NO];
732 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
733 data = [self sendCommand:usbSuccess toDevice:myContext expectResponse:YES careForReturn:YES];
734 amountReceived += 0xfe00;
735 if (timesAround < 8 || timesAround % updateRate == 0) {
736 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
737 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
738 [NSNumber numberWithDouble:(double)amountReceived], @"offset",
739 fileSize, @"size", startTime, @"startTime", nil]];
741 if ([data length] > 16){ //there is something to read
742 header = [[data subdataWithRange:(NSRange) {4,4}] retain]; //cmd data
743 NSData* finalData = [data subdataWithRange:(NSRange) {16,[data length]-16}];
744 [outFile writeData:finalData];
745 [outFile synchronizeFile];
751 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO]; // send reset again just to make sure!
753 //now add the right modification date to the file
754 [[NSFileManager defaultManager]
755 changeFileAttributes:[NSDictionary
756 dictionaryWithObject:[fileInfo objectForKey:@"date"]
757 forKey:@"NSFileModificationDate"]
759 [self turnTurboOn:NO];
760 if (looping) [[self uiElements] finishTransfer];
764 - (void) uploadFile:(NSString*) fileToUpload ofSize:(long long) size
765 fromPath:(NSString*)curPath withAttributes:(NSData*) typeFile
766 atOffset:(unsigned long long)offset existingTime:(NSTimeInterval)existingTime {
767 NSLog(@"upload: %@,%@,%qu", fileToUpload, curPath, offset);
768 USBDeviceContext* dev = myContext;
769 // prepare Send command
770 NSMutableArray* array = [NSMutableArray arrayWithArray:[fileToUpload componentsSeparatedByString:@"/"]];
771 NSMutableString* fname = [NSMutableString stringWithString:[array lastObject]];
772 char dir = USB_FileToDevice;
773 NSMutableData* build = [NSMutableData dataWithBytes:&dir length:1];
774 short int nsize = [fname length]+[curPath length]+2;// one for slash, one for padding 0x00
775 const UInt16 nsize_bigendian = EndianU16_NtoB(nsize);
776 [build appendData:[NSData dataWithBytes:&nsize_bigendian length:2]];
777 NSData* d = [curPath dataUsingEncoding:NSISOLatin1StringEncoding];
778 [build appendData:d];
779 dir = 0x5c; // 0x5c = "/"
780 [build appendData:[NSData dataWithBytes:&dir length:1]];
781 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];// may need to pad to 95...
782 if ([fname length] < 95)
783 [build increaseLengthBy:95-[fname length]];
785 [build appendData:[NSData dataWithBytes:&dir length:1]];
786 UInt64 offset_bigendian = EndianU64_NtoB(offset);
787 [build appendData:[NSData dataWithBytes:&offset_bigendian length:8]];
789 // prepackage commands to send
790 NSData* fileSendCmd = [self prepareCommand:USB_CmdHddFileSend withData:build];
791 NSData* fileStartCmd = [self prepareCommand:USB_DataHddFileStart withData:typeFile];
792 NSData* fileEndCmd = [self prepareCommand:USB_DataHddFileEnd withData:nil];
795 NSDate* startTime = [NSDate date];
796 startTime = [startTime addTimeInterval:(0-existingTime)];
799 [self turnTurboOn:YES];
802 // now the proper commands
803 NSData *data = [self sendCommand:fileSendCmd toDevice:dev expectResponse:YES careForReturn:YES];
804 data = [self sendCommand:fileStartCmd toDevice:dev expectResponse:YES careForReturn:YES];
805 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
806 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
807 NSData* responseToCheck = nil;
809 NSFileHandle* fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileToUpload];
810 [fileHandle seekToFileOffset:offset];
812 if ([priorityTransferQueue count] != 0) {
813 //break out and create a new transfer to continue it
814 NSLog(@"pausing upload");
815 [self addTransfer:[NSDictionary dictionaryWithObjectsAndKeys:
816 fileToUpload,@"filename",
817 [NSNumber numberWithUnsignedLongLong:size],@"fileSize",
818 curPath,@"path",typeFile,@"attributes",@"upload",@"transferType",
819 [NSNumber numberWithUnsignedLongLong:offset],@"offset",
820 [NSNumber numberWithInt:[[NSDate date] timeIntervalSinceDate:startTime]],@"existingTime",
822 atIndex:1]; //nb use index 1 as current transfer is at 0 and will be deleted
823 [self turnTurboOn:NO];
826 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
827 offset_bigendian = EndianU64_NtoB(offset);
828 NSMutableData* fileData = [NSMutableData dataWithBytes:&offset_bigendian length:8];
829 [fileData appendData:[fileHandle readDataOfLength:rate]];
831 NSData* fileDataCmd = [self prepareCommand:USB_DataHddFileData withData:fileData];
832 data = [self sendCommand:fileDataCmd toDevice:dev expectResponse:YES careForReturn:YES];
833 if ([data length] >= 8)
834 responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
835 else responseToCheck = nil;
836 if (responseToCheck == nil || ![responseWanted isEqualToData:responseToCheck])
838 [NSThread detachNewThreadSelector:@selector(updateProgress:) toTarget:self
839 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
840 [NSNumber numberWithDouble:(double)offset], @"offset",
841 [NSNumber numberWithDouble:(double)size], @"size",
842 startTime, @"startTime", nil]];
845 } while (offset < size && [[[self uiElements] isConnected] intValue]);
846 if ([[[self uiElements] isConnected] intValue])
847 data = [self sendCommand:fileEndCmd toDevice:dev expectResponse:YES careForReturn:YES];
848 [fileHandle closeFile];
849 [[self uiElements] goToPath:[[self uiElements]currentPath]];
850 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]];
851 [[self uiElements] tableView:tableView didClickTableColumn:[[self uiElements]selectedColumn]]; //twice so get the same sort as before
852 [self turnTurboOn:NO];
853 [[self uiElements] finishTransfer];
856 - (void) turnTurboOn:(BOOL) turnOn {
857 if (![[[self uiElements] isConnected] intValue]) return;
858 [self checkUSB:myContext];
862 const SInt32 mode_bigendian = EndianS32_NtoB(mode);
863 NSData* modeData = [NSData dataWithBytes:&mode_bigendian length:4];
864 NSLog([modeData description]); //debugging
865 NSData* turboCommand = [self prepareCommand:USB_CmdTurbo withData:modeData];
866 [self sendCommand:turboCommand toDevice:myContext expectResponse:YES careForReturn:NO];
869 - (void) deleteFile:(NSDictionary*)fileInfo fromPath:(NSString*)currentPath {
870 [self checkUSB:myContext];
871 NSMutableString* fname = [NSMutableString stringWithString:[fileInfo objectForKey:@"name"]];
872 NSMutableData* build = [NSMutableData dataWithData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
873 char dir = 0x5c; // 0x5c = "\"
874 [build appendData:[NSData dataWithBytes:&dir length:1]];
875 [build appendData:[fname dataUsingEncoding:NSISOLatin1StringEncoding]];
877 [build appendData:[NSData dataWithBytes:&dir length:1]];
878 NSData* fileDelCmd = [self prepareCommand:USB_CmdHddDel withData:build];
879 [self sendCommand:fileDelCmd toDevice:myContext expectResponse:YES careForReturn:NO];
882 - (void) renameFile:(NSString*) oldName withName:(NSString*)newName atPath:(NSString*)currentPath {
883 NSLog(@"%@,%@,%@", oldName, newName, currentPath);
884 [self checkUSB:myContext];
885 unsigned short int i = [oldName length]+[currentPath length]+2;
886 UInt16 i_bigendian = EndianU16_NtoB(i);
887 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
888 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
890 [build appendData:[NSData dataWithBytes:&dir length:1]];
891 [build appendData:[oldName dataUsingEncoding:NSISOLatin1StringEncoding]];
893 [build appendData:[NSData dataWithBytes:&dir length:1]];
894 i = [newName length]+[currentPath length]+2;
895 i_bigendian = EndianU16_NtoB(i);
896 [build appendData:[NSData dataWithBytes:&i_bigendian length:2]];
897 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
899 [build appendData:[NSData dataWithBytes:&dir length:1]];
900 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
902 [build appendData:[NSData dataWithBytes:&dir length:1]];
903 NSData* fileRenCmd = [self prepareCommand:USB_CmdHddRename withData:build];
904 [self sendCommand:fileRenCmd toDevice:myContext expectResponse:YES careForReturn:NO];
907 - (void) makeFolder:(NSString*)newName atPath:(NSString*)currentPath {
908 [self checkUSB:myContext];
909 unsigned short int i = [newName length]+[currentPath length]+2;
910 const UInt16 i_bigendian = EndianU16_NtoB(i);
911 NSMutableData* build = [NSMutableData dataWithBytes:&i_bigendian length:2];
912 [build appendData:[currentPath dataUsingEncoding:NSISOLatin1StringEncoding]];
914 [build appendData:[NSData dataWithBytes:&dir length:1]];
915 [build appendData:[newName dataUsingEncoding:NSISOLatin1StringEncoding]];
917 [build appendData:[NSData dataWithBytes:&dir length:1]];
918 NSData* newFoldCmd = [self prepareCommand:USB_CmdHddCreateDir withData:build];
919 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
920 [self sendCommand:usbCancel toDevice:myContext expectResponse:YES careForReturn:NO];
921 [self sendCommand:newFoldCmd toDevice:myContext expectResponse:YES careForReturn:NO];
924 - (void) checkUSB:(USBDeviceContext*)device {
925 NSData* usbCancel = [self prepareCommand:USB_Cancel withData:nil];
926 NSData* data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
927 const UInt32 rW_bigendian = EndianU32_NtoB(USB_Success);
929 NSData* responseWanted = [NSData dataWithBytes:&rW_bigendian length:4];
930 while ((data == nil || [data length] < 8) && i < 8) {
931 NSLog (@"incorrect response");
933 data = [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:YES];
936 // tell someone that no longer connected here??
939 NSData* responseToCheck = [data subdataWithRange:(NSRange) {4,4}];
940 if (![responseWanted isEqualToData:responseToCheck]) {
941 [self sendCommand:usbCancel toDevice:device expectResponse:YES careForReturn:NO]; // send reset again
945 // ---------------------------------------------------------------------------
947 // ---------------------------------------------------------------------------
949 - (void) addPriorityTransfer:(id)newTransfer {
950 [priorityTransferQueue addObject:newTransfer];
951 NSLog(@"%i,p:%i-%@",[transferQueue count],[priorityTransferQueue count],[newTransfer objectForKey:@"transferType"]);
954 - (void) addTransfer:(id)newTransfer atIndex:(int)front { //-1 for at end
955 if (front>=0) [transferQueue insertObject:newTransfer atIndex:front];
956 else [transferQueue addObject:newTransfer];
957 NSLog(@"%i,p:%i",[transferQueue count],[priorityTransferQueue count]);
960 - (void) clearQueues {
961 [priorityTransferQueue removeAllObjects];
962 [transferQueue removeAllObjects];
963 [pausedQueue removeAllObjects];
966 - (id) transferQueue {
967 return transferQueue;
974 - (void) transfer:(id)sender {
975 while ([[[self uiElements] isConnected] intValue]) {
976 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
977 if ([priorityTransferQueue count] != 0 ) {
978 id currentTransfer = [priorityTransferQueue objectAtIndex:0];
979 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"fileList"]) {
980 [self getFileListForPath:[currentTransfer objectForKey:@"path"]];
981 [priorityTransferQueue removeObjectAtIndex:0];
982 // NSLog(@"fL fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
984 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"turbo"]) {
985 // [self turnTurboOn:[[currentTransfer objectForKey:@"turboOn"] boolValue]];
986 [priorityTransferQueue removeObjectAtIndex:0];
988 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"rename"]) {
989 [self renameFile:[currentTransfer objectForKey:@"oldName"]
990 withName:[currentTransfer objectForKey:@"newName"]
991 atPath:[currentTransfer objectForKey:@"path"]];
992 [priorityTransferQueue removeObjectAtIndex:0];
994 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"newFolder"]) {
995 [self makeFolder:[currentTransfer objectForKey:@"newName"] atPath:[currentTransfer objectForKey:@"path"]];
996 [priorityTransferQueue removeObjectAtIndex:0];
998 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"delete"]) {
999 [self deleteFile:[currentTransfer objectForKey:@"file"] fromPath:[currentTransfer objectForKey:@"path"]];
1000 [priorityTransferQueue removeObjectAtIndex:0];
1002 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"pause"]) {
1003 if ([transferQueue count] != 0) {
1004 NSArray* toPause = [transferQueue filteredArrayUsingPredicate:
1005 [NSPredicate predicateWithFormat:@"(filename = %@)",
1006 [currentTransfer objectForKey:@"filename"]]];
1007 if ([toPause count] > 1) NSLog(@"multiple pauses?");
1009 [pausedQueue addObjectsFromArray:toPause];
1010 [transferQueue removeObjectsInArray:toPause];
1013 [priorityTransferQueue removeObjectAtIndex:0];
1015 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"resume"]) {
1016 if ([pausedQueue count] != 0) {
1017 NSArray* toResume = [pausedQueue filteredArrayUsingPredicate:
1018 [NSPredicate predicateWithFormat:@"(filename = %@)",
1019 [currentTransfer objectForKey:@"filename"]]];
1020 if ([toResume count] > 1) NSLog(@"multiple resumes?");
1022 [transferQueue addObjectsFromArray:toResume];
1023 [pausedQueue removeObjectsInArray:toResume];
1026 [priorityTransferQueue removeObjectAtIndex:0];
1029 else if ([transferQueue count] != 0) {
1030 id currentTransfer = [transferQueue objectAtIndex:0];
1031 if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"download"]) {
1032 [self getFile:[currentTransfer objectForKey:@"filename"]
1033 forPath:[currentTransfer objectForKey:@"path"]
1034 toSaveTo:[currentTransfer objectForKey:@"savePath"]
1035 beginAtOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1036 withLooping:[[currentTransfer objectForKey:@"looping"] boolValue]
1037 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1038 [transferQueue removeObjectAtIndex:0];
1039 // NSLog(@"dl fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1041 else if ([[currentTransfer objectForKey:@"transferType"] isEqualToString:@"upload"]) {
1042 // NSLog(@"ul start %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1043 [self uploadFile:[currentTransfer objectForKey:@"filename"]
1044 ofSize:[[currentTransfer objectForKey:@"fileSize"] unsignedLongLongValue]
1045 fromPath:[currentTransfer objectForKey:@"path"]
1046 withAttributes:[currentTransfer objectForKey:@"attributes"]
1047 atOffset:[[currentTransfer objectForKey:@"offset"] unsignedLongLongValue]
1048 existingTime:[[currentTransfer objectForKey:@"existingTime"] intValue]];
1049 [transferQueue removeObjectAtIndex:0];
1050 // NSLog(@"ul fin %i,p:%i",[transferQueue count],[priorityTransferQueue count]);
1058 // ---------------------------------------------------------------------------
1060 // ---------------------------------------------------------------------------
1062 - (UIElements*) uiElements {
1063 return [NSApp delegate];
1066 -(void) setProgressBar:(NSProgressIndicator*)bar time:(NSTextField*)timeField turbo:(NSButton*)turbo{
1068 progressTime = timeField;
1072 -(void) updateProgress:(NSDictionary*) inDict {
1073 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
1074 double offset = [[inDict objectForKey:@"offset"] doubleValue];
1075 double size = [[inDict objectForKey:@"size"] doubleValue];
1076 NSDate* startTime = [inDict objectForKey:@"startTime"];
1077 [progressBar setDoubleValue:((double)offset/size*100)];
1078 [progressBar displayIfNeeded];
1079 [progressTime setStringValue:[self elapsedTime:[[NSDate date] timeIntervalSinceDate:startTime]]];
1080 [progressTime displayIfNeeded];
1084 - (NSString*) elapsedTime:(NSTimeInterval) totalSeconds {
1087 char sp = 20; //padding
1088 int hours = (totalSeconds / 3600); // returns number of whole hours fitted in totalSecs
1091 int minutes = ((totalSeconds / 60) - hours*60); // Whole minutes
1094 int seconds = ((long) totalSeconds % 60); // Here we can use modulo to get num secs NOT fitting in whole minutes (60 secs)
1097 return [NSString stringWithFormat:@"%c%i:%c%i:%c%i", hp, hours, mp, minutes, sp, seconds];
1100 - (void) setDH:(id)newDH tableView:(id)tv {
1105 // ---------------------------------------------------------------------------
1107 // ---------------------------------------------------------------------------
1110 if (self = [super init]) {
1113 transferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1114 pausedQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1115 priorityTransferQueue = [[NSMutableArray arrayWithCapacity:1] retain];
1120 void hexDump(UInt8 *buf, int len) {
1121 int row, col, maxrows;
1124 if (len % 16) maxrows++;
1125 for (row=0; row< maxrows; row++) {
1126 for (col=0; col<16; col++) {
1127 if (!(col%2)) printf(" ");
1128 printf("%02x", buf[row*16 + col] & 0xff);
1131 for (col=0; col<16; col++) {
1132 if ((buf[row*16 + col]>32) && (buf[row*16 + col]<126)) {
1133 printf("%c", buf[row*16 + col]);
1135 else { printf("."); }
1142 return connectedSpeed;
1145 -(void) setDebug:(int)mode {
1147 NSLog(@"Debug level: %i", debug);
1150 -(void) setRate:(int) newRate {
1152 NSLog(@"New rate set: %i", rate);