]> git.gir.st - tmk_keyboard.git/blob - protocol/usb_hid/USB_Host_Shield_2.0/masstorage.cpp
Squashed 'tmk_core/' changes from caca2c0..dc0e46e
[tmk_keyboard.git] / protocol / usb_hid / USB_Host_Shield_2.0 / masstorage.cpp
1 /* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
2
3 This software may be distributed and modified under the terms of the GNU
4 General Public License version 2 (GPL2) as published by the Free Software
5 Foundation and appearing in the file GPL2.TXT included in the packaging of
6 this file. Please note that GPL2 Section 2[b] requires that all works based
7 on this software must also be made publicly available under the terms of
8 the GPL2 ("Copyleft").
9
10 Contact information
11 -------------------
12
13 Circuits At Home, LTD
14 Web : http://www.circuitsathome.com
15 e-mail : support@circuitsathome.com
16 */
17
18 #include "masstorage.h"
19
20 const uint8_t BulkOnly::epDataInIndex = 1;
21 const uint8_t BulkOnly::epDataOutIndex = 2;
22 const uint8_t BulkOnly::epInterruptInIndex = 3;
23
24 ////////////////////////////////////////////////////////////////////////////////
25
26 // Interface code
27
28 ////////////////////////////////////////////////////////////////////////////////
29
30 /**
31 * Get the capacity of the media
32 *
33 * @param lun Logical Unit Number
34 * @return media capacity
35 */
36 uint32_t BulkOnly::GetCapacity(uint8_t lun) {
37 if(LUNOk[lun])
38 return CurrentCapacity[lun];
39 return 0LU;
40 }
41
42 /**
43 * Get the sector (block) size used on the media
44 *
45 * @param lun Logical Unit Number
46 * @return media sector size
47 */
48 uint16_t BulkOnly::GetSectorSize(uint8_t lun) {
49 if(LUNOk[lun])
50 return CurrentSectorSize[lun];
51 return 0U;
52 }
53
54 /**
55 * Test if LUN is ready for use
56 *
57 * @param lun Logical Unit Number
58 * @return true if LUN is ready for use
59 */
60 bool BulkOnly::LUNIsGood(uint8_t lun) {
61 return LUNOk[lun];
62 }
63
64 /**
65 * Test if LUN is write protected
66 *
67 * @param lun Logical Unit Number
68 * @return cached status of write protect switch
69 */
70 bool BulkOnly::WriteProtected(uint8_t lun) {
71 return WriteOk[lun];
72 }
73
74 /**
75 * Wrap and execute a SCSI CDB with length of 6
76 *
77 * @param cdb CDB to execute
78 * @param buf_size Size of expected transaction
79 * @param buf Buffer
80 * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
81 * @return
82 */
83 uint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
84 // promote buf_size to 32bits.
85 CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
86 //SetCurLUN(cdb->LUN);
87 return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
88 }
89
90 /**
91 * Wrap and execute a SCSI CDB with length of 10
92 *
93 * @param cdb CDB to execute
94 * @param buf_size Size of expected transaction
95 * @param buf Buffer
96 * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
97 * @return
98 */
99 uint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
100 // promote buf_size to 32bits.
101 CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
102 //SetCurLUN(cdb->LUN);
103 return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
104 }
105
106 /**
107 * Lock or Unlock the tray or door on device.
108 * Caution: Some devices with buggy firmware will lock up.
109 *
110 * @param lun Logical Unit Number
111 * @param lock 1 to lock, 0 to unlock
112 * @return
113 */
114 uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) {
115 Notify(PSTR("\r\nLockMedia\r\n"), 0x80);
116 Notify(PSTR("---------\r\n"), 0x80);
117
118 CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock);
119 return SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_IN);
120 }
121
122 /**
123 * Media control, for spindle motor and media tray or door.
124 * This includes CDROM, TAPE and anything with a media loader.
125 *
126 * @param lun Logical Unit Number
127 * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media
128 * @return 0 on success
129 */
130 uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) {
131 Notify(PSTR("\r\nMediaCTL\r\n"), 0x80);
132 Notify(PSTR("-----------------\r\n"), 0x80);
133
134 uint8_t rcode = MASS_ERR_UNIT_NOT_READY;
135 if(bAddress) {
136 CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0);
137 rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT);
138 } else {
139 SetCurLUN(lun);
140 }
141 return rcode;
142 }
143
144 /**
145 * Read data from media
146 *
147 * @param lun Logical Unit Number
148 * @param addr LBA address on media to read
149 * @param bsize size of a block (we should probably use the cached size)
150 * @param blocks how many blocks to read
151 * @param buf memory that is able to hold the requested data
152 * @return 0 on success
153 */
154 uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {
155 if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
156 Notify(PSTR("\r\nRead LUN:\t"), 0x80);
157 D_PrintHex<uint8_t > (lun, 0x90);
158 Notify(PSTR("\r\nLBA:\t\t"), 0x90);
159 D_PrintHex<uint32_t > (addr, 0x90);
160 Notify(PSTR("\r\nblocks:\t\t"), 0x90);
161 D_PrintHex<uint8_t > (blocks, 0x90);
162 Notify(PSTR("\r\nblock size:\t"), 0x90);
163 D_PrintHex<uint16_t > (bsize, 0x90);
164 Notify(PSTR("\r\n---------\r\n"), 0x80);
165 CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr);
166
167 again:
168 uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN);
169
170 if(er == MASS_ERR_STALL) {
171 MediaCTL(lun, 1);
172 delay(150);
173 if(!TestUnitReady(lun)) goto again;
174 }
175 return er;
176 }
177
178 /**
179 * Write data to media
180 *
181 * @param lun Logical Unit Number
182 * @param addr LBA address on media to write
183 * @param bsize size of a block (we should probably use the cached size)
184 * @param blocks how many blocks to write
185 * @param buf memory that contains the data to write
186 * @return 0 on success
187 */
188 uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {
189 if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
190 if(!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED;
191 Notify(PSTR("\r\nWrite LUN:\t"), 0x80);
192 D_PrintHex<uint8_t > (lun, 0x90);
193 Notify(PSTR("\r\nLBA:\t\t"), 0x90);
194 D_PrintHex<uint32_t > (addr, 0x90);
195 Notify(PSTR("\r\nblocks:\t\t"), 0x90);
196 D_PrintHex<uint8_t > (blocks, 0x90);
197 Notify(PSTR("\r\nblock size:\t"), 0x90);
198 D_PrintHex<uint16_t > (bsize, 0x90);
199 Notify(PSTR("\r\n---------\r\n"), 0x80);
200 CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr);
201
202 again:
203 uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT);
204
205 if(er == MASS_ERR_WRITE_STALL) {
206 MediaCTL(lun, 1);
207 delay(150);
208 if(!TestUnitReady(lun)) goto again;
209 }
210 return er;
211 }
212
213 // End of user functions, the remaining code below is driver internals.
214 // Only developer serviceable parts below!
215
216 ////////////////////////////////////////////////////////////////////////////////
217
218 // Main driver code
219
220 ////////////////////////////////////////////////////////////////////////////////
221
222 BulkOnly::BulkOnly(USB *p) :
223 pUsb(p),
224 bAddress(0),
225 bIface(0),
226 bNumEP(1),
227 qNextPollTime(0),
228 bPollEnable(false),
229 //dCBWTag(0),
230 bLastUsbError(0) {
231 ClearAllEP();
232 dCBWTag = 0;
233 if(pUsb)
234 pUsb->RegisterDeviceClass(this);
235 }
236
237 /**
238 * USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success
239 * We need to standardize either the rcode, or change the API to return values
240 * so a signal that additional actions are required can be produced.
241 * Some of these codes do exist already.
242 *
243 * TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance.
244 * Doing so would save some program memory when using multiple drivers.
245 *
246 * @param parent USB address of parent
247 * @param port address of port on parent
248 * @param lowspeed true if device is low speed
249 * @return
250 */
251 uint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
252
253 const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
254
255 uint8_t buf[constBufSize];
256 USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
257 uint8_t rcode;
258 UsbDevice *p = NULL;
259 EpInfo *oldep_ptr = NULL;
260 USBTRACE("MS ConfigureDevice\r\n");
261 ClearAllEP();
262 AddressPool &addrPool = pUsb->GetAddressPool();
263
264
265 if(bAddress)
266 return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
267
268 // <TECHNICAL>
269 // Get pointer to pseudo device with address 0 assigned
270 p = addrPool.GetUsbDevicePtr(0);
271 if(!p) {
272 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
273 }
274
275 if(!p->epinfo) {
276 USBTRACE("epinfo\r\n");
277 return USB_ERROR_EPINFO_IS_NULL;
278 }
279
280 // Save old pointer to EP_RECORD of address 0
281 oldep_ptr = p->epinfo;
282
283 // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
284 p->epinfo = epInfo;
285
286 p->lowspeed = lowspeed;
287 // Get device descriptor
288 rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
289
290 // Restore p->epinfo
291 p->epinfo = oldep_ptr;
292
293 if(rcode) {
294 goto FailGetDevDescr;
295 }
296 // Allocate new address according to device class
297 bAddress = addrPool.AllocAddress(parent, false, port);
298
299 if(!bAddress)
300 return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
301
302 // Extract Max Packet Size from the device descriptor
303 epInfo[0].maxPktSize = udd->bMaxPacketSize0;
304 // Steal and abuse from epInfo structure to save on memory.
305 epInfo[1].epAddr = udd->bNumConfigurations;
306 // </TECHNICAL>
307 return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
308
309 FailGetDevDescr:
310 #ifdef DEBUG_USB_HOST
311 NotifyFailGetDevDescr(rcode);
312 #endif
313 rcode = USB_ERROR_FailGetDevDescr;
314
315 Release();
316 return rcode;
317 };
318
319 /**
320 *
321 * @param parent (not used)
322 * @param port (not used)
323 * @param lowspeed true if device is low speed
324 * @return 0 for success
325 */
326 uint8_t BulkOnly::Init(uint8_t parent, uint8_t port, bool lowspeed) {
327 uint8_t rcode;
328 uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations
329 epInfo[1].epAddr = 0;
330 USBTRACE("MS Init\r\n");
331
332 AddressPool &addrPool = pUsb->GetAddressPool();
333 UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress);
334
335 if(!p)
336 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
337
338 // Assign new address to the device
339 delay(2000);
340 rcode = pUsb->setAddr(0, 0, bAddress);
341
342 if(rcode) {
343 p->lowspeed = false;
344 addrPool.FreeAddress(bAddress);
345 bAddress = 0;
346 USBTRACE2("setAddr:", rcode);
347 return rcode;
348 }
349
350 USBTRACE2("Addr:", bAddress);
351
352 p->lowspeed = false;
353
354 p = addrPool.GetUsbDevicePtr(bAddress);
355
356 if(!p)
357 return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
358
359 p->lowspeed = lowspeed;
360
361 // Assign epInfo to epinfo pointer
362 rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
363
364 if(rcode)
365 goto FailSetDevTblEntry;
366
367 USBTRACE2("NC:", num_of_conf);
368
369 for(uint8_t i = 0; i < num_of_conf; i++) {
370 ConfigDescParser< USB_CLASS_MASS_STORAGE,
371 MASS_SUBCLASS_SCSI,
372 MASS_PROTO_BBB,
373 CP_MASK_COMPARE_CLASS |
374 CP_MASK_COMPARE_SUBCLASS |
375 CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this);
376
377 rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser);
378
379 if(rcode)
380 goto FailGetConfDescr;
381
382 if(bNumEP > 1)
383 break;
384 }
385
386 if(bNumEP < 3)
387 return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
388
389 // Assign epInfo to epinfo pointer
390 pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
391
392 USBTRACE2("Conf:", bConfNum);
393
394 // Set Configuration Value
395 rcode = pUsb->setConf(bAddress, 0, bConfNum);
396
397 if(rcode)
398 goto FailSetConfDescr;
399
400 //Linux does a 1sec delay after this.
401 delay(1000);
402
403 rcode = GetMaxLUN(&bMaxLUN);
404 if(rcode)
405 goto FailGetMaxLUN;
406
407 if(bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1;
408 ErrorMessage<uint8_t > (PSTR("MaxLUN"), bMaxLUN);
409
410 delay(1000); // Delay a bit for slow firmware.
411
412 for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
413 InquiryResponse response;
414 rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response);
415 if(rcode) {
416 ErrorMessage<uint8_t > (PSTR("Inquiry"), rcode);
417 } else {
418 #if 0
419 printf("LUN %i `", lun);
420 uint8_t *buf = response.VendorID;
421 for(int i = 0; i < 28; i++) printf("%c", buf[i]);
422 printf("'\r\nQualifier %1.1X ", response.PeripheralQualifier);
423 printf("Device type %2.2X ", response.DeviceType);
424 printf("RMB %1.1X ", response.Removable);
425 printf("SSCS %1.1X ", response.SCCS);
426 uint8_t sv = response.Version;
427 printf("SCSI version %2.2X\r\nDevice conforms to ", sv);
428 switch(sv) {
429 case 0:
430 printf("No specific");
431 break;
432 case 1:
433 printf("ANSI X3.131-1986 (ANSI 1)");
434 break;
435 case 2:
436 printf("ANSI X3.131-1994 (ANSI 2)");
437 break;
438 case 3:
439 printf("ANSI INCITS 301-1997 (SPC)");
440 break;
441 case 4:
442 printf("ANSI INCITS 351-2001 (SPC-2)");
443 break;
444 case 5:
445 printf("ANSI INCITS 408-2005 (SPC-4)");
446 break;
447 case 6:
448 printf("T10/1731-D (SPC-4)");
449 break;
450 default:
451 printf("unknown");
452 }
453 printf(" standards.\r\n");
454 #endif
455 uint8_t tries = 0xf0;
456 while((rcode = TestUnitReady(lun))) {
457 if(rcode == 0x08) break; // break on no media, this is OK to do.
458 // try to lock media and spin up
459 if(tries < 14) {
460 LockMedia(lun, 1);
461 MediaCTL(lun, 1); // I actually have a USB stick that needs this!
462 } else delay(2 * (tries + 1));
463 tries++;
464 if(!tries) break;
465 }
466 if(!rcode) {
467 delay(1000);
468 LUNOk[lun] = CheckLUN(lun);
469 if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
470 }
471 }
472 }
473
474
475 CheckMedia();
476
477 rcode = OnInit();
478
479 if(rcode)
480 goto FailOnInit;
481
482 #ifdef DEBUG_USB_HOST
483 USBTRACE("MS configured\r\n\r\n");
484 #endif
485
486 bPollEnable = true;
487
488 //USBTRACE("Poll enabled\r\n");
489 return 0;
490
491 FailSetConfDescr:
492 #ifdef DEBUG_USB_HOST
493 NotifyFailSetConfDescr();
494 goto Fail;
495 #endif
496
497 FailOnInit:
498 #ifdef DEBUG_USB_HOST
499 USBTRACE("OnInit:");
500 goto Fail;
501 #endif
502
503 FailGetMaxLUN:
504 #ifdef DEBUG_USB_HOST
505 USBTRACE("GetMaxLUN:");
506 goto Fail;
507 #endif
508
509 //#ifdef DEBUG_USB_HOST
510 //FailInvalidSectorSize:
511 // USBTRACE("Sector Size is NOT VALID: ");
512 // goto Fail;
513 //#endif
514
515 FailSetDevTblEntry:
516 #ifdef DEBUG_USB_HOST
517 NotifyFailSetDevTblEntry();
518 goto Fail;
519 #endif
520
521 FailGetConfDescr:
522 #ifdef DEBUG_USB_HOST
523 NotifyFailGetConfDescr();
524 #endif
525
526 #ifdef DEBUG_USB_HOST
527 Fail:
528 NotifyFail(rcode);
529 #endif
530 Release();
531 return rcode;
532 }
533
534 /**
535 * For driver use only.
536 *
537 * @param conf
538 * @param iface
539 * @param alt
540 * @param proto
541 * @param pep
542 */
543 void BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR * pep) {
544 ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
545 ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
546 ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
547
548 bConfNum = conf;
549
550 uint8_t index;
551
552 #if 1
553 if((pep->bmAttributes & 0x02) == 2) {
554 index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
555 // Fill in the endpoint info structure
556 epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
557 epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
558 epInfo[index].epAttribs = 0;
559
560 bNumEP++;
561
562 PrintEndpointDescriptor(pep);
563
564 }
565 #else
566 if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)
567 index = epInterruptInIndex;
568 else
569 if((pep->bmAttributes & 0x02) == 2)
570 index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
571 else
572 return;
573
574 // Fill in the endpoint info structure
575 epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
576 epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
577 epInfo[index].epAttribs = 0;
578
579 bNumEP++;
580
581 PrintEndpointDescriptor(pep);
582 #endif
583 }
584
585 /**
586 * For driver use only.
587 *
588 * @return
589 */
590 uint8_t BulkOnly::Release() {
591 ClearAllEP();
592 pUsb->GetAddressPool().FreeAddress(bAddress);
593 return 0;
594 }
595
596 /**
597 * For driver use only.
598 *
599 * @param lun Logical Unit Number
600 * @return true if LUN is ready for use.
601 */
602 bool BulkOnly::CheckLUN(uint8_t lun) {
603 uint8_t rcode;
604 Capacity capacity;
605 for(uint8_t i = 0; i < 8; i++) capacity.data[i] = 0;
606
607 rcode = ReadCapacity10(lun, (uint8_t*)capacity.data);
608 if(rcode) {
609 //printf(">>>>>>>>>>>>>>>>ReadCapacity returned %i\r\n", rcode);
610 return false;
611 }
612 ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>CAPACITY OK ON LUN"), lun);
613 for(uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++)
614 D_PrintHex<uint8_t > (capacity.data[i], 0x80);
615 Notify(PSTR("\r\n\r\n"), 0x80);
616 // Only 512/1024/2048/4096 are valid values!
617 uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]);
618 if(c != 0x0200LU && c != 0x0400LU && c != 0x0800LU && c != 0x1000LU) {
619 return false;
620 }
621 // Store capacity information.
622 CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF);
623
624 CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1;
625 if(CurrentCapacity[lun] == /*0xffffffffLU */ 0x01LU || CurrentCapacity[lun] == 0x00LU) {
626 // Buggy firmware will report 0xffffffff or 0 for no media
627 if(CurrentCapacity[lun])
628 ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN"), lun);
629 return false;
630 }
631 delay(20);
632 Page3F(lun);
633 if(!TestUnitReady(lun)) return true;
634 return false;
635 }
636
637 /**
638 * For driver use only.
639 *
640 * Scan for media change on all LUNs
641 */
642 void BulkOnly::CheckMedia() {
643 for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
644 if(TestUnitReady(lun)) {
645 LUNOk[lun] = false;
646 continue;
647 }
648 if(!LUNOk[lun])
649 LUNOk[lun] = CheckLUN(lun);
650 }
651 #if 0
652 printf("}}}}}}}}}}}}}}}}STATUS ");
653 for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
654 if(LUNOk[lun])
655 printf("#");
656 else printf(".");
657 }
658 printf("\r\n");
659 #endif
660 qNextPollTime = millis() + 2000;
661 }
662
663 /**
664 * For driver use only.
665 *
666 * @return
667 */
668 uint8_t BulkOnly::Poll() {
669 //uint8_t rcode = 0;
670
671 if(!bPollEnable)
672 return 0;
673
674 if((long)(millis() - qNextPollTime) >= 0L) {
675 CheckMedia();
676 }
677 //rcode = 0;
678
679 return 0;
680 }
681
682 ////////////////////////////////////////////////////////////////////////////////
683
684
685 // SCSI code
686
687
688 ////////////////////////////////////////////////////////////////////////////////
689
690 /**
691 * For driver use only.
692 *
693 * @param plun
694 * @return
695 */
696 uint8_t BulkOnly::GetMaxLUN(uint8_t *plun) {
697 uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, NULL);
698
699 if(ret == hrSTALL)
700 *plun = 0;
701
702 return 0;
703 }
704
705 /**
706 * For driver use only. Used during Driver Init
707 *
708 * @param lun Logical Unit Number
709 * @param bsize
710 * @param buf
711 * @return
712 */
713 uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) {
714 Notify(PSTR("\r\nInquiry\r\n"), 0x80);
715 Notify(PSTR("---------\r\n"), 0x80);
716
717 CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0LU, (uint8_t)bsize, 0);
718 uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN);
719
720 return rc;
721 }
722
723 /**
724 * For driver use only.
725 *
726 * @param lun Logical Unit Number
727 * @return
728 */
729 uint8_t BulkOnly::TestUnitReady(uint8_t lun) {
730 //SetCurLUN(lun);
731 if(!bAddress)
732 return MASS_ERR_UNIT_NOT_READY;
733
734 Notify(PSTR("\r\nTestUnitReady\r\n"), 0x80);
735 Notify(PSTR("-----------------\r\n"), 0x80);
736
737 CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0);
738 return SCSITransaction6(&cdb, 0, NULL, (uint8_t)MASS_CMD_DIR_IN);
739
740 }
741
742 /**
743 * For driver use only.
744 *
745 * @param lun Logical Unit Number
746 * @param pc
747 * @param page
748 * @param subpage
749 * @param len
750 * @param pbuf
751 * @return
752 */
753 uint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) {
754 Notify(PSTR("\r\rModeSense\r\n"), 0x80);
755 Notify(PSTR("------------\r\n"), 0x80);
756
757 CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0);
758 return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN);
759 }
760
761 /**
762 * For driver use only.
763 *
764 * @param lun Logical Unit Number
765 * @param bsize
766 * @param buf
767 * @return
768 */
769 uint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) {
770 Notify(PSTR("\r\nReadCapacity\r\n"), 0x80);
771 Notify(PSTR("---------------\r\n"), 0x80);
772
773 CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun);
774 return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN);
775 }
776
777 /**
778 * For driver use only.
779 *
780 * Page 3F contains write protect status.
781 *
782 * @param lun Logical Unit Number to test.
783 * @return Write protect switch status.
784 */
785 uint8_t BulkOnly::Page3F(uint8_t lun) {
786 uint8_t buf[192];
787 for(int i = 0; i < 192; i++) {
788 buf[i] = 0x00;
789 }
790 WriteOk[lun] = true;
791 uint8_t rc = ModeSense6(lun, 0, 0x3f, 0, 192, buf);
792 if(!rc) {
793 WriteOk[lun] = ((buf[2] & 0x80) == 0);
794 Notify(PSTR("Mode Sense: "), 0x80);
795 for(int i = 0; i < 4; i++) {
796 D_PrintHex<uint8_t > (buf[i], 0x80);
797 Notify(PSTR(" "), 0x80);
798 }
799 Notify(PSTR("\r\n"), 0x80);
800 }
801 return rc;
802 }
803
804 /**
805 * For driver use only.
806 *
807 * @param lun Logical Unit Number
808 * @param size
809 * @param buf
810 * @return
811 */
812 uint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) {
813 Notify(PSTR("\r\nRequestSense\r\n"), 0x80);
814 Notify(PSTR("----------------\r\n"), 0x80);
815
816 CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0LU, (uint8_t)size, 0);
817 CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN);
818 //SetCurLUN(lun);
819 return Transaction(&cbw, size, buf);
820 }
821
822
823 ////////////////////////////////////////////////////////////////////////////////
824
825
826 // USB code
827
828
829 ////////////////////////////////////////////////////////////////////////////////
830
831 /**
832 * For driver use only.
833 *
834 * @param index
835 * @return
836 */
837 uint8_t BulkOnly::ClearEpHalt(uint8_t index) {
838 if(index == 0)
839 return 0;
840
841 uint8_t ret = 0;
842
843 while((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, NULL, NULL)) == 0x01))
844 delay(6);
845
846 if(ret) {
847 ErrorMessage<uint8_t > (PSTR("ClearEpHalt"), ret);
848 ErrorMessage<uint8_t > (PSTR("EP"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr));
849 return ret;
850 }
851 epInfo[index].bmSndToggle = 0;
852 epInfo[index].bmRcvToggle = 0;
853 // epAttribs = 0;
854 return 0;
855 }
856
857 /**
858 * For driver use only.
859 *
860 */
861 void BulkOnly::Reset() {
862 while(pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, NULL, NULL) == 0x01) delay(6);
863 }
864
865 /**
866 * For driver use only.
867 *
868 * @return 0 if successful
869 */
870 uint8_t BulkOnly::ResetRecovery() {
871 Notify(PSTR("\r\nResetRecovery\r\n"), 0x80);
872 Notify(PSTR("-----------------\r\n"), 0x80);
873
874 delay(6);
875 Reset();
876 delay(6);
877 ClearEpHalt(epDataInIndex);
878 delay(6);
879 bLastUsbError = ClearEpHalt(epDataOutIndex);
880 delay(6);
881 return bLastUsbError;
882 }
883
884 /**
885 * For driver use only.
886 *
887 * Clear all EP data and clear all LUN status
888 */
889 void BulkOnly::ClearAllEP() {
890 for(uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) {
891 epInfo[i].epAddr = 0;
892 epInfo[i].maxPktSize = (i) ? 0 : 8;
893 epInfo[i].epAttribs = 0;
894
895 epInfo[i].bmNakPower = USB_NAK_DEFAULT;
896 }
897
898 for(uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) {
899 LUNOk[i] = false;
900 WriteOk[i] = false;
901 CurrentCapacity[i] = 0lu;
902 CurrentSectorSize[i] = 0;
903 }
904
905 bIface = 0;
906 bNumEP = 1;
907 bAddress = 0;
908 qNextPollTime = 0;
909 bPollEnable = false;
910 bLastUsbError = 0;
911 bMaxLUN = 0;
912 bTheLUN = 0;
913 }
914
915 /**
916 * For driver use only.
917 *
918 * @param pcsw
919 * @param pcbw
920 * @return
921 */
922 bool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) {
923 if(pcsw->dCSWSignature != MASS_CSW_SIGNATURE) {
924 Notify(PSTR("CSW:Sig error\r\n"), 0x80);
925 return false;
926 }
927 if(pcsw->dCSWTag != pcbw->dCBWTag) {
928 Notify(PSTR("CSW:Wrong tag\r\n"), 0x80);
929 return false;
930 }
931 return true;
932 }
933
934 /**
935 * For driver use only.
936 *
937 * @param error
938 * @param index
939 * @return
940 */
941 uint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) {
942 uint8_t count = 3;
943
944 bLastUsbError = error;
945 //if (error)
946 //ClearEpHalt(index);
947 while(error && count) {
948 if(error != hrSUCCESS) {
949 ErrorMessage<uint8_t > (PSTR("USB Error"), error);
950 ErrorMessage<uint8_t > (PSTR("Index"), index);
951 }
952 switch(error) {
953 // case hrWRONGPID:
954 case hrSUCCESS:
955 return MASS_ERR_SUCCESS;
956 case hrBUSY:
957 // SIE is busy, just hang out and try again.
958 return MASS_ERR_UNIT_BUSY;
959 case hrTIMEOUT:
960 case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED;
961 case hrSTALL:
962 if(index == 0)
963 return MASS_ERR_STALL;
964 ClearEpHalt(index);
965 if(index != epDataInIndex)
966 return MASS_ERR_WRITE_STALL;
967 return MASS_ERR_STALL;
968
969 case hrNAK:
970 if(index == 0)
971 return MASS_ERR_UNIT_BUSY;
972 return MASS_ERR_UNIT_BUSY;
973
974 case hrTOGERR:
975 // Handle a very super rare corner case, where toggles become de-synched.
976 // I have only ran into one device that has this firmware bug, and this is
977 // the only clean way to get back into sync with the buggy device firmware.
978 // --AJK
979 if(bAddress && bConfNum) {
980 error = pUsb->setConf(bAddress, 0, bConfNum);
981
982 if(error)
983 break;
984 }
985 return MASS_ERR_SUCCESS;
986 default:
987 ErrorMessage<uint8_t > (PSTR("\r\nUSB"), error);
988 return MASS_ERR_GENERAL_USB_ERROR;
989 }
990 count--;
991 } // while
992
993 return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS);
994 }
995
996 #if MS_WANT_PARSER
997
998 uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) {
999 return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0);
1000 }
1001 #endif
1002
1003 /**
1004 * For driver use only.
1005 *
1006 * @param pcbw
1007 * @param buf_size
1008 * @param buf
1009 * @param flags
1010 * @return
1011 */
1012 uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf
1013 #if MS_WANT_PARSER
1014 , uint8_t flags
1015 #endif
1016 ) {
1017
1018 #if MS_WANT_PARSER
1019 uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength;
1020 printf("Transfersize %i\r\n", bytes);
1021 delay(1000);
1022
1023 bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK;
1024 #else
1025 uint16_t bytes = buf_size;
1026 #endif
1027 bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN;
1028 uint8_t ret = 0;
1029 uint8_t usberr;
1030 CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles.
1031 SetCurLUN(pcbw->bmCBWLUN);
1032 ErrorMessage<uint32_t > (PSTR("CBW.dCBWTag"), pcbw->dCBWTag);
1033
1034 while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1);
1035
1036 ret = HandleUsbError(usberr, epDataOutIndex);
1037 //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex);
1038 if(ret) {
1039 ErrorMessage<uint8_t > (PSTR("============================ CBW"), ret);
1040 } else {
1041 if(bytes) {
1042 if(!write) {
1043 #if MS_WANT_PARSER
1044 if(callback) {
1045 uint8_t rbuf[bytes];
1046 while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1);
1047 if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0);
1048 } else {
1049 #endif
1050 while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
1051 #if MS_WANT_PARSER
1052
1053 }
1054 #endif
1055 ret = HandleUsbError(usberr, epDataInIndex);
1056 } else {
1057 while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
1058 ret = HandleUsbError(usberr, epDataOutIndex);
1059 }
1060 if(ret) {
1061 ErrorMessage<uint8_t > (PSTR("============================ DAT"), ret);
1062 }
1063 }
1064 }
1065
1066 {
1067 bytes = sizeof (CommandStatusWrapper);
1068 int tries = 2;
1069 while(tries--) {
1070 while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1);
1071 if(!usberr) break;
1072 ClearEpHalt(epDataInIndex);
1073 if(tries) ResetRecovery();
1074 }
1075 if(!ret) {
1076 Notify(PSTR("CBW:\t\tOK\r\n"), 0x80);
1077 Notify(PSTR("Data Stage:\tOK\r\n"), 0x80);
1078 } else {
1079 // Throw away csw, IT IS NOT OF ANY USE.
1080 ResetRecovery();
1081 return ret;
1082 }
1083 ret = HandleUsbError(usberr, epDataInIndex);
1084 if(ret) {
1085 ErrorMessage<uint8_t > (PSTR("============================ CSW"), ret);
1086 }
1087 if(usberr == hrSUCCESS) {
1088 if(IsValidCSW(&csw, pcbw)) {
1089 //ErrorMessage<uint32_t > (PSTR("CSW.dCBWTag"), csw.dCSWTag);
1090 //ErrorMessage<uint8_t > (PSTR("bCSWStatus"), csw.bCSWStatus);
1091 //ErrorMessage<uint32_t > (PSTR("dCSWDataResidue"), csw.dCSWDataResidue);
1092 Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80);
1093 return csw.bCSWStatus;
1094 } else {
1095 // NOTE! Sometimes this is caused by the reported residue being wrong.
1096 // Get a different device. It isn't compliant, and should have never passed Q&A.
1097 // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter.
1098 // Other devices that exhibit this behavior exist in the wild too.
1099 // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk
1100 Notify(PSTR("Invalid CSW\r\n"), 0x80);
1101 ResetRecovery();
1102 //return MASS_ERR_SUCCESS;
1103 return MASS_ERR_INVALID_CSW;
1104 }
1105 }
1106 }
1107 return ret;
1108 }
1109
1110 /**
1111 * For driver use only.
1112 *
1113 * @param lun Logical Unit Number
1114 * @return
1115 */
1116 uint8_t BulkOnly::SetCurLUN(uint8_t lun) {
1117 if(lun > bMaxLUN)
1118 return MASS_ERR_INVALID_LUN;
1119 bTheLUN = lun;
1120 return MASS_ERR_SUCCESS;
1121 };
1122
1123 /**
1124 * For driver use only.
1125 *
1126 * @param status
1127 * @return
1128 */
1129 uint8_t BulkOnly::HandleSCSIError(uint8_t status) {
1130 uint8_t ret = 0;
1131
1132 switch(status) {
1133 case 0: return MASS_ERR_SUCCESS;
1134
1135 case 2:
1136 ErrorMessage<uint8_t > (PSTR("Phase Error"), status);
1137 ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
1138 ResetRecovery();
1139 return MASS_ERR_GENERAL_SCSI_ERROR;
1140
1141 case 1:
1142 ErrorMessage<uint8_t > (PSTR("SCSI Error"), status);
1143 ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
1144 RequestSenseResponce rsp;
1145
1146 ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp);
1147
1148 if(ret) {
1149 return MASS_ERR_GENERAL_SCSI_ERROR;
1150 }
1151 ErrorMessage<uint8_t > (PSTR("Response Code"), rsp.bResponseCode);
1152 if(rsp.bResponseCode & 0x80) {
1153 Notify(PSTR("Information field: "), 0x80);
1154 for(int i = 0; i < 4; i++) {
1155 D_PrintHex<uint8_t > (rsp.CmdSpecificInformation[i], 0x80);
1156 Notify(PSTR(" "), 0x80);
1157 }
1158 Notify(PSTR("\r\n"), 0x80);
1159 }
1160 ErrorMessage<uint8_t > (PSTR("Sense Key"), rsp.bmSenseKey);
1161 ErrorMessage<uint8_t > (PSTR("Add Sense Code"), rsp.bAdditionalSenseCode);
1162 ErrorMessage<uint8_t > (PSTR("Add Sense Qual"), rsp.bAdditionalSenseQualifier);
1163 // warning, this is not testing ASQ, only SK and ASC.
1164 switch(rsp.bmSenseKey) {
1165 case SCSI_S_UNIT_ATTENTION:
1166 switch(rsp.bAdditionalSenseCode) {
1167 case SCSI_ASC_MEDIA_CHANGED:
1168 return MASS_ERR_MEDIA_CHANGED;
1169 default:
1170 return MASS_ERR_UNIT_NOT_READY;
1171 }
1172 case SCSI_S_NOT_READY:
1173 switch(rsp.bAdditionalSenseCode) {
1174 case SCSI_ASC_MEDIUM_NOT_PRESENT:
1175 return MASS_ERR_NO_MEDIA;
1176 default:
1177 return MASS_ERR_UNIT_NOT_READY;
1178 }
1179 case SCSI_S_ILLEGAL_REQUEST:
1180 switch(rsp.bAdditionalSenseCode) {
1181 case SCSI_ASC_LBA_OUT_OF_RANGE:
1182 return MASS_ERR_BAD_LBA;
1183 default:
1184 return MASS_ERR_CMD_NOT_SUPPORTED;
1185 }
1186 default:
1187 return MASS_ERR_GENERAL_SCSI_ERROR;
1188 }
1189
1190 // case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later.
1191 // case 0x05/0x14: we stalled out
1192 // case 0x15/0x16: we naked out.
1193 default:
1194 ErrorMessage<uint8_t > (PSTR("Gen SCSI Err"), status);
1195 ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
1196 return status;
1197 } // switch
1198 }
1199
1200
1201 ////////////////////////////////////////////////////////////////////////////////
1202
1203
1204 // Debugging code
1205
1206
1207 ////////////////////////////////////////////////////////////////////////////////
1208
1209 /**
1210 *
1211 * @param ep_ptr
1212 */
1213 void BulkOnly::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR * ep_ptr) {
1214 Notify(PSTR("Endpoint descriptor:"), 0x80);
1215 Notify(PSTR("\r\nLength:\t\t"), 0x80);
1216 D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
1217 Notify(PSTR("\r\nType:\t\t"), 0x80);
1218 D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
1219 Notify(PSTR("\r\nAddress:\t"), 0x80);
1220 D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
1221 Notify(PSTR("\r\nAttributes:\t"), 0x80);
1222 D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
1223 Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
1224 D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
1225 Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
1226 D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
1227 Notify(PSTR("\r\n"), 0x80);
1228 }
1229
1230
1231 ////////////////////////////////////////////////////////////////////////////////
1232
1233
1234 // misc/to kill/to-do
1235
1236
1237 ////////////////////////////////////////////////////////////////////////////////
1238
1239 /* We won't be needing this... */
1240 uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser * prs) {
1241 #if MS_WANT_PARSER
1242 if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
1243 Notify(PSTR("\r\nRead (With parser)\r\n"), 0x80);
1244 Notify(PSTR("---------\r\n"), 0x80);
1245
1246 CommandBlockWrapper cbw = CommandBlockWrapper();
1247
1248 cbw.dCBWSignature = MASS_CBW_SIGNATURE;
1249 cbw.dCBWTag = ++dCBWTag;
1250 cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks);
1251 cbw.bmCBWFlags = MASS_CMD_DIR_IN,
1252 cbw.bmCBWLUN = lun;
1253 cbw.bmCBWCBLength = 10;
1254
1255 cbw.CBWCB[0] = SCSI_CMD_READ_10;
1256 cbw.CBWCB[8] = blocks;
1257 cbw.CBWCB[2] = ((addr >> 24) & 0xff);
1258 cbw.CBWCB[3] = ((addr >> 16) & 0xff);
1259 cbw.CBWCB[4] = ((addr >> 8) & 0xff);
1260 cbw.CBWCB[5] = (addr & 0xff);
1261
1262 return HandleSCSIError(Transaction(&cbw, bsize, prs, 1));
1263 #else
1264 return MASS_ERR_NOT_IMPLEMENTED;
1265 #endif
1266 }
Imprint / Impressum