1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 |
- /**
- * \file
- *
- * \brief USB Device Mass Storage Class (MSC) interface.
- *
- * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved.
- *
- * \asf_license_start
- *
- * \page License
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. The name of Atmel may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * 4. This software may only be redistributed and used in connection with an
- * Atmel microcontroller product.
- *
- * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
- * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * \asf_license_stop
- *
- */
- /*
- * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
- */
-
- #ifdef ARDUINO_ARCH_SAM
-
- #include "conf_usb.h"
- #include "usb_protocol.h"
- #include "usb_protocol_msc.h"
- #include "spc_protocol.h"
- #include "sbc_protocol.h"
- #include "udd.h"
- #include "udc.h"
- #include "udi_msc.h"
- #include "ctrl_access.h"
- #include <string.h>
-
- #if ENABLED(SDSUPPORT)
-
- #ifndef UDI_MSC_NOTIFY_TRANS_EXT
- # define UDI_MSC_NOTIFY_TRANS_EXT()
- #endif
-
- /**
- * \ingroup udi_msc_group
- * \defgroup udi_msc_group_udc Interface with USB Device Core (UDC)
- *
- * Structures and functions required by UDC.
- *
- * @{
- */
- bool udi_msc_enable(void);
- void udi_msc_disable(void);
- bool udi_msc_setup(void);
- uint8_t udi_msc_getsetting(void);
-
- //! Global structure which contains standard UDI API for UDC
- UDC_DESC_STORAGE udi_api_t udi_api_msc = {
- .enable = udi_msc_enable,
- .disable = udi_msc_disable,
- .setup = udi_msc_setup,
- .getsetting = udi_msc_getsetting,
- .sof_notify = NULL,
- };
- //@}
-
-
- /**
- * \ingroup udi_msc_group
- * \defgroup udi_msc_group_internal Implementation of UDI MSC
- *
- * Class internal implementation
- * @{
- */
-
- //! Static block size for all memories
- #define UDI_MSC_BLOCK_SIZE 512L
-
- /**
- * \name Variables to manage SCSI requests
- */
- //@{
-
- //! Structure to receive a CBW packet
- UDC_BSS(4) static struct usb_msc_cbw udi_msc_cbw;
- //! Structure to send a CSW packet
- UDC_DATA(4) static struct usb_msc_csw udi_msc_csw =
- {.dCSWSignature = CPU_TO_BE32(USB_CSW_SIGNATURE) };
- //! Number of lun
- UDC_DATA(4) static uint8_t udi_msc_nb_lun = 0;
- //! Structure with current SCSI sense data
- UDC_BSS(4) static struct scsi_request_sense_data udi_msc_sense;
-
- /**
- * \name Variables to manage the background read/write SCSI commands
- */
- //@{
- //! True if an invalid CBW command has been detected
- static bool udi_msc_b_cbw_invalid = false;
- //! True if a transfer command must be processed
- static bool udi_msc_b_trans_req = false;
- //! True if it is a read command, else write command
- static bool udi_msc_b_read;
- //! Memory address to execute the command
- static uint32_t udi_msc_addr;
- //! Number of block to transfer
- static uint16_t udi_msc_nb_block;
- //! Signal end of transfer, if true
- volatile bool udi_msc_b_ack_trans = true;
- //! Status of transfer, aborted if true
- volatile bool udi_msc_b_abort_trans;
- //! Signal (re)init of transfer, if true (by reset/reconnect)
- volatile bool udi_msc_b_reset_trans = true;
- //@}
-
- //@}
-
-
- /**
- * \name Internal routines
- */
- //@{
-
- /**
- * \name Routines to process CBW packet
- */
- //@{
-
- /**
- * \brief Stall CBW request
- */
- static void udi_msc_cbw_invalid(void);
-
- /**
- * \brief Stall CSW request
- */
- static void udi_msc_csw_invalid(void);
-
- /**
- * \brief Links a callback and buffer on endpoint OUT reception
- *
- * Called by:
- * - enable interface
- * - at the end of previous command after sending the CSW
- */
- static void udi_msc_cbw_wait(void);
-
- /**
- * \brief Callback called after CBW reception
- * Called by UDD when a transfer is finished or aborted
- *
- * \param status UDD_EP_TRANSFER_OK, if transfer is finished
- * \param status UDD_EP_TRANSFER_ABORT, if transfer is aborted
- * \param nb_received number of data transfered
- */
- static void udi_msc_cbw_received(udd_ep_status_t status,
- iram_size_t nb_received, udd_ep_id_t ep);
-
- /**
- * \brief Function to check the CBW length and direction
- * Call it after SCSI command decode to check integrity of command
- *
- * \param alloc_len number of bytes that device want transfer
- * \param dir_flag Direction of transfer (USB_CBW_DIRECTION_IN/OUT)
- *
- * \retval true if the command can be processed
- */
- static bool udi_msc_cbw_validate(uint32_t alloc_len, uint8_t dir_flag);
- //@}
-
-
- /**
- * \name Routines to process small data packet
- */
- //@{
-
- /**
- * \brief Sends data on MSC IN endpoint
- * Called by SCSI command which must send a data to host followed by a CSW
- *
- * \param buffer Internal RAM buffer to send
- * \param buf_size Size of buffer to send
- */
- static void udi_msc_data_send(uint8_t * buffer, uint8_t buf_size);
-
- /**
- * \brief Callback called after data sent
- * It start CSW packet process
- *
- * \param status UDD_EP_TRANSFER_OK, if transfer finish
- * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted
- * \param nb_sent number of data transfered
- */
- static void udi_msc_data_sent(udd_ep_status_t status, iram_size_t nb_sent,
- udd_ep_id_t ep);
- //@}
-
-
- /**
- * \name Routines to process CSW packet
- */
- //@{
-
- /**
- * \brief Build CSW packet and send it
- *
- * Called at the end of SCSI command
- */
- static void udi_msc_csw_process(void);
-
- /**
- * \brief Sends CSW
- *
- * Called by #udi_msc_csw_process()
- * or UDD callback when endpoint halt is cleared
- */
- void udi_msc_csw_send(void);
-
- /**
- * \brief Callback called after CSW sent
- * It restart CBW reception.
- *
- * \param status UDD_EP_TRANSFER_OK, if transfer is finished
- * \param status UDD_EP_TRANSFER_ABORT, if transfer is aborted
- * \param nb_sent number of data transfered
- */
- static void udi_msc_csw_sent(udd_ep_status_t status, iram_size_t nb_sent,
- udd_ep_id_t ep);
- //@}
-
-
- /**
- * \name Routines manage sense data
- */
- //@{
-
- /**
- * \brief Reinitialize sense data.
- */
- static void udi_msc_clear_sense(void);
-
- /**
- * \brief Update sense data with new value to signal a fail
- *
- * \param sense_key Sense key
- * \param add_sense Additional Sense Code
- * \param lba LBA corresponding at error
- */
- static void udi_msc_sense_fail(uint8_t sense_key, uint16_t add_sense,
- uint32_t lba);
-
- /**
- * \brief Update sense data with new value to signal success
- */
- static void udi_msc_sense_pass(void);
-
- /**
- * \brief Update sense data to signal that memory is not present
- */
- static void udi_msc_sense_fail_not_present(void);
-
- /**
- * \brief Update sense data to signal that memory is busy
- */
- static void udi_msc_sense_fail_busy_or_change(void);
-
- /**
- * \brief Update sense data to signal a hardware error on memory
- */
- static void udi_msc_sense_fail_hardware(void);
-
- /**
- * \brief Update sense data to signal that memory is protected
- */
- static void udi_msc_sense_fail_protected(void);
-
- /**
- * \brief Update sense data to signal that CDB fields are not valid
- */
- static void udi_msc_sense_fail_cdb_invalid(void);
-
- /**
- * \brief Update sense data to signal that command is not supported
- */
- static void udi_msc_sense_command_invalid(void);
- //@}
-
-
- /**
- * \name Routines manage SCSI Commands
- */
- //@{
-
- /**
- * \brief Process SPC Request Sense command
- * Returns error information about last command
- */
- static void udi_msc_spc_requestsense(void);
-
- /**
- * \brief Process SPC Inquiry command
- * Returns information (name,version) about disk
- */
- static void udi_msc_spc_inquiry(void);
-
- /**
- * \brief Checks state of disk
- *
- * \retval true if disk is ready, otherwise false and updates sense data
- */
- static bool udi_msc_spc_testunitready_global(void);
-
- /**
- * \brief Process test unit ready command
- * Returns state of logical unit
- */
- static void udi_msc_spc_testunitready(void);
-
- /**
- * \brief Process prevent allow medium removal command
- */
- static void udi_msc_spc_prevent_allow_medium_removal(void);
-
- /**
- * \brief Process mode sense command
- *
- * \param b_sense10 Sense10 SCSI command, if true
- * \param b_sense10 Sense6 SCSI command, if false
- */
- static void udi_msc_spc_mode_sense(bool b_sense10);
-
- /**
- * \brief Process start stop command
- */
- static void udi_msc_sbc_start_stop(void);
-
- /**
- * \brief Process read capacity command
- */
- static void udi_msc_sbc_read_capacity(void);
-
- /**
- * \brief Process read10 or write10 command
- *
- * \param b_read Read transfer, if true,
- * \param b_read Write transfer, if false
- */
- static void udi_msc_sbc_trans(bool b_read);
- //@}
-
- //@}
-
-
- bool udi_msc_enable(void)
- {
- uint8_t lun;
- udi_msc_b_trans_req = false;
- udi_msc_b_cbw_invalid = false;
- udi_msc_b_ack_trans = true;
- udi_msc_b_reset_trans = true;
- udi_msc_nb_lun = get_nb_lun();
- if (0 == udi_msc_nb_lun)
- return false; // No lun available, then not authorize to enable interface
- udi_msc_nb_lun--;
- // Call application callback
- // to initialize memories or signal that interface is enabled
- if (!UDI_MSC_ENABLE_EXT())
- return false;
- // Load the medium on each LUN
- for (lun = 0; lun <= udi_msc_nb_lun; lun ++) {
- mem_unload(lun, false);
- }
- // Start MSC process by CBW reception
- udi_msc_cbw_wait();
- return true;
- }
-
-
- void udi_msc_disable(void)
- {
- udi_msc_b_trans_req = false;
- udi_msc_b_ack_trans = true;
- udi_msc_b_reset_trans = true;
- UDI_MSC_DISABLE_EXT();
- }
-
-
- bool udi_msc_setup(void)
- {
- if (Udd_setup_is_in()) {
- // Requests Interface GET
- if (Udd_setup_type() == USB_REQ_TYPE_CLASS) {
- // Requests Class Interface Get
- switch (udd_g_ctrlreq.req.bRequest) {
- case USB_REQ_MSC_GET_MAX_LUN:
- // Give the number of memories available
- if (1 != udd_g_ctrlreq.req.wLength)
- return false; // Error for USB host
- if (0 != udd_g_ctrlreq.req.wValue)
- return false;
- udd_g_ctrlreq.payload = &udi_msc_nb_lun;
- udd_g_ctrlreq.payload_size = 1;
- return true;
- }
- }
- }
- if (Udd_setup_is_out()) {
- // Requests Interface SET
- if (Udd_setup_type() == USB_REQ_TYPE_CLASS) {
- // Requests Class Interface Set
- switch (udd_g_ctrlreq.req.bRequest) {
- case USB_REQ_MSC_BULK_RESET:
- // Reset MSC interface
- if (0 != udd_g_ctrlreq.req.wLength)
- return false;
- if (0 != udd_g_ctrlreq.req.wValue)
- return false;
- udi_msc_b_cbw_invalid = false;
- udi_msc_b_trans_req = false;
- // Abort all tasks (transfer or clear stall wait) on endpoints
- udd_ep_abort(UDI_MSC_EP_OUT);
- udd_ep_abort(UDI_MSC_EP_IN);
- // Restart by CBW wait
- udi_msc_cbw_wait();
- return true;
- }
- }
- }
- return false; // Not supported request
- }
-
- uint8_t udi_msc_getsetting(void)
- {
- return 0; // MSC don't have multiple alternate setting
- }
-
-
- // ------------------------
- //------- Routines to process CBW packet
-
- static void udi_msc_cbw_invalid(void)
- {
- if (!udi_msc_b_cbw_invalid)
- return; // Don't re-stall endpoint if error reseted by setup
- udd_ep_set_halt(UDI_MSC_EP_OUT);
- // If stall cleared then re-stall it. Only Setup MSC Reset can clear it
- udd_ep_wait_stall_clear(UDI_MSC_EP_OUT, udi_msc_cbw_invalid);
- }
-
- static void udi_msc_csw_invalid(void)
- {
- if (!udi_msc_b_cbw_invalid)
- return; // Don't re-stall endpoint if error reseted by setup
- udd_ep_set_halt(UDI_MSC_EP_IN);
- // If stall cleared then re-stall it. Only Setup MSC Reset can clear it
- udd_ep_wait_stall_clear(UDI_MSC_EP_IN, udi_msc_csw_invalid);
- }
-
- static void udi_msc_cbw_wait(void)
- {
- // Register buffer and callback on OUT endpoint
- if (!udd_ep_run(UDI_MSC_EP_OUT, true,
- (uint8_t *) & udi_msc_cbw,
- sizeof(udi_msc_cbw),
- udi_msc_cbw_received)) {
- // OUT endpoint not available (halted), then wait a clear of halt.
- udd_ep_wait_stall_clear(UDI_MSC_EP_OUT, udi_msc_cbw_wait);
- }
- }
-
-
- static void udi_msc_cbw_received(udd_ep_status_t status,
- iram_size_t nb_received, udd_ep_id_t ep)
- {
- UNUSED(ep);
- // Check status of transfer
- if (UDD_EP_TRANSFER_OK != status) {
- // Transfer aborted
- // Now wait MSC setup reset to relaunch CBW reception
- return;
- }
- // Check CBW integrity:
- // transfer status/CBW length/CBW signature
- if ((sizeof(udi_msc_cbw) != nb_received)
- || (udi_msc_cbw.dCBWSignature !=
- CPU_TO_BE32(USB_CBW_SIGNATURE))) {
- // (5.2.1) Devices receiving a CBW with an invalid signature should stall
- // further traffic on the Bulk In pipe, and either stall further traffic
- // or accept and discard further traffic on the Bulk Out pipe, until
- // reset recovery.
- udi_msc_b_cbw_invalid = true;
- udi_msc_cbw_invalid();
- udi_msc_csw_invalid();
- return;
- }
- // Check LUN asked
- udi_msc_cbw.bCBWLUN &= USB_CBW_LUN_MASK;
- if (udi_msc_cbw.bCBWLUN > udi_msc_nb_lun) {
- // Bad LUN, then stop command process
- udi_msc_sense_fail_cdb_invalid();
- udi_msc_csw_process();
- return;
- }
- // Prepare CSW residue field with the size requested
- udi_msc_csw.dCSWDataResidue =
- le32_to_cpu(udi_msc_cbw.dCBWDataTransferLength);
-
- // Decode opcode
- switch (udi_msc_cbw.CDB[0]) {
- case SPC_REQUEST_SENSE:
- udi_msc_spc_requestsense();
- break;
-
- case SPC_INQUIRY:
- udi_msc_spc_inquiry();
- break;
-
- case SPC_MODE_SENSE6:
- udi_msc_spc_mode_sense(false);
- break;
- case SPC_MODE_SENSE10:
- udi_msc_spc_mode_sense(true);
- break;
-
- case SPC_TEST_UNIT_READY:
- udi_msc_spc_testunitready();
- break;
-
- case SBC_READ_CAPACITY10:
- udi_msc_sbc_read_capacity();
- break;
-
- case SBC_START_STOP_UNIT:
- udi_msc_sbc_start_stop();
- break;
-
- // Accepts request to support plug/plug in case of card reader
- case SPC_PREVENT_ALLOW_MEDIUM_REMOVAL:
- udi_msc_spc_prevent_allow_medium_removal();
- break;
-
- // Accepts request to support full format from Windows
- case SBC_VERIFY10:
- udi_msc_sense_pass();
- udi_msc_csw_process();
- break;
-
- case SBC_READ10:
- udi_msc_sbc_trans(true);
- break;
-
- case SBC_WRITE10:
- udi_msc_sbc_trans(false);
- break;
-
- default:
- udi_msc_sense_command_invalid();
- udi_msc_csw_process();
- break;
- }
- }
-
-
- static bool udi_msc_cbw_validate(uint32_t alloc_len, uint8_t dir_flag)
- {
- /*
- * The following cases should result in a phase error:
- * - Case 2: Hn < Di
- * - Case 3: Hn < Do
- * - Case 7: Hi < Di
- * - Case 8: Hi <> Do
- * - Case 10: Ho <> Di
- * - Case 13: Ho < Do
- */
- if (((udi_msc_cbw.bmCBWFlags ^ dir_flag) & USB_CBW_DIRECTION_IN)
- || (udi_msc_csw.dCSWDataResidue < alloc_len)) {
- udi_msc_sense_fail_cdb_invalid();
- udi_msc_csw_process();
- return false;
- }
-
- /*
- * The following cases should result in a stall and nonzero
- * residue:
- * - Case 4: Hi > Dn
- * - Case 5: Hi > Di
- * - Case 9: Ho > Dn
- * - Case 11: Ho > Do
- */
- return true;
- }
-
-
- // ------------------------
- //------- Routines to process small data packet
-
- static void udi_msc_data_send(uint8_t * buffer, uint8_t buf_size)
- {
- // Sends data on IN endpoint
- if (!udd_ep_run(UDI_MSC_EP_IN, true,
- buffer, buf_size, udi_msc_data_sent)) {
- // If endpoint not available, then exit process command
- udi_msc_sense_fail_hardware();
- udi_msc_csw_process();
- }
- }
-
-
- static void udi_msc_data_sent(udd_ep_status_t status, iram_size_t nb_sent,
- udd_ep_id_t ep)
- {
- UNUSED(ep);
- if (UDD_EP_TRANSFER_OK != status) {
- // Error protocol
- // Now wait MSC setup reset to relaunch CBW reception
- return;
- }
- // Update sense data
- udi_msc_sense_pass();
- // Update CSW
- udi_msc_csw.dCSWDataResidue -= nb_sent;
- udi_msc_csw_process();
- }
-
-
- // ------------------------
- //------- Routines to process CSW packet
-
- static void udi_msc_csw_process(void)
- {
- if (0 != udi_msc_csw.dCSWDataResidue) {
- // Residue not NULL
- // then STALL next request from USB host on corresponding endpoint
- if (udi_msc_cbw.bmCBWFlags & USB_CBW_DIRECTION_IN)
- udd_ep_set_halt(UDI_MSC_EP_IN);
- else
- udd_ep_set_halt(UDI_MSC_EP_OUT);
- }
- // Prepare and send CSW
- udi_msc_csw.dCSWTag = udi_msc_cbw.dCBWTag;
- udi_msc_csw.dCSWDataResidue = cpu_to_le32(udi_msc_csw.dCSWDataResidue);
- udi_msc_csw_send();
- }
-
-
- void udi_msc_csw_send(void)
- {
- // Sends CSW on IN endpoint
- if (!udd_ep_run(UDI_MSC_EP_IN, false,
- (uint8_t *) & udi_msc_csw,
- sizeof(udi_msc_csw),
- udi_msc_csw_sent)) {
- // Endpoint not available
- // then restart CSW sent when endpoint IN STALL will be cleared
- udd_ep_wait_stall_clear(UDI_MSC_EP_IN, udi_msc_csw_send);
- }
- }
-
-
- static void udi_msc_csw_sent(udd_ep_status_t status, iram_size_t nb_sent,
- udd_ep_id_t ep)
- {
- UNUSED(ep);
- UNUSED(status);
- UNUSED(nb_sent);
- // CSW is sent or not
- // In all case, restart process and wait CBW
- udi_msc_cbw_wait();
- }
-
-
- // ------------------------
- //------- Routines manage sense data
-
- static void udi_msc_clear_sense(void)
- {
- memset((uint8_t*)&udi_msc_sense, 0, sizeof(struct scsi_request_sense_data));
- udi_msc_sense.valid_reponse_code = SCSI_SENSE_VALID | SCSI_SENSE_CURRENT;
- udi_msc_sense.AddSenseLen = SCSI_SENSE_ADDL_LEN(sizeof(udi_msc_sense));
- }
-
- static void udi_msc_sense_fail(uint8_t sense_key, uint16_t add_sense,
- uint32_t lba)
- {
- udi_msc_clear_sense();
- udi_msc_csw.bCSWStatus = USB_CSW_STATUS_FAIL;
- udi_msc_sense.sense_flag_key = sense_key;
- udi_msc_sense.information[0] = lba >> 24;
- udi_msc_sense.information[1] = lba >> 16;
- udi_msc_sense.information[2] = lba >> 8;
- udi_msc_sense.information[3] = lba;
- udi_msc_sense.AddSenseCode = add_sense >> 8;
- udi_msc_sense.AddSnsCodeQlfr = add_sense;
- }
-
- static void udi_msc_sense_pass(void)
- {
- udi_msc_clear_sense();
- udi_msc_csw.bCSWStatus = USB_CSW_STATUS_PASS;
- }
-
-
- static void udi_msc_sense_fail_not_present(void)
- {
- udi_msc_sense_fail(SCSI_SK_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0);
- }
-
- static void udi_msc_sense_fail_busy_or_change(void)
- {
- udi_msc_sense_fail(SCSI_SK_UNIT_ATTENTION,
- SCSI_ASC_NOT_READY_TO_READY_CHANGE, 0);
- }
-
- static void udi_msc_sense_fail_hardware(void)
- {
- udi_msc_sense_fail(SCSI_SK_HARDWARE_ERROR,
- SCSI_ASC_NO_ADDITIONAL_SENSE_INFO, 0);
- }
-
- static void udi_msc_sense_fail_protected(void)
- {
- udi_msc_sense_fail(SCSI_SK_DATA_PROTECT, SCSI_ASC_WRITE_PROTECTED, 0);
- }
-
- static void udi_msc_sense_fail_cdb_invalid(void)
- {
- udi_msc_sense_fail(SCSI_SK_ILLEGAL_REQUEST,
- SCSI_ASC_INVALID_FIELD_IN_CDB, 0);
- }
-
- static void udi_msc_sense_command_invalid(void)
- {
- udi_msc_sense_fail(SCSI_SK_ILLEGAL_REQUEST,
- SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, 0);
- }
-
-
- // ------------------------
- //------- Routines manage SCSI Commands
-
- static void udi_msc_spc_requestsense(void)
- {
- uint8_t length = udi_msc_cbw.CDB[4];
-
- // Can't send more than sense data length
- if (length > sizeof(udi_msc_sense))
- length = sizeof(udi_msc_sense);
-
- if (!udi_msc_cbw_validate(length, USB_CBW_DIRECTION_IN))
- return;
- // Send sense data
- udi_msc_data_send((uint8_t*)&udi_msc_sense, length);
- }
-
-
- static void udi_msc_spc_inquiry(void)
- {
- uint8_t length, i;
- UDC_DATA(4)
- // Constant inquiry data for all LUNs
- static struct scsi_inquiry_data udi_msc_inquiry_data = {
- .pq_pdt = SCSI_INQ_PQ_CONNECTED | SCSI_INQ_DT_DIR_ACCESS,
- .version = SCSI_INQ_VER_SPC,
- .flags3 = SCSI_INQ_RSP_SPC2,
- .addl_len = SCSI_INQ_ADDL_LEN(sizeof(struct scsi_inquiry_data)),
- .vendor_id = {UDI_MSC_GLOBAL_VENDOR_ID},
- .product_rev = {UDI_MSC_GLOBAL_PRODUCT_VERSION},
- };
-
- length = udi_msc_cbw.CDB[4];
-
- // Can't send more than inquiry data length
- if (length > sizeof(udi_msc_inquiry_data))
- length = sizeof(udi_msc_inquiry_data);
-
- if (!udi_msc_cbw_validate(length, USB_CBW_DIRECTION_IN))
- return;
- if ((0 != (udi_msc_cbw.CDB[1] & (SCSI_INQ_REQ_EVPD | SCSI_INQ_REQ_CMDT)))
- || (0 != udi_msc_cbw.CDB[2])) {
- // CMDT and EPVD bits are not at 0
- // PAGE or OPERATION CODE fields are not empty
- // = No standard inquiry asked
- udi_msc_sense_fail_cdb_invalid(); // Command is unsupported
- udi_msc_csw_process();
- return;
- }
-
- udi_msc_inquiry_data.flags1 = mem_removal(udi_msc_cbw.bCBWLUN) ?
- SCSI_INQ_RMB : 0;
-
- //* Fill product ID field
- // Copy name in product id field
- memcpy(udi_msc_inquiry_data.product_id,
- mem_name(udi_msc_cbw.bCBWLUN)+1, // To remove first '"'
- sizeof(udi_msc_inquiry_data.product_id));
-
- // Search end of name '/0' or '"'
- i = 0;
- while (sizeof(udi_msc_inquiry_data.product_id) != i) {
- if ((0 == udi_msc_inquiry_data.product_id[i])
- || ('"' == udi_msc_inquiry_data.product_id[i])) {
- break;
- }
- i++;
- }
- // Padding with space char
- while (sizeof(udi_msc_inquiry_data.product_id) != i) {
- udi_msc_inquiry_data.product_id[i] = ' ';
- i++;
- }
-
- // Send inquiry data
- udi_msc_data_send((uint8_t *) & udi_msc_inquiry_data, length);
- }
-
-
- static bool udi_msc_spc_testunitready_global(void)
- {
- switch (mem_test_unit_ready(udi_msc_cbw.bCBWLUN)) {
- case CTRL_GOOD:
- return true; // Don't change sense data
- case CTRL_BUSY:
- udi_msc_sense_fail_busy_or_change();
- break;
- case CTRL_NO_PRESENT:
- udi_msc_sense_fail_not_present();
- break;
- case CTRL_FAIL:
- default:
- udi_msc_sense_fail_hardware();
- break;
- }
- return false;
- }
-
-
- static void udi_msc_spc_testunitready(void)
- {
- if (udi_msc_spc_testunitready_global()) {
- // LUN ready, then update sense data with status pass
- udi_msc_sense_pass();
- }
- // Send status in CSW packet
- udi_msc_csw_process();
- }
-
-
- static void udi_msc_spc_mode_sense(bool b_sense10)
- {
- // Union of all mode sense structures
- union sense_6_10 {
- struct {
- struct scsi_mode_param_header6 header;
- struct spc_control_page_info_execpt sense_data;
- } s6;
- struct {
- struct scsi_mode_param_header10 header;
- struct spc_control_page_info_execpt sense_data;
- } s10;
- };
-
- uint8_t data_sense_lgt;
- uint8_t mode;
- uint8_t request_lgt;
- uint8_t wp;
- struct spc_control_page_info_execpt *ptr_mode;
- UDC_BSS(4) static union sense_6_10 sense;
-
- // Clear all fields
- memset(&sense, 0, sizeof(sense));
-
- // Initialize process
- if (b_sense10) {
- request_lgt = udi_msc_cbw.CDB[8];
- ptr_mode = &sense.s10.sense_data;
- data_sense_lgt = sizeof(struct scsi_mode_param_header10);
- } else {
- request_lgt = udi_msc_cbw.CDB[4];
- ptr_mode = &sense.s6.sense_data;
- data_sense_lgt = sizeof(struct scsi_mode_param_header6);
- }
-
- // No Block descriptor
-
- // Fill page(s)
- mode = udi_msc_cbw.CDB[2] & SCSI_MS_MODE_ALL;
- if ((SCSI_MS_MODE_INFEXP == mode)
- || (SCSI_MS_MODE_ALL == mode)) {
- // Informational exceptions control page (from SPC)
- ptr_mode->page_code =
- SCSI_MS_MODE_INFEXP;
- ptr_mode->page_length =
- SPC_MP_INFEXP_PAGE_LENGTH;
- ptr_mode->mrie =
- SPC_MP_INFEXP_MRIE_NO_SENSE;
- data_sense_lgt += sizeof(struct spc_control_page_info_execpt);
- }
- // Can't send more than mode sense data length
- if (request_lgt > data_sense_lgt)
- request_lgt = data_sense_lgt;
- if (!udi_msc_cbw_validate(request_lgt, USB_CBW_DIRECTION_IN))
- return;
-
- // Fill mode parameter header length
- wp = (mem_wr_protect(udi_msc_cbw.bCBWLUN)) ? SCSI_MS_SBC_WP : 0;
-
- if (b_sense10) {
- sense.s10.header.mode_data_length =
- cpu_to_be16((data_sense_lgt - 2));
- //sense.s10.header.medium_type = 0;
- sense.s10.header.device_specific_parameter = wp;
- //sense.s10.header.block_descriptor_length = 0;
- } else {
- sense.s6.header.mode_data_length = data_sense_lgt - 1;
- //sense.s6.header.medium_type = 0;
- sense.s6.header.device_specific_parameter = wp;
- //sense.s6.header.block_descriptor_length = 0;
- }
-
- // Send mode sense data
- udi_msc_data_send((uint8_t *) & sense, request_lgt);
- }
-
-
- static void udi_msc_spc_prevent_allow_medium_removal(void)
- {
- uint8_t prevent = udi_msc_cbw.CDB[4];
- if (0 == prevent) {
- udi_msc_sense_pass();
- } else {
- udi_msc_sense_fail_cdb_invalid(); // Command is unsupported
- }
- udi_msc_csw_process();
- }
-
-
- static void udi_msc_sbc_start_stop(void)
- {
- bool start = 0x1 & udi_msc_cbw.CDB[4];
- bool loej = 0x2 & udi_msc_cbw.CDB[4];
- if (loej) {
- mem_unload(udi_msc_cbw.bCBWLUN, !start);
- }
- udi_msc_sense_pass();
- udi_msc_csw_process();
- }
-
-
- static void udi_msc_sbc_read_capacity(void)
- {
- UDC_BSS(4) static struct sbc_read_capacity10_data udi_msc_capacity;
-
- if (!udi_msc_cbw_validate(sizeof(udi_msc_capacity),
- USB_CBW_DIRECTION_IN))
- return;
-
- // Get capacity of LUN
- switch (mem_read_capacity(udi_msc_cbw.bCBWLUN,
- &udi_msc_capacity.max_lba)) {
- case CTRL_GOOD:
- break;
- case CTRL_BUSY:
- udi_msc_sense_fail_busy_or_change();
- udi_msc_csw_process();
- return;
- case CTRL_NO_PRESENT:
- udi_msc_sense_fail_not_present();
- udi_msc_csw_process();
- return;
- default:
- udi_msc_sense_fail_hardware();
- udi_msc_csw_process();
- return;
- }
-
- // Format capacity data
- udi_msc_capacity.block_len = CPU_TO_BE32(UDI_MSC_BLOCK_SIZE);
- udi_msc_capacity.max_lba = cpu_to_be32(udi_msc_capacity.max_lba);
- // Send the corresponding sense data
- udi_msc_data_send((uint8_t *) & udi_msc_capacity,
- sizeof(udi_msc_capacity));
- }
-
-
- static void udi_msc_sbc_trans(bool b_read)
- {
- uint32_t trans_size;
-
- if (!b_read) {
- // Write operation then check Write Protect
- if (mem_wr_protect(udi_msc_cbw.bCBWLUN)) {
- // Write not authorized
- udi_msc_sense_fail_protected();
- udi_msc_csw_process();
- return;
- }
- }
- // Read/Write command fields (address and number of block)
- MSB0(udi_msc_addr) = udi_msc_cbw.CDB[2];
- MSB1(udi_msc_addr) = udi_msc_cbw.CDB[3];
- MSB2(udi_msc_addr) = udi_msc_cbw.CDB[4];
- MSB3(udi_msc_addr) = udi_msc_cbw.CDB[5];
- MSB(udi_msc_nb_block) = udi_msc_cbw.CDB[7];
- LSB(udi_msc_nb_block) = udi_msc_cbw.CDB[8];
-
- // Compute number of byte to transfer and valid it
- trans_size = (uint32_t) udi_msc_nb_block *UDI_MSC_BLOCK_SIZE;
- if (!udi_msc_cbw_validate(trans_size,
- (b_read) ? USB_CBW_DIRECTION_IN :
- USB_CBW_DIRECTION_OUT))
- return;
-
- // Record transfer request to do it in a task and not under interrupt
- udi_msc_b_read = b_read;
- udi_msc_b_trans_req = true;
- UDI_MSC_NOTIFY_TRANS_EXT();
- }
-
-
- bool udi_msc_process_trans(void)
- {
- Ctrl_status status;
-
- if (!udi_msc_b_trans_req)
- return false; // No Transfer request to do
- udi_msc_b_trans_req = false;
- udi_msc_b_reset_trans = false;
-
- // Start transfer
- if (udi_msc_b_read) {
- status = memory_2_usb(udi_msc_cbw.bCBWLUN, udi_msc_addr,
- udi_msc_nb_block);
- } else {
- status = usb_2_memory(udi_msc_cbw.bCBWLUN, udi_msc_addr,
- udi_msc_nb_block);
- }
-
- // Check if transfer is aborted by reset
- if (udi_msc_b_reset_trans) {
- udi_msc_b_reset_trans = false;
- return true;
- }
-
- // Check status of transfer
- switch (status) {
- case CTRL_GOOD:
- udi_msc_sense_pass();
- break;
- case CTRL_BUSY:
- udi_msc_sense_fail_busy_or_change();
- break;
- case CTRL_NO_PRESENT:
- udi_msc_sense_fail_not_present();
- break;
- default:
- case CTRL_FAIL:
- udi_msc_sense_fail_hardware();
- break;
- }
- // Send status of transfer in CSW packet
- udi_msc_csw_process();
- return true;
- }
-
-
- static void udi_msc_trans_ack(udd_ep_status_t status, iram_size_t n,
- udd_ep_id_t ep)
- {
- UNUSED(ep);
- UNUSED(n);
- // Update variable to signal the end of transfer
- udi_msc_b_abort_trans = (UDD_EP_TRANSFER_OK != status) ? true : false;
- udi_msc_b_ack_trans = true;
- }
-
-
- bool udi_msc_trans_block(bool b_read, uint8_t * block, iram_size_t block_size,
- void (*callback) (udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep))
- {
- if (!udi_msc_b_ack_trans)
- return false; // No possible, transfer on going
-
- // Start transfer Internal RAM<->USB line
- udi_msc_b_ack_trans = false;
- if (!udd_ep_run((b_read) ? UDI_MSC_EP_IN : UDI_MSC_EP_OUT,
- false,
- block,
- block_size,
- (NULL == callback) ? udi_msc_trans_ack :
- callback)) {
- udi_msc_b_ack_trans = true;
- return false;
- }
- if (NULL == callback) {
- while (!udi_msc_b_ack_trans);
- if (udi_msc_b_abort_trans) {
- return false;
- }
- udi_msc_csw.dCSWDataResidue -= block_size;
- return (!udi_msc_b_abort_trans);
- }
- udi_msc_csw.dCSWDataResidue -= block_size;
- return true;
- }
-
- //@}
-
- #endif // SDSUPPORT
-
- #endif // ARDUINO_ARCH_SAM
|