/**
 * Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact information
 * -------------------
 *
 * Circuits At Home, LTD
 * Web      :  http://www.circuitsathome.com
 * e-mail   :  support@circuitsathome.com
 */

#pragma once

// 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

  USB *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(USB *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);
};
