USB_Host_Shield_2.0/masstorage.h
Kristian Sloth Lauszus 6c36eed239 Renamed USB class to USBHost, so it does not collide with the one defined in the Arduino Zero core
Also renamed the struct UsbDevice to UsbDeviceDefinition
2016-01-19 16:34:45 +01:00

571 lines
19 KiB
C++

/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(__MASSTORAGE_H__)
#define __MASSTORAGE_H__
// Cruft removal, makes driver smaller, faster.
#ifndef MS_WANT_PARSER
#define MS_WANT_PARSER 0
#endif
#include "Usb.h"
#define bmREQ_MASSOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
#define bmREQ_MASSIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
// Mass Storage Subclass Constants
#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00 // De facto use
#define MASS_SUBCLASS_RBC 0x01
#define MASS_SUBCLASS_ATAPI 0x02 // MMC-5 (ATAPI)
#define MASS_SUBCLASS_OBSOLETE1 0x03 // Was QIC-157
#define MASS_SUBCLASS_UFI 0x04 // Specifies how to interface Floppy Disk Drives to USB
#define MASS_SUBCLASS_OBSOLETE2 0x05 // Was SFF-8070i
#define MASS_SUBCLASS_SCSI 0x06 // SCSI Transparent Command Set
#define MASS_SUBCLASS_LSDFS 0x07 // Specifies how host has to negotiate access before trying SCSI
#define MASS_SUBCLASS_IEEE1667 0x08
// Mass Storage Class Protocols
#define MASS_PROTO_CBI 0x00 // CBI (with command completion interrupt)
#define MASS_PROTO_CBI_NO_INT 0x01 // CBI (without command completion interrupt)
#define MASS_PROTO_OBSOLETE 0x02
#define MASS_PROTO_BBB 0x50 // Bulk Only Transport
#define MASS_PROTO_UAS 0x62
// Request Codes
#define MASS_REQ_ADSC 0x00
#define MASS_REQ_GET 0xFC
#define MASS_REQ_PUT 0xFD
#define MASS_REQ_GET_MAX_LUN 0xFE
#define MASS_REQ_BOMSR 0xFF // Bulk-Only Mass Storage Reset
#define MASS_CBW_SIGNATURE 0x43425355
#define MASS_CSW_SIGNATURE 0x53425355
#define MASS_CMD_DIR_OUT 0 // (0 << 7)
#define MASS_CMD_DIR_IN 0x80 //(1 << 7)
/*
* Reference documents from T10 (http://www.t10.org)
* SCSI Primary Commands - 3 (SPC-3)
* SCSI Block Commands - 2 (SBC-2)
* Multi-Media Commands - 5 (MMC-5)
*/
/* Group 1 commands (CDB's here are should all be 6-bytes) */
#define SCSI_CMD_TEST_UNIT_READY 0x00
#define SCSI_CMD_REQUEST_SENSE 0x03
#define SCSI_CMD_FORMAT_UNIT 0x04
#define SCSI_CMD_READ_6 0x08
#define SCSI_CMD_WRITE_6 0x0A
#define SCSI_CMD_INQUIRY 0x12
#define SCSI_CMD_MODE_SELECT_6 0x15
#define SCSI_CMD_MODE_SENSE_6 0x1A
#define SCSI_CMD_START_STOP_UNIT 0x1B
#define SCSI_CMD_PREVENT_REMOVAL 0x1E
/* Group 2 Commands (CDB's here are 10-bytes) */
#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23
#define SCSI_CMD_READ_CAPACITY_10 0x25
#define SCSI_CMD_READ_10 0x28
#define SCSI_CMD_WRITE_10 0x2A
#define SCSI_CMD_SEEK_10 0x2B
#define SCSI_CMD_ERASE_10 0x2C
#define SCSI_CMD_WRITE_AND_VERIFY_10 0x2E
#define SCSI_CMD_VERIFY_10 0x2F
#define SCSI_CMD_SYNCHRONIZE_CACHE 0x35
#define SCSI_CMD_WRITE_BUFFER 0x3B
#define SCSI_CMD_READ_BUFFER 0x3C
#define SCSI_CMD_READ_SUBCHANNEL 0x42
#define SCSI_CMD_READ_TOC 0x43
#define SCSI_CMD_READ_HEADER 0x44
#define SCSI_CMD_PLAY_AUDIO_10 0x45
#define SCSI_CMD_GET_CONFIGURATION 0x46
#define SCSI_CMD_PLAY_AUDIO_MSF 0x47
#define SCSI_CMD_PLAY_AUDIO_TI 0x48
#define SCSI_CMD_PLAY_TRACK_REL_10 0x49
#define SCSI_CMD_GET_EVENT_STATUS 0x4A
#define SCSI_CMD_PAUSE_RESUME 0x4B
#define SCSI_CMD_READ_DISC_INFORMATION 0x51
#define SCSI_CMD_READ_TRACK_INFORMATION 0x52
#define SCSI_CMD_RESERVE_TRACK 0x53
#define SCSI_CMD_SEND_OPC_INFORMATION 0x54
#define SCSI_CMD_MODE_SELECT_10 0x55
#define SCSI_CMD_REPAIR_TRACK 0x58
#define SCSI_CMD_MODE_SENSE_10 0x5A
#define SCSI_CMD_CLOSE_TRACK_SESSION 0x5B
#define SCSI_CMD_READ_BUFFER_CAPACITY 0x5C
#define SCSI_CMD_SEND_CUE_SHEET 0x5D
/* Group 5 Commands (CDB's here are 12-bytes) */
#define SCSI_CMD_REPORT_LUNS 0xA0
#define SCSI_CMD_BLANK 0xA1
#define SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2
#define SCSI_CMD_SEND_KEY 0xA3
#define SCSI_CMD_REPORT_KEY 0xA4
#define SCSI_CMD_PLAY_AUDIO_12 0xA5
#define SCSI_CMD_LOAD_UNLOAD 0xA6
#define SCSI_CMD_SET_READ_AHEAD 0xA7
#define SCSI_CMD_READ_12 0xA8
#define SCSI_CMD_PLAY_TRACK_REL_12 0xA9
#define SCSI_CMD_WRITE_12 0xAA
#define SCSI_CMD_READ_MEDIA_SERIAL_12 0xAB
#define SCSI_CMD_GET_PERFORMANCE 0xAC
#define SCSI_CMD_READ_DVD_STRUCTURE 0xAD
#define SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5
#define SCSI_CMD_SET_STREAMING 0xB6
#define SCSI_CMD_READ_MSF 0xB9
#define SCSI_CMD_SET_SPEED 0xBB
#define SCSI_CMD_MECHANISM_STATUS 0xBD
#define SCSI_CMD_READ_CD 0xBE
#define SCSI_CMD_SEND_DISC_STRUCTURE 0xBF
/* Vendor-unique Commands, included for completeness */
#define SCSI_CMD_CD_PLAYBACK_STATUS 0xC4 /* SONY unique */
#define SCSI_CMD_PLAYBACK_CONTROL 0xC9 /* SONY unique */
#define SCSI_CMD_READ_CDDA 0xD8 /* Vendor unique */
#define SCSI_CMD_READ_CDXA 0xDB /* Vendor unique */
#define SCSI_CMD_READ_ALL_SUBCODES 0xDF /* Vendor unique */
/* SCSI error codes */
#define SCSI_S_NOT_READY 0x02
#define SCSI_S_MEDIUM_ERROR 0x03
#define SCSI_S_ILLEGAL_REQUEST 0x05
#define SCSI_S_UNIT_ATTENTION 0x06
#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21
#define SCSI_ASC_MEDIA_CHANGED 0x28
#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A
/* USB error codes */
#define MASS_ERR_SUCCESS 0x00
#define MASS_ERR_PHASE_ERROR 0x02
#define MASS_ERR_UNIT_NOT_READY 0x03
#define MASS_ERR_UNIT_BUSY 0x04
#define MASS_ERR_STALL 0x05
#define MASS_ERR_CMD_NOT_SUPPORTED 0x06
#define MASS_ERR_INVALID_CSW 0x07
#define MASS_ERR_NO_MEDIA 0x08
#define MASS_ERR_BAD_LBA 0x09
#define MASS_ERR_MEDIA_CHANGED 0x0A
#define MASS_ERR_DEVICE_DISCONNECTED 0x11
#define MASS_ERR_UNABLE_TO_RECOVER 0x12 // Reset recovery error
#define MASS_ERR_INVALID_LUN 0x13
#define MASS_ERR_WRITE_STALL 0x14
#define MASS_ERR_READ_NAKS 0x15
#define MASS_ERR_WRITE_NAKS 0x16
#define MASS_ERR_WRITE_PROTECTED 0x17
#define MASS_ERR_NOT_IMPLEMENTED 0xFD
#define MASS_ERR_GENERAL_SCSI_ERROR 0xFE
#define MASS_ERR_GENERAL_USB_ERROR 0xFF
#define MASS_ERR_USER 0xA0 // For subclasses to define their own error codes
#define MASS_TRANS_FLG_CALLBACK 0x01 // Callback is involved
#define MASS_TRANS_FLG_NO_STALL_CHECK 0x02 // STALL condition is not checked
#define MASS_TRANS_FLG_NO_PHASE_CHECK 0x04 // PHASE_ERROR is not checked
#define MASS_MAX_ENDPOINTS 3
struct Capacity {
uint8_t data[8];
//uint32_t dwBlockAddress;
//uint32_t dwBlockLength;
} __attribute__((packed));
struct BASICCDB {
uint8_t Opcode;
unsigned unused : 5;
unsigned LUN : 3;
uint8_t info[12];
} __attribute__((packed));
typedef BASICCDB BASICCDB_t;
struct CDB6 {
uint8_t Opcode;
unsigned LBAMSB : 5;
unsigned LUN : 3;
uint8_t LBAHB;
uint8_t LBALB;
uint8_t AllocationLength;
uint8_t Control;
public:
CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :
Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1f), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)),
AllocationLength(_AllocationLength), Control(_Control) {
}
CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) :
Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0),
AllocationLength(_AllocationLength), Control(_Control) {
}
} __attribute__((packed));
typedef CDB6 CDB6_t;
struct CDB10 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned LUN : 3;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t Misc2;
uint8_t ALC_MB;
uint8_t ALC_LB;
uint8_t Control;
public:
CDB10(uint8_t _Opcode, uint8_t _LUN) :
Opcode(_Opcode), Service_Action(0), LUN(_LUN),
LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0),
Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) {
}
CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :
Opcode(_Opcode), Service_Action(0), LUN(_LUN),
LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)),
Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) {
}
} __attribute__((packed));
typedef CDB10 CDB10_t;
struct CDB12 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned Misc : 3;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Control;
} __attribute__((packed));
typedef CDB12 CDB12_t;
struct CDB_LBA32_16 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned Misc : 3;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t A_M_M_MB;
uint8_t A_M_M_LB;
uint8_t A_M_L_MB;
uint8_t A_M_L_LB;
uint8_t ALC_M_MB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Misc2;
uint8_t Control;
} __attribute__((packed));
struct CDB_LBA64_16 {
uint8_t Opcode;
uint8_t Misc;
uint8_t LBA_M_M_MB;
uint8_t LBA_M_M_LB;
uint8_t LBA_M_L_MB;
uint8_t LBA_M_L_LB;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t ALC_M_MB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Misc2;
uint8_t Control;
} __attribute__((packed));
struct InquiryResponse {
uint8_t DeviceType : 5;
uint8_t PeripheralQualifier : 3;
unsigned Reserved : 7;
unsigned Removable : 1;
uint8_t Version;
unsigned ResponseDataFormat : 4;
unsigned HISUP : 1;
unsigned NormACA : 1;
unsigned TrmTsk : 1;
unsigned AERC : 1;
uint8_t AdditionalLength;
//uint8_t Reserved3[2];
unsigned PROTECT : 1;
unsigned Res : 2;
unsigned ThreePC : 1;
unsigned TPGS : 2;
unsigned ACC : 1;
unsigned SCCS : 1;
unsigned ADDR16 : 1;
unsigned R1 : 1;
unsigned R2 : 1;
unsigned MCHNGR : 1;
unsigned MULTIP : 1;
unsigned VS : 1;
unsigned ENCSERV : 1;
unsigned BQUE : 1;
unsigned SoftReset : 1;
unsigned CmdQue : 1;
unsigned Reserved4 : 1;
unsigned Linked : 1;
unsigned Sync : 1;
unsigned WideBus16Bit : 1;
unsigned WideBus32Bit : 1;
unsigned RelAddr : 1;
uint8_t VendorID[8];
uint8_t ProductID[16];
uint8_t RevisionID[4];
} __attribute__((packed));
struct CommandBlockWrapperBase {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
public:
CommandBlockWrapperBase() {
}
CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :
dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {
}
} __attribute__((packed));
struct CommandBlockWrapper : public CommandBlockWrapperBase {
struct {
uint8_t bmCBWLUN : 4;
uint8_t bmReserved1 : 4;
};
struct {
uint8_t bmCBWCBLength : 4;
uint8_t bmReserved2 : 4;
};
uint8_t CBWCB[16];
public:
// All zeroed.
CommandBlockWrapper() :
CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
}
// Generic Wrap, CDB zeroed.
CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :
CommandBlockWrapperBase(tag, xflen, flgs),
bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
// Type punning can cause optimization problems and bugs.
// Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this.
//(((BASICCDB_t *) CBWCB)->LUN) = cmd;
BASICCDB_t *x = reinterpret_cast<BASICCDB_t *>(CBWCB);
x->LUN = cmd;
}
// Wrap for CDB of 6
CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) :
CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {
memcpy(&CBWCB, cdb, 6);
}
// Wrap for CDB of 10
CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) :
CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {
memcpy(&CBWCB, cdb, 10);
}
} __attribute__((packed));
struct CommandStatusWrapper {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));
struct RequestSenseResponce {
uint8_t bResponseCode;
uint8_t bSegmentNumber;
uint8_t bmSenseKey : 4;
uint8_t bmReserved : 1;
uint8_t bmILI : 1;
uint8_t bmEOM : 1;
uint8_t bmFileMark : 1;
uint8_t Information[4];
uint8_t bAdditionalLength;
uint8_t CmdSpecificInformation[4];
uint8_t bAdditionalSenseCode;
uint8_t bAdditionalSenseQualifier;
uint8_t bFieldReplaceableUnitCode;
uint8_t SenseKeySpecific[3];
} __attribute__((packed));
class BulkOnly : public USBDeviceConfig, public UsbConfigXtracter {
protected:
static const uint8_t epDataInIndex; // DataIn endpoint index
static const uint8_t epDataOutIndex; // DataOUT endpoint index
static const uint8_t epInterruptInIndex; // InterruptIN endpoint index
USBHost *pUsb;
uint8_t bAddress;
uint8_t bConfNum; // configuration number
uint8_t bIface; // interface value
uint8_t bNumEP; // total number of EP in the configuration
uint32_t qNextPollTime; // next poll time
bool bPollEnable; // poll enable flag
EpInfo epInfo[MASS_MAX_ENDPOINTS];
uint32_t dCBWTag; // Tag
//uint32_t dCBWDataTransferLength; // Data Transfer Length
uint8_t bLastUsbError; // Last USB error
uint8_t bMaxLUN; // Max LUN
uint8_t bTheLUN; // Active LUN
uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors
uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits
bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.
bool WriteOk[MASS_MAX_SUPPORTED_LUN];
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
// Additional Initialization Method for Subclasses
virtual uint8_t OnInit() {
return 0;
};
public:
BulkOnly(USBHost *p);
uint8_t GetLastUsbError() {
return bLastUsbError;
};
uint8_t GetbMaxLUN() {
return bMaxLUN; // Max LUN
}
uint8_t GetbTheLUN() {
return bTheLUN; // Active LUN
}
bool WriteProtected(uint8_t lun);
uint8_t MediaCTL(uint8_t lun, uint8_t ctl);
uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);
uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs);
uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf);
uint8_t LockMedia(uint8_t lun, uint8_t lock);
bool LUNIsGood(uint8_t lun);
uint32_t GetCapacity(uint8_t lun);
uint16_t GetSectorSize(uint8_t lun);
// USBDeviceConfig implementation
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
uint8_t Release();
uint8_t Poll();
virtual uint8_t GetAddress() {
return bAddress;
};
// UsbConfigXtracter implementation
void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
virtual bool DEVCLASSOK(uint8_t klass) {
return (klass == USB_CLASS_MASS_STORAGE);
}
uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
private:
uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf);
uint8_t TestUnitReady(uint8_t lun);
uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf);
uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf);
uint8_t GetMaxLUN(uint8_t *max_lun);
uint8_t SetCurLUN(uint8_t lun);
void Reset();
uint8_t ResetRecovery();
uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);
void ClearAllEP();
void CheckMedia();
bool CheckLUN(uint8_t lun);
uint8_t Page3F(uint8_t lun);
bool IsValidCBW(uint8_t size, uint8_t *pcbw);
bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);
bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw);
uint8_t ClearEpHalt(uint8_t index);
#if MS_WANT_PARSER
uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags);
#endif
uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf);
uint8_t HandleUsbError(uint8_t error, uint8_t index);
uint8_t HandleSCSIError(uint8_t status);
};
#endif // __MASSTORAGE_H__