/** * Media control, for spindle motor and media tray or door. * This includes CDROM, TAPE and anything with a media loader. * * @param lun Logical Unit Number * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media * @return 0 on success */ uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) { Notify(PSTR("\r\nMediaCTL\r\n"), 0x80); Notify(PSTR("-----------------\r\n"), 0x80); SetCurLUN(lun); uint8_t rcode = MASS_ERR_UNIT_NOT_READY; if (bAddress) { CommandBlockWrapper cbw; cbw.dCBWSignature = MASS_CBW_SIGNATURE; cbw.dCBWTag = ++dCBWTag; cbw.dCBWDataTransferLength = 0; cbw.bmCBWFlags = MASS_CMD_DIR_OUT; cbw.bmCBWLUN = lun; cbw.bmCBWCBLength = 6; for (uint8_t i = 0; i < 16; i++) cbw.CBWCB[i] = 0; cbw.CBWCB[0] = SCSI_CMD_START_STOP_UNIT; cbw.CBWCB[1] = lun << 5; cbw.CBWCB[4] = ctl & 0x03; rcode = HandleSCSIError(Transaction(&cbw, 0, NULL, 0)); } return rcode; }
/** * For driver use only. Used during Driver Init * * @param lun Logical Unit Number * @param bsize * @param buf * @return */ uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) { Notify(PSTR("\r\nInquiry\r\n"), 0x80); Notify(PSTR("---------\r\n"), 0x80); CommandBlockWrapper cbw; SetCurLUN(lun); cbw.dCBWSignature = MASS_CBW_SIGNATURE; cbw.dCBWTag = ++dCBWTag; cbw.dCBWDataTransferLength = bsize; cbw.bmCBWFlags = MASS_CMD_DIR_IN; cbw.bmCBWLUN = lun; cbw.bmCBWCBLength = 6; for (uint8_t i = 0; i < 16; i++) cbw.CBWCB[i] = 0; cbw.CBWCB[0] = SCSI_CMD_INQUIRY; cbw.CBWCB[1] = lun << 5; cbw.CBWCB[4] = bsize; uint8_t rc = HandleSCSIError(Transaction(&cbw, bsize, buf, 0)); #if 0 if (!rc) { printf("LUN %i `", lun); for (int i = 8; i < 36; i++) printf("%c", buf[i]); printf("'\r\nQualifier %1.1X ", (buf[0]&0xE0) >> 5); printf("Device type %2.2X ", buf[0]&0x1f); printf("RMB %1.1X ", buf[1]&0x80 >> 7); printf("SSCS% 1.1X ", buf[5]&0x80 >> 7); uint8_t sv = buf[2]; printf("SCSI version %2.2X\r\nDevice conforms to ", sv); switch (sv) { case 0: printf("No specific"); break; /* case 1: printf(""); break; */ case 2: printf("ANSI 2"); break; case 3: printf("ANSI INCITS 301-1997 (SPC)"); break; case 4: printf("ANSI INCITS 351-2001 (SPC-2)"); break; case 5: printf("ANSI INCITS 408-2005 (SPC-4)"); break; case 6: printf("T10/1731-D (SPC-4)"); break; default: printf("unknown"); } printf(" standards.\r\n"); }
/** * Media control, for spindle motor and media tray or door. * This includes CDROM, TAPE and anything with a media loader. * * @param lun Logical Unit Number * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media * @return 0 on success */ uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) { Notify(PSTR("\r\nMediaCTL\r\n"), 0x80); Notify(PSTR("-----------------\r\n"), 0x80); uint8_t rcode = MASS_ERR_UNIT_NOT_READY; if(bAddress) { CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0); rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT); } else { SetCurLUN(lun); } return rcode; }
/** * Write data to media * * @param lun Logical Unit Number * @param addr LBA address on media to write * @param bsize size of a block (we should probably use the cached size) * @param blocks how many blocks to write * @param buf memory that contains the data to write * @return 0 on success */ uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) { if (!LUNOk[lun]) return MASS_ERR_NO_MEDIA; if (!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED; Notify(PSTR("\r\nWrite LUN:\t"), 0x80); D_PrintHex<uint8_t > (lun, 0x90); //printf("LUN=%i LBA=%8.8X BLOCKS=%i SIZE=%i\r\n", lun, addr, blocks, bsize); Notify(PSTR("\r\nLBA:\t\t"), 0x90); D_PrintHex<uint32_t > (addr, 0x90); Notify(PSTR("\r\nblocks:\t\t"), 0x90); D_PrintHex<uint8_t > (blocks, 0x90); Notify(PSTR("\r\nblock size:\t"), 0x90); D_PrintHex<uint16_t > (bsize, 0x90); Notify(PSTR("\r\n---------\r\n"), 0x80); //MediaCTL(lun, 0x01); CommandBlockWrapper cbw; again: cbw.dCBWSignature = MASS_CBW_SIGNATURE; cbw.dCBWTag = ++dCBWTag; cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks); cbw.bmCBWFlags = MASS_CMD_DIR_OUT; cbw.bmCBWLUN = lun; cbw.bmCBWCBLength = 10; for (uint8_t i = 0; i < 16; i++) cbw.CBWCB[i] = 0; cbw.CBWCB[0] = SCSI_CMD_WRITE_10; cbw.CBWCB[1] = lun << 5; cbw.CBWCB[2] = ((addr >> 24) & 0xff); cbw.CBWCB[3] = ((addr >> 16) & 0xff); cbw.CBWCB[4] = ((addr >> 8) & 0xff); cbw.CBWCB[5] = (addr & 0xff); cbw.CBWCB[8] = 1; SetCurLUN(lun); uint8_t er = HandleSCSIError(Transaction(&cbw, bsize, (void*)buf, 0)); if (er == MASS_ERR_WRITE_STALL) { MediaCTL(lun, 1); delay(150); if (!TestUnitReady(lun)) goto again; } return er; }
/** * Lock or Unlock the tray or door on device. * Caution: Some devices with buggy firmware will lock up. * * @param lun Logical Unit Number * @param lock 1 to lock, 0 to unlock * @return */ uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) { Notify(PSTR("\r\nLockMedia\r\n"), 0x80); Notify(PSTR("---------\r\n"), 0x80); CommandBlockWrapper cbw; SetCurLUN(lun); cbw.dCBWSignature = MASS_CBW_SIGNATURE; cbw.dCBWTag = ++dCBWTag; cbw.dCBWDataTransferLength = 0; cbw.bmCBWFlags = MASS_CMD_DIR_IN; cbw.bmCBWLUN = lun; cbw.bmCBWCBLength = 6; for (uint8_t i = 0; i < 16; i++) cbw.CBWCB[i] = 0; cbw.CBWCB[0] = SCSI_CMD_PREVENT_REMOVAL; cbw.CBWCB[4] = lock; return (HandleSCSIError(Transaction(&cbw, 0, NULL, 0))); }
/** * For driver use only. * * @param pcbw * @param buf_size * @param buf * @param flags * @return */ uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf #if MS_WANT_PARSER , uint8_t flags #endif ) { #if MS_WANT_PARSER uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength; printf("Transfersize %i\r\n", bytes); delay(1000); bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK; #else uint16_t bytes = buf_size; #endif bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN; uint8_t ret = 0; uint8_t usberr; CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles. SetCurLUN(pcbw->bmCBWLUN); ErrorMessage<uint32_t > (PSTR("CBW.dCBWTag"), pcbw->dCBWTag); while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1); ret = HandleUsbError(usberr, epDataOutIndex); //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex); if(ret) { ErrorMessage<uint8_t > (PSTR("============================ CBW"), ret); } else { if(bytes) { if(!write) { #if MS_WANT_PARSER if(callback) { uint8_t rbuf[bytes]; while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1); if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0); } else { #endif while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1); #if MS_WANT_PARSER } #endif ret = HandleUsbError(usberr, epDataInIndex); } else { while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1); ret = HandleUsbError(usberr, epDataOutIndex); } if(ret) { ErrorMessage<uint8_t > (PSTR("============================ DAT"), ret); } } } { bytes = sizeof (CommandStatusWrapper); int tries = 2; while(tries--) { while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1); if(!usberr) break; ClearEpHalt(epDataInIndex); if(tries) ResetRecovery(); } if(!ret) { Notify(PSTR("CBW:\t\tOK\r\n"), 0x80); Notify(PSTR("Data Stage:\tOK\r\n"), 0x80); } else { // Throw away csw, IT IS NOT OF ANY USE. ResetRecovery(); return ret; } ret = HandleUsbError(usberr, epDataInIndex); if(ret) { ErrorMessage<uint8_t > (PSTR("============================ CSW"), ret); } if(usberr == hrSUCCESS) { if(IsValidCSW(&csw, pcbw)) { //ErrorMessage<uint32_t > (PSTR("CSW.dCBWTag"), csw.dCSWTag); //ErrorMessage<uint8_t > (PSTR("bCSWStatus"), csw.bCSWStatus); //ErrorMessage<uint32_t > (PSTR("dCSWDataResidue"), csw.dCSWDataResidue); Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80); return csw.bCSWStatus; } else { // NOTE! Sometimes this is caused by the reported residue being wrong. // Get a different device. It isn't compliant, and should have never passed Q&A. // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter. // Other devices that exhibit this behavior exist in the wild too. // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk Notify(PSTR("Invalid CSW\r\n"), 0x80); ResetRecovery(); //return MASS_ERR_SUCCESS; return MASS_ERR_INVALID_CSW; } } } return ret; }