m-stack updated. assume it works but will test later.
--- a/cdcacm.c Sat Feb 18 16:14:32 2017 +0000
+++ b/cdcacm.c Sat Feb 18 16:25:38 2017 +0000
@@ -118,7 +118,7 @@
}
#endif
-void
+int8_t
app_set_line_coding_callback(uint8_t interface, const struct cdc_line_coding *coding)
{
}
--- a/cdcacm.h Sat Feb 18 16:14:32 2017 +0000
+++ b/cdcacm.h Sat Feb 18 16:25:38 2017 +0000
@@ -44,7 +44,7 @@
void app_set_comm_feature_callback(uint8_t, bool, bool);
void app_clear_comm_feature_callback(uint8_t, bool, bool);
int8_t app_get_comm_feature_callback(uint8_t, bool *, bool *);
-void app_set_line_coding_callback(uint8_t, const struct cdc_line_coding *);
+int8_t app_set_line_coding_callback(uint8_t, const struct cdc_line_coding *);
int8_t app_get_line_coding_callback(uint8_t, struct cdc_line_coding *);
int8_t app_set_control_line_state_callback(uint8_t, bool, bool);
int8_t app_send_break_callback(uint8_t, uint16_t);
--- a/usb/include/usb.h Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/include/usb.h Sat Feb 18 16:25:38 2017 +0000
@@ -522,14 +522,38 @@
*
* This is the callback function type expected to be passed to @p
* usb_start_receive_ep0_data_stage() and @p usb_send_data_stage().
- * Callback functions will be called by the stack when the event for which
- * they are registered occurs.
*
- * @param transfer_ok @a true if transaction completed successfully, or
- * @a false if there was an error
+ * For OUT transfers with data, the callback function will be called by the
+ * stack when the data stage of the transfer completes. For this type of
+ * transfer, the callback can return -1 or 0:
+ * -1: The application has rejected the data. This will cause the stack to
+ * send a STALL to the host.
+ * 0: The application has accepted and processed the data. This will cause
+ * the stack to send a zero-length packet (indicating success) to be sent
+ * as the status stage.
+ *
+ * For OUT transfers without data (which do not have a data stage), the
+ * callback function will be called when the status stage of the transfer
+ * completes; the return value is ignored.
+ *
+ * For IN transfers, the callback function will be called by the stack when
+ * the status stage of the transfer completes; the return value is ignored.
+ *
+ * Note that the functionality is different for different types of transfers.
+ * The callback gets called at the place which which has the most meaning for
+ * each type of transfer.
+ *
+ * @param data_ok True if transaction(s) completed successfully, or
+ * false if there was an error
* @param context A pointer to application-provided context data
+ *
+ * @returns
+ * For OUT transfers with data, -1 or 0 can be returned as described.
+ *
+ * For IN transfers and for OUT transfers without data, the return value
+ * is ignored.
*/
-typedef void (*usb_ep0_data_stage_callback)(bool transfer_ok, void *context);
+typedef int8_t (*usb_ep0_data_stage_callback)(bool data_ok, void *context);
/** @brief Start the data stage of an OUT control transfer
*
--- a/usb/include/usb_cdc.h Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/include/usb_cdc.h Sat Feb 18 16:25:38 2017 +0000
@@ -356,8 +356,12 @@
* the idle setting.
* @param data_multiplexed_state Whether to set the data multiplexed
* state. True = clear the multiplexed state.
+ *
+ * @returns
+ * Return 0 if the request can be handled or -1 if it cannot. Returning -1
+ * will cause STALL to be returned to the host.
*/
-extern void CDC_SET_COMM_FEATURE_CALLBACK(uint8_t interface,
+extern int8_t CDC_SET_COMM_FEATURE_CALLBACK(uint8_t interface,
bool idle_setting,
bool data_multiplexed_state);
#endif
@@ -376,10 +380,14 @@
* the idle setting.
* @param data_multiplexed_state Whether to clear the data multiplexed
* state. True = clear the multiplexed state.
+ *
+ * @returns
+ * Return 0 if the request can be handled or -1 if it cannot. Returning -1
+ * will cause STALL to be returned to the host.
*/
-extern void CDC_CLEAR_COMM_FEATURE_CALLBACK(uint8_t interface,
- bool idle_setting,
- bool data_multiplexed_state);
+extern int8_t CDC_CLEAR_COMM_FEATURE_CALLBACK(uint8_t interface,
+ bool idle_setting,
+ bool data_multiplexed_state);
#endif
#ifdef CDC_GET_COMM_FEATURE_CALLBACK
@@ -416,8 +424,11 @@
* @param interface The interface for which the command is intended
* @param coding The new line coding set by the host
*
+ * @returns
+ * Return 0 if the request can be handled or -1 if it cannot. Returning -1
+ * will cause STALL to be returned to the host.
*/
-extern void CDC_SET_LINE_CODING_CALLBACK(uint8_t interface,
+extern int8_t CDC_SET_LINE_CODING_CALLBACK(uint8_t interface,
const struct cdc_line_coding *coding);
#endif
--- a/usb/include/usb_hid.h Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/include/usb_hid.h Sat Feb 18 16:25:38 2017 +0000
@@ -186,8 +186,8 @@
* does not dereference this pointer.
*
* @returns
- * Return 0 if the request can be handled or -1 if it cannot. Returning -1
- * will cause STALL to be returned to the host.
+ * Return the length of the report or -1 if the request is invalid.
+ * Returning -1 will cause STALL to be returned to the host.
*/
extern int16_t HID_GET_REPORT_CALLBACK(uint8_t interface, uint8_t report_type,
uint8_t report_id, const void **report,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/include/usb_msc.h Sat Feb 18 16:25:38 2017 +0000
@@ -0,0 +1,798 @@
+/*
+ * M-Stack USB Mass Storage Device Class Structures
+ * Copyright (C) 2014 Alan Ott <alan@signal11.us>
+ * Copyright (C) 2014 Signal 11 Software
+ *
+ * 2014-06-04
+ *
+ * M-Stack is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, version 3; or the Apache License, version 2.0
+ * as published by the Apache Software Foundation. If you have purchased a
+ * commercial license for this software from Signal 11 Software, your
+ * commerical license superceeds the information in this header.
+ *
+ * M-Stack 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You should have received a copy of the Apache License, verion 2.0 along
+ * with this software. If not, see <http://www.apache.org/licenses/>.
+ */
+
+#ifndef USB_MSC_H__
+#define USB_MSC_H__
+
+/** @file usb_msc.h
+ * @brief USB Mass Storage Class Enumerations and Structures
+ * @defgroup public_api Public API
+ */
+
+/** @addtogroup public_api
+ * @{
+ */
+
+#include <stdint.h>
+#include "usb_config.h"
+
+#if defined(__XC16__) || defined(__XC32__)
+#pragma pack(push, 1)
+#elif __XC8
+#else
+#error "Compiler not supported"
+#endif
+
+/** @defgroup msc_items USB MSC Class Enumerations and Descriptors
+ * @brief Packet structs, constants, and callback functions implementing
+ * the "Universal Serial Bus Mass Storage Class", revision 1.4, and the
+ * "Universal Serial Bus Mass Storage Class Bulk-Only Transport", revision
+ * 1.0.
+ *
+ * An indespensible reference is Jan Axelson's "USB Mass Storage" book.
+ * The major value in this book is the real-life perspective regarding
+ * which parts of the SCSI standards are actually used in USB devices, what
+ * they are used for, and how the SCSI commands and terminology map onto USB
+ * devices.
+ *
+ * Document sections listed in the comments are prefixed with the document
+ * they reference as follows:\n
+ * \b MSCO: USB Mass Storage Class, revision 1.4\n
+ * \b BOT: USB Mass Storage Class Bulk-Only Transport, revision 1.0\n
+ * \b Axelson: Jan Axelson's "USB Mass Storage" book\n
+ *
+ * For more information, see the above referenced document, available from
+ * http://www.usb.org .
+ * @addtogroup msc_items
+ * @{
+ */
+
+#define MSC_DEVICE_CLASS 0x08 /* http://www.usb.org/developers/defined_class */
+#define MSC_SCSI_TRANSPARENT_COMMAND_SET_SUBCLASS 0x06
+/* Many of the subclass codes (MSCO: sec 2) are omitted here. Get in
+ * contact with Signal 11 if you need something specific. */
+
+#define MSC_PROTOCOL_CODE_BBB 0x50 /* Bulk-Only */
+/* Many of the protocol codes (MSCO: sec 3) are omitted here. Get in
+ * contact with Signal 11 if you need something specific. */
+
+#if MSC_MAX_LUNS_PER_INTERFACE <= 8
+ typedef uint8_t msc_lun_mask_t;
+#else
+ typedef uint16_t msc_lun_mask_t;
+#endif
+
+
+/** MSC Class Requests
+ *
+ * These are the class requests needed for MSC Bulk-Only Transport (MSCO:
+ * sec 4, table 3). Others are omitted. Get in contact with Signal 11 if
+ * you need something specific.
+ */
+enum MSCRequests {
+ MSC_GET_MAX_LUN = 0xfe,
+ MSC_BULK_ONLY_MASS_STORAGE_RESET = 0xff,
+};
+
+/** MSC Command Block Status Values
+ *
+ * See BOT, 5.2
+ */
+enum MSCStatus {
+ MSC_STATUS_PASSED = 0, /* Success */
+ MSC_STATUS_FAILED = 1,
+ MSC_STATUS_PHASE_ERROR = 2,
+};
+
+/** MSC Command Block Flags (Direction is the only used bit)
+ *
+ * See BOT, 5.1
+ */
+enum MSCDirection {
+ MSC_DIRECTION_OUT = 0x0,
+ MSC_DIRECTION_IN_BIT = 0x80,
+};
+
+/** MSC Bulk-Only Data Command Block Wrapper
+ *
+ * See BOT, 5.1
+ */
+struct msc_command_block_wrapper {
+ uint32_t dCBWSignature; /* Set to 0x43425355 */
+ uint32_t dCBWTag;
+ uint32_t dCBWDataTransferLength; /** Data to be transfered */
+ uint8_t bmCBWFlags; /** bit 0x80=data-in, 0x00=data-out */
+ uint8_t bCBWLUN; /**< Lower 4 bits only */
+ uint8_t bCBWCBLength ; /**< Lower 4 bits only; length of CBWCB */
+ uint8_t CBWCB[16];
+};
+
+/** MSC Bulk-Only Data Command Block Wrapper
+ *
+ * See BOT, 5.2
+ */
+struct msc_command_status_wrapper {
+ uint32_t dCSWSignature; /**< Set to 0x53425355 */
+ uint32_t dCSWTag;
+ uint32_t dCSWDataResidue;
+ uint8_t bCSWStatus; /**< @see enum MSCStatus */
+};
+
+/* SCSI Definitions and Structures */
+
+enum MSCSCSICommands {
+ MSC_SCSI_FORMAT_UNIT = 0x04,
+ MSC_SCSI_INQUIRY = 0x12,
+ MSC_SCSI_MODE_SELECT_6 = 0x15,
+ MSC_SCSI_MODE_SELECT_10 = 0x55,
+ MSC_SCSI_MODE_SENSE_6 = 0x1a,
+ MSC_SCSI_MODE_SENSE_10 = 0x5a,
+ MSC_SCSI_START_STOP_UNIT = 0x1b,
+ MSC_SCSI_READ_6 = 0x08,
+ MSC_SCSI_READ_10 = 0x28,
+ MSC_SCSI_READ_CAPACITY_10 = 0x25,
+ MSC_SCSI_REPORT_LUNS = 0xa0,
+ MSC_SCSI_REQUEST_SENSE = 0x03,
+ MSC_SCSI_SEND_DIAGNOSTIC = 0x1d,
+ MSC_SCSI_TEST_UNIT_READY = 0x00,
+ MSC_SCSI_VERIFY = 0x2f,
+ MSC_SCSI_WRITE_6 = 0x0a,
+ MSC_SCSI_WRITE_10 = 0x2a,
+};
+
+struct msc_scsi_inquiry_command {
+ uint8_t operation_code; /* 0x12 */
+ uint8_t evpd; /* bit 0 only */
+ uint8_t page_code;
+ uint16_t allocation_length;
+ uint8_t control;
+};
+
+struct msc_scsi_request_sense_command {
+ uint8_t operation_code; /* 0x3 */
+ uint8_t desc; /* bit 0 only */
+ uint8_t reserved[2];
+ uint8_t allocation_length;
+ uint8_t control;
+};
+
+struct msc_scsi_mode_sense_6_command {
+ uint8_t operation_code; /* 0x1a */
+ uint8_t dbd_reserved;
+ uint8_t pc_page_code; /* bits 6-7: PC, bits 0-5: Page Code */
+ uint8_t subpage_code;
+ uint8_t allocation_length;
+ uint8_t control;
+};
+
+struct msc_scsi_start_stop_unit {
+ uint8_t operation_code;
+ uint8_t immed; /* Bit 0 only */
+ uint8_t reserved[2];
+ uint8_t command; /* bit 0: start, bit 1: LOEJ, bits 4-7: power cond */
+ uint8_t control;
+};
+
+struct msc_scsi_read_10_command {
+ uint8_t opcode;
+ uint8_t unused_flags;
+ uint32_t logical_block_address;
+ uint8_t group_number;
+ uint16_t transfer_length;
+ uint8_t control_flags;
+};
+
+struct msc_scsi_write_10_command {
+ uint8_t operation_code;
+ uint8_t wrprotect_flags;
+ uint32_t logical_block_address;
+ uint8_t group_number;
+ uint16_t transfer_length;
+ uint8_t control;
+};
+
+enum MSCSCSIVersion {
+ MSC_SCSI_SPC_VERSION_2 = 4,
+ MSC_SCSI_SPC_VERSION_3 = 5,
+};
+
+
+struct scsi_inquiry_response {
+ uint8_t peripheral; /**< Set to 0x0 */
+ uint8_t rmb; /**< 0x80 for removable media */
+ uint8_t version; /**< enum MSCSCSIVersion */
+ uint8_t response_data_format; /**< Set to 0x2 */
+ uint8_t additional_length; /**< 4 less than sizeof(inquiry_response) */
+ uint8_t unused[3]; /**< Not used in our implementation */
+ char vendor[8]; /**< ASCII, no zero-termination */
+ char product[16]; /**< ASCII, no zero-termination */
+ char revision[4]; /**< ASCII, no zero-termination */
+};
+
+struct scsi_capacity_response {
+ uint32_t last_block;
+ uint32_t block_length;
+};
+
+enum SCSISenseResponseCode {
+ SCSI_SENSE_CURRENT_ERRORS = 0x70,
+ SCSI_SENSE_DEFERRED_ERRORS = 0x71,
+ SCSI_SENSE_INFORMATION_VALID = 0x80,
+};
+
+enum SCSISenseFlags {
+ SCSI_SENSE_FILEMARK = 0x80,
+ SCSI_SENSE_EOM = 0x40,
+ SCSI_SENSE_ILI = 0x20,
+ SCSI_SENSE_KEY_MASK = 0x0f,
+};
+
+struct scsi_mode_sense_response {
+ uint8_t mode_data_length; /**< sizeof(scsi_mode_sense_response) - 1 */
+ uint8_t medium_type; /**< Set to 0x00 for SBC devices */
+ uint8_t device_specific_parameter; /**< set to 0x80 for write-protect, else 0x0 */
+ uint8_t block_descriptor_length; /**< Set to 0x0 */
+};
+
+enum SCSISenseKeys {
+ SCSI_SENSE_KEY_NOT_READY = 0x2,
+ SCSI_SENSE_KEY_MEDIUM_ERROR = 0x3,
+ SCSI_SENSE_KEY_ILLEGAL_REQUEST = 0x5,
+ SCSI_SENSE_KEY_UNIT_ATTENTION = 0x6,
+ SCSI_SENSE_KEY_DATA_PROTECT = 0x7,
+};
+
+enum SCSIAdditionalSenseCodes {
+ /* ILLEGAL_REQUEST */
+ SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE = 0x21,
+ SCSI_ASC_INVALID_COMMAND_OPERATION_CODE = 0x20,
+ SCSI_ASC_INVALID_FIELD_IN_COMMAND_PACKET = 0x24,
+ SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED = 0x25,
+
+ /* MEDIUM_ERROR */
+ SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x03,
+ SCSI_ASC_UNRECOVERED_READ_ERROR = 0x11,
+
+ /* UNIT_ATTENTION */
+ SCSI_ASC_WRITE_ERROR = 0x0c,
+ SCSI_ASC_MEDIUM_NOT_PRESENT = 0x3a,
+
+ /* WRITE_DATA_PROTECT */
+ SCSI_ASC_WRITE_PROTECTED = 0x27,
+};
+
+struct scsi_sense_response {
+ uint8_t response_code; /**< 0x70=current_errors, 0x71=deferred_errors
+ * 0x80 bit = information_valid */
+ uint8_t obsolete;
+ uint8_t flags; /**< Bitmask of SCSISenseFlags OR'd with one from SCSISenseKeys */
+ uint32_t information;
+ uint8_t additional_sense_length;
+ uint32_t command_specific_information;
+ uint8_t additional_sense_code; /**< SCSIAdditionalSenseCodes */
+ uint8_t additional_sense_code_qualifier;
+ uint8_t field_replaceable_unit_code;
+ uint8_t sense_key_specific[3];
+ /* Additional, vendor-specific sense data goes here. */
+};
+
+#if defined(__XC16__) || defined(__XC32__)
+#pragma pack(pop)
+#elif __XC8
+#else
+#error "Compiler not supported"
+#endif
+
+
+/** MSC Application States
+ *
+ * The states the transport goes through.
+ */
+enum MSCApplicationStates {
+ MSC_IDLE,
+ MSC_DATA_TRANSPORT_IN, /**< Next transactions will be data sent */
+ MSC_DATA_TRANSPORT_OUT, /**< Next transactions will be data received */
+ MSC_STALL, /**< Next transaction needs to stall */
+ MSC_CSW, /**< Next transaction will contain the CSW */
+ MSC_NEEDS_RESET_RECOVERY, /**< Reset recovery is required */
+};
+
+/** Return codes used by application callbacks.
+ *
+ * Success is defined to be 0 and all failures are < 0.
+ */
+enum MSCReturnCodes {
+ MSC_SUCCESS = 0,
+ MSC_ERROR_MEDIUM_NOT_PRESENT = -1, /**< The medium is not physically present */
+ MSC_ERROR_INVALID_LUN = -2, /**< The LUN is out of range */
+ MSC_ERROR_INVALID_ADDRESS = -3, /**< The LBA address is out of range */
+ MSC_ERROR_WRITE_PROTECTED = -4, /**< The medium is write protected */
+ MSC_ERROR_READ = -5, /**< Error while reading the medium */
+ MSC_ERROR_WRITE = -6, /**< Error while writing the medium */
+ MSC_ERROR_MEDIUM = -7, /**< Unspecified medium error */
+};
+
+/* Forward declare struct msc_application_data to enable the following:
+ * 1. The struct be used by the callback,
+ * 2. The callback can be used by the struct. */
+struct msc_application_data;
+
+/** @brief MSC Transmission Complete Callback
+ *
+ * This is the callback function type expected to be passed to @p
+ * msc_start_send_to_host(). Callback functions will be called by the stack
+ * when the transmission of a block of data has completed.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param transfer_ok @a true if transaction completed successfully, or
+ * @a false if there was an error
+ */
+typedef void (*msc_completion_callback) (struct msc_application_data *app_data,
+ bool transfer_ok);
+
+/** MSC Applicaiton Data
+ *
+ * The application shall provide one of these structures for each interface
+ * when the MSC class is initialized and keep it as a persistent object for
+ * the lifetime of the application. Global variables work well for this.
+ *
+ * The Application shall initialize the variables in the first section. The
+ * variables in the second section are used by the MSC class to keep track of
+ * the state of the connection.
+ *
+ * The application will pass this structure to the MSC class whenever it needs
+ * the MSC class to process data for an interface.
+ */
+struct msc_application_data {
+ /* Application should initialize the following: */
+ uint8_t interface;
+ uint8_t max_lun; /**< The maximally numbered LUN. One-less than the number of LUNs. */
+ uint8_t in_endpoint;
+ uint8_t out_endpoint;
+ uint8_t in_endpoint_size; /**< Size in bytes for IN endpoint */
+ msc_lun_mask_t media_is_removable_mask; /**< bitmask, one bit for each LUN */
+ const char *vendor; /**< SCSI-assigned vendor. Pointer to global or constant. */
+ const char *product; /**< Pointer to global or constant. */
+ const char *revision; /**< Pointer to global or constant. */
+
+ /* MSC Class handler will initialize and use the following. The
+ * applicaiton should ignore these: */
+ uint8_t state; /**< enum MSCApplicationStates */
+ uint32_t current_tag;
+ /* Error-reporting codes */
+ uint8_t sense_key;
+ uint8_t additional_sense_code;
+ /* CSW fields */
+ uint32_t residue;
+ uint8_t status; /**< enum MSCStatus */
+ /* READ command handling fields */
+ uint32_t requested_bytes; /* Bytes requested by SCSI */
+ uint32_t requested_bytes_cbw; /* Bytes requested by the MSC (CBW) */
+ uint32_t transferred_bytes;
+ /* Block size for each LUN */
+ uint32_t block_size[MSC_MAX_LUNS_PER_INTERFACE];
+
+ /* Asynchronous transmit/receive data */
+ union {
+ const uint8_t *tx_buf; /**< Data to be sent to the host. */
+ uint8_t *rx_buf; /**< Data received from the host. */
+ };
+ uint16_t tx_len_remaining; /**< TX data remaining in the current block */
+#ifdef MSC_WRITE_SUPPORT
+ uint8_t *rx_buf_cur; /**< Current position in the RX buffer */
+ size_t rx_buf_len; /**< Length of the application's block RX buffer */
+ /* Endpoint buffer management. */
+ uint8_t out_ep_missed_transactions; /**< Number of out transactions not processed */
+#endif
+ msc_completion_callback operation_complete_callback;
+};
+
+/** Initialize the MSC class for all interfaces
+ *
+ * Initialize all instances of the MSC class. Call this function with an
+ * array containing an @class msc_application_data for each
+ * MSC interface. The @p app_data pointer should point to an array of valid
+ * @class msc_application_data structures which have been filled out properly.
+ *
+ * The array should be valid for the lifetime of the application. Global
+ * variables work well for this.
+ *
+ * @param app_data Pointer to an array of application data structures
+ * @param count Number of structures (size of array) in app_data
+ *
+ * @returns
+ * Returns 0 on success, or -1 if some of the data is invalid. On a -1
+ * return, assume that none of the interfaces have been initialized
+ * properly and fail.
+ */
+uint8_t msc_init(struct msc_application_data *app_data, uint8_t count);
+
+/** Process MSC Setup Request
+ *
+ * Process a setup request which has been unhandled as if it is potentially
+ * an MSC setup request. This function will then call appropriate callbacks
+ * into the appliction if the setup packet is one recognized by the MSC
+ * specification.
+ *
+ * @param setup A setup packet to handle
+ *
+ * @returns
+ * Returns 0 if the setup packet could be processed or -1 if it could not.
+ */
+int8_t process_msc_setup_request(const struct setup_packet *setup);
+
+/** Clear Halt on MSC Endpoint
+ *
+ * Notify the MSC class that a CLEAR_FEATURE ENDPOINT_HALT has been
+ * received for an MSC endpoint.
+ *
+ * @param endpoint_num The endpoint number affected
+ * @param direction The endpoint direction affected. 0=out, 1=in
+ *
+ */
+void msc_clear_halt(uint8_t endpoint_num, uint8_t direction);
+
+/** Notify of a transaction completing on the Data-IN endpoint
+ *
+ * Notify the MSC class that an IN transaction has completed on the Data-IN
+ * endpoint. If using interrupts, call this function from the
+ * @p IN_TRANSACTION_COMPLETE_CALLBACK.
+ *
+ * This function will not block.
+ *
+ * @param endpoint_num The endpoint number affected
+ */
+void msc_in_transaction_complete(uint8_t endpoint_num);
+
+/** Notify of a transaction completing on the Data-OUT endpoint
+ *
+ * Notify the MSC class that an OUT transaction has completed on the
+ * Data-OUT endpoint. This function will process the data which is
+ * available on endpoint @p endpoint_num and will re-arm the endpoint when
+ * appropriate.
+ *
+ * If using interrupts, call this function from the
+ * @p OUT_TRANSACTION_CALLBACK.
+ *
+ * This function will not block.
+ *
+ * @param endpoint_num The endpoint number affected
+ */
+void msc_out_transaction_complete(uint8_t endpoint_num);
+
+/** Send data read from the medium to the host
+ *
+ * Start transmission of data that has been read from the storage medium to
+ * the host. This function simply starts the transmission and returns control
+ * immediately to the caller without blocking. When the transmission has
+ * completed, the @p completion_callback will be called. The application
+ * should call this function as soon as data is available from the storage
+ * medium, and should not call it again until the @p completion_callback has
+ * been called. For example, if 8 blocks are requested by the host, the
+ * application could read a single block, call @p msc_start_send_to_host(),
+ * wait for the @p completion_callback to be called, then call
+ * @p msc_start_send_to_host() with the second block, and so on..
+ *
+ * This function does not block.
+ *
+ * @p completion_callback will be called from interrupt context and must
+ * not block.
+ *
+ * @p len needs to be multiple of the IN endpoint size for all calls to
+ * this function except the last in response to an @p MSC_READ() callback.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param data Pointer to the data to send.
+ * @param len Data length in bytes. Must be a multiple of
+ * the IN endpoint size.
+ * @param completion_callback Pointer to a function which is called when
+ * the transmission has completed.
+ *
+ * @returns
+ * Returns 0 if the transmission could be started or -1 if it could not.
+ */
+uint8_t msc_start_send_to_host(struct msc_application_data *app_data,
+ const uint8_t *data, uint16_t len,
+ msc_completion_callback completion_callback);
+
+/** Notify the MSC class that a Read Operation has Completed
+ *
+ * Tell the MSC class that a read operation has been completed with either
+ * success or failure.
+ *
+ * In the case of success, pass true to @p passed, indicating that the data
+ * requested by an @p MSC_READ() callback has all been passed to the MSC
+ * class using @p msc_start_send_to_host(). This will cause the transfer to
+ * complete successfully.
+ *
+ * If the read failed, pass false to @p passed. This will cause a SCSI
+ * MEDIUM_ERROR to be returned to the host.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param passed Whether the read operation completed successfully
+ */
+void msc_notify_read_operation_complete(
+ struct msc_application_data *app_data,
+ bool passed);
+
+#ifdef MSC_WRITE_SUPPORT
+/** Notify Write Data Handled
+ *
+ * Tell the MSC class that a block of data provided to a write callback
+ * has been handled, and that the buffer is no longer in use by the
+ * applicaiton. The MSC stack will now provide the next block to be written
+ * into the same buffer and call the callback provided to @p
+ * MSC_START_WRITE().
+ *
+ * Note that calling this function does not necessarily indicate that the
+ * data was successfully written to the medium. It only indicates that the
+ * application has received the buffer and dealt with it, and requests that
+ * the MSC stack put the next block of data into the buffer. It is possible
+ * the application does not know that a write operation has failed (or will
+ * fail) until the end of the write operation.
+ *
+ * Note that if there are OUT transfers pending (and waiting because the
+ * application is using the buffer), and if the application's buffer is
+ * sufficiently small, then the MSC stack could potentially call the @p
+ * msc_completion_callback() from this function. With small buffers, this is
+ * a typical case.
+ *
+ * If there was an error in handling the data, call @p
+ * msc_notify_write_operation_complete() instead to cancel the transport.
+ *
+ * @param app_data Pointer to application data for this interface.
+ */
+void msc_notify_write_data_handled(struct msc_application_data *app_data);
+
+/** Notify Write Operation Complete
+ *
+ * Tell the MSC class that a write operation has completed, either with
+ * full success, with partial success, or with failure. The MSC stack can now
+ * complete the data transport and report the status to the host.
+ *
+ * In the case of a complete success, set @p passed to true, and set @p
+ * bytes_processed to the number of bytes which were asked for and written.
+ *
+ * In the case of a failure or a partial success, set @p passed to false
+ * and set @p bytes_processed to the number of bytes which were acutally
+ * written successfully.
+ *
+ * Pass true as @p passed if the write succeeded or false if it failed.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param passed Whether the write operation fully completed
+ * successfully, writing all desired data.
+ * @param bytes_processed The number of bytes successfully written. This may
+ * be fewer than the number of bytes asked for by the
+ * host.
+ */
+void msc_notify_write_operation_complete(struct msc_application_data *app_data,
+ bool passed,
+ uint32_t bytes_processed);
+#endif
+
+/** MSC Bulk-Only Mass Storage Reset callback
+ *
+ * The MSC class will call this function when a Bulk-Only Mass Storage Reset
+ * request has been received from the host. This function should perform
+ * a reset of the device indicated by @p interfce and not return until this
+ * has been completed.
+ *
+ * @param interface The interface for which the command is intended
+ *
+ * @returns
+ * Return 0 if the request can be handled or -1 if it cannot. Returning -1
+ * will cause STALL to be returned to the host.
+ */
+extern int8_t MSC_BULK_ONLY_MASS_STORAGE_RESET_CALLBACK(uint8_t interface);
+
+#ifdef MSC_GET_STORAGE_INFORMATION
+/** MSC Get Storage Information Callback
+ *
+ * The USB Stack will call this function when the host requests information
+ * about the storage device.
+ *
+ * SCSI (and therefore USB) drives are required to support LBA (Logical Block
+ * Addressing) which means every block on the drive can be addressed as an
+ * array of fixed-sized blocks.
+ *
+ * This function may be called more than once as data is requested by the
+ * host.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param lun The Logical Unit Number (LUN) of the medium requested.
+ * @param block_size The block size for this medium. This block size is
+ * used for both reading and writing. It should be a
+ * multiple of both the IN and OUT endpoint sizes, but
+ * does not strictly have to be. If it is not, the
+ * handling of reads and writes on the application side
+ * will likely be more complex. The block_size must be
+ * below 2^24.
+ * @param num_blocks The number of blocks the medium contains.
+ * @param write_protect Whether write-protection is enabled.
+ *
+ *
+ * @returns
+ * Return a code from @p MSCReturnCodes. Returning non-success will cause
+ * an error to be returned to the host.
+ */
+extern int8_t MSC_GET_STORAGE_INFORMATION(
+ const struct msc_application_data *app_data,
+ uint8_t lun,
+ uint32_t *block_size,
+ uint32_t *num_blocks,
+ bool *write_protect);
+#else
+#error "You must define MSC_GET_STORAGE_INFORMATION in your usb_config.h"
+#endif
+
+#ifdef MSC_UNIT_READY
+/** MSC Check if Medium is Ready Callback
+ *
+ * The USB Stack will call this function when the host requests information
+ * regarding whether the medium is ready or not.
+ *
+ * This function may be called more than once as data is requested by the
+ * host.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param lun The Logical Unit Number (LUN) of the medium requested.
+ *
+ * @returns
+ * Return a code from @p MSCReturnCodes. Returning non-success will cause
+ * an error to be returned to the host.
+ */
+extern int8_t MSC_UNIT_READY(
+ const struct msc_application_data *app_data,
+ uint8_t lun);
+#else
+#error "You must define MSC_UNIT_READY in your usb_config.h"
+#endif
+
+#ifdef MSC_START_STOP_UNIT
+/** Start or Stop an MSC Unit
+ *
+ * The USB Stack will call this function when the host requests that a
+ * logical unit start or stop. This is mostly used for stopping and ejecting
+ * the medium.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param lun The Logical Unit Number (LUN) of the medium requested.
+ * @param start Whether to start or stop the medium. true means to
+ * start, and false means to stop the medium.
+ * @param load_eject Whether to load or eject the medium. The desired
+ * behavior depends on the value of @p start. When
+ * load_eject is true, then the application should load
+ * media if start is true, or eject media if start is
+ * false.
+ *
+ * The four cases for @p start and @p load_eject:
+ * if (start && load_eject): load the medium
+ * if (start && !load_eject): neither load nor eject
+ * if (!start && load_eject): eject the medium
+ * if (!start && !load_eject): neither load nor eject
+ *
+ * @returns
+ * Return a code from @p MSCReturnCodes. Returning non-success will cause
+ * an error to be returned to the host.
+ */
+extern int8_t MSC_START_STOP_UNIT(
+ const struct msc_application_data *app_data,
+ uint8_t lun,
+ bool start,
+ bool load_eject);
+#else
+#error "You must define MSC_START_STOP_UNIT in your usb_config.h"
+#endif
+
+#ifdef MSC_START_READ
+/** MSC Read Callback
+ *
+ * The USB Stack will call this function when the host requests a read
+ * operation from the device. This callback will initiate reading data from
+ * the medium. The application will then call @p msc_start_send_to_host()
+ * repeatedly until all data has been given to the MSC class. At that point,
+ * the application must call @p msc_notify_read_operation_complete() to
+ * indicate that the Data-Transport is complete.
+ *
+ * See the documentation for @p msc_start_send_to_host() for additional
+ * restrictions.
+ *
+ * Note that this funciton must simply kick-off the reading/sending of the
+ * data and return quickly. In other words, this function must not block.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param lun The Logical Unit Number (LUN) of the medium requested.
+ * @param lba_address Logical Block Address to start reading from.
+ * @param num_blocks Number of blocks to read.
+ *
+ * @returns
+ * Return a code from @p MSCReturnCodes. Returning non-success will cause
+ * an error to be returned to the host.
+ */
+extern int8_t MSC_START_READ(
+ struct msc_application_data *app_data,
+ uint8_t lun,
+ uint32_t lba_address,
+ uint16_t num_blocks);
+#else
+#error "You must define MSC_START_READ in your usb_config.h"
+#endif
+
+#ifdef MSC_WRITE_SUPPORT
+#ifdef MSC_START_WRITE
+/** MSC Write Callback
+ *
+ * The USB Stack will call this function when the host requests to write
+ * data to the device. The application's implementation of this function
+ * will provide a buffer (@p buffer) where the USB stack will place the data
+ * as it is received. Once the buffer is full, the USB stack will call the
+ * provided callback function (@p callback), notifying the application that
+ * it can begin processing the data (ie: writing it to the medium). Once
+ * the buffer has been processed (and the application is done with the
+ * buffer), the application must then call @p
+ * msc_notify_block_write_complete() to notify the USB stack that it is
+ * ready for the next buffer-full of data.
+ *
+ * Note that this funciton must simply set any necessary state on the
+ * appliction side and return the requested data quickly. In other words,
+ * this function must not block.
+ *
+ * @param app_data Pointer to application data for this interface.
+ * @param lun The Logical Unit Number (LUN) of the medium requested.
+ * @param lba_address Logical Block Address the data is intended for.
+ * @param num_blocks Number of blocks which will eventually be written.
+ * @param buffer The place to put the data from the USB bus
+ * @param buffer_len The size of the buffer in bytes. It must be a
+ * multiple of the OUT endpoint size.
+ * @param callback A function to be called when the data has been
+ * received from the host. It will be called from
+ * interrupt context and must not block.
+ *
+ * @returns
+ * Return a code from @p MSCReturnCodes. Returning non-success will cause
+ * an error to be returned to the host.
+ */
+extern int8_t MSC_START_WRITE(
+ struct msc_application_data *app_data,
+ uint8_t lun,
+ uint32_t lba_address,
+ uint16_t num_blocks,
+ uint8_t **buffer,
+ size_t *buffer_len,
+ msc_completion_callback *callback);
+#else
+#error "You must either define MSC_START_WRITE in your usb_config.h or make this MSC class read-only."
+#endif /* MSC_START_WRITE */
+#endif /* MSC_WRITE_SUPPORT */
+
+/* Doxygen end-of-group for msc_items */
+/** @}*/
+
+/* Doxygen end-of-group for public_api */
+/** @}*/
+
+#endif /* USB_MSC_H__ */
--- a/usb/src/usb.c Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/src/usb.c Sat Feb 18 16:25:38 2017 +0000
@@ -750,6 +750,10 @@
#endif
}
+/* This function is only called in either:
+ * 1. a direct response to a SETUP packet, or
+ * 2. as a STATUS stage,
+ * hence the hard-coding of DTS to 1, which is appropriate in both cases. */
static void send_zero_length_packet_ep0()
{
#ifdef PPB_EP0_IN
@@ -839,9 +843,12 @@
uint8_t bytes_to_send = MIN(len, EP_0_IN_LEN);
bytes_to_send = MIN(bytes_to_send, bytes_asked_for);
returning_short = len < bytes_asked_for;
- copy_to_ep0_in_buf(ptr, bytes_to_send);
+ if (bytes_to_send > 0)
+ copy_to_ep0_in_buf(ptr, bytes_to_send);
ep0_data_stage_in_buffer = ((char*)ptr) + bytes_to_send;
ep0_data_stage_buf_remaining = MIN(bytes_asked_for, len) - bytes_to_send;
+ if (ep0_data_stage_buf_remaining == 0 && returning_short)
+ control_need_zlp = 1;
/* Send back the first transaction */
ep0_buf.flags |= EP_TX_DTS;
@@ -1171,7 +1178,7 @@
* in progress and thus invalidates any IN transactions which were
* pending for a previous control transfer. Cancel any of these IN
* transactions which were pending. */
-#ifdef PPB_EP0_OUT
+#ifdef PPB_EP0_IN
/* For ping-pong mode on EP 0, note below that ppbi is the next
* ping-pong buffer which would be written to, meaning that !ppbi is
* the buffer which would have an IN transaction pending (if any).
@@ -1313,8 +1320,29 @@
reset_ep0_data_stage();
}
else {
- /* The data stage has completed. Set up the status stage. */
- send_zero_length_packet_ep0();
+ /* The data stage has completed. Notify the application
+ * and set up the status stage accordingly as success
+ * (zero-length packet) or failure (STALL). */
+ int8_t res = 0;
+
+ if (ep0_data_stage_callback)
+ res = ep0_data_stage_callback(1/*true*/, ep0_data_stage_context);
+
+ if (res < 0) {
+ /* The application has indicated failure of
+ * some kind, so stall the transfer */
+ stall_ep0();
+ }
+ else {
+ /* Set up the successful status
+ * stage. */
+ send_zero_length_packet_ep0();
+ }
+
+ /* Remove the callback pointer so it
+ * is not called again at the end of
+ * the status stage */
+ ep0_data_stage_callback = NULL;
}
}
}
@@ -1355,8 +1383,8 @@
if (ep0_data_stage_direc == 0/*OUT*/) {
/* An IN on the control endpoint with no data pending
* and during an OUT transfer means the STATUS stage
- * of the control transfer has completed. Notify the
- * application, if applicable. */
+ * of the control transfer has completed. If there
+ * is still a callback, call it. */
if (ep0_data_stage_callback)
ep0_data_stage_callback(1/*true*/, ep0_data_stage_context);
reset_ep0_data_stage();
--- a/usb/src/usb_cdc.c Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/src/usb_cdc.c Sat Feb 18 16:25:38 2017 +0000
@@ -81,37 +81,38 @@
#if defined(CDC_SET_COMM_FEATURE_CALLBACK) || defined(CDC_CLEAR_COMM_FEATURE_CALLBACK)
static uint8_t set_or_clear_request;
-static void set_or_clear_comm_feature_callback(bool transfer_ok, void *context)
+static int8_t set_or_clear_comm_feature_callback(bool transfer_ok, void *context)
{
/* Only ABSTRACT_STATE is supported here. */
if (!transfer_ok)
- return;
+ return -1;
bool idle_setting = (transfer_data.comm_feature & 1) != 0;
bool data_multiplexed_state = (transfer_data.comm_feature & 2) != 0;
if (set_or_clear_request == CDC_SET_COMM_FEATURE) {
- CDC_SET_COMM_FEATURE_CALLBACK(transfer_interface,
- idle_setting,
- data_multiplexed_state);
+ return CDC_SET_COMM_FEATURE_CALLBACK(transfer_interface,
+ idle_setting,
+ data_multiplexed_state);
}
else {
/* request == CDC_CLEAR_COMM_FEATURE */
- CDC_CLEAR_COMM_FEATURE_CALLBACK(transfer_interface,
- idle_setting,
- data_multiplexed_state);
+ return CDC_CLEAR_COMM_FEATURE_CALLBACK(transfer_interface,
+ idle_setting,
+ data_multiplexed_state);
}
+ return 0;
}
#endif
#if defined(CDC_SET_LINE_CODING_CALLBACK)
-static void set_line_coding(bool transfer_ok, void *context) {
+static int8_t set_line_coding(bool transfer_ok, void *context) {
if (!transfer_ok)
- return;
+ return -1;
- CDC_SET_LINE_CODING_CALLBACK(transfer_interface,
- &transfer_data.line_coding);
+ return CDC_SET_LINE_CODING_CALLBACK(transfer_interface,
+ &transfer_data.line_coding);
}
#endif
@@ -159,7 +160,7 @@
return -1;
usb_send_data_stage((void*)response,
- MIN(len, setup->wLength),
+ min(len, setup->wLength),
callback, context);
return 0;
}
@@ -227,7 +228,7 @@
(uint16_t) data_multiplexed_state << 1;
usb_send_data_stage((char*)&transfer_data.comm_feature,
- MIN(setup->wLength,
+ min(setup->wLength,
sizeof(transfer_data.comm_feature)),
NULL/*callback*/, NULL);
return 0;
@@ -241,7 +242,7 @@
transfer_interface = interface;
usb_start_receive_ep0_data_stage(
(char*)&transfer_data.line_coding,
- MIN(setup->wLength,
+ min(setup->wLength,
sizeof(transfer_data.line_coding)),
set_line_coding, NULL);
return 0;
@@ -260,7 +261,7 @@
return -1;
usb_send_data_stage((char*)&transfer_data.line_coding,
- MIN(setup->wLength,
+ min(setup->wLength,
sizeof(transfer_data.line_coding)),
/*callback*/NULL, NULL);
return 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/src/usb_hid.c Sat Feb 18 16:25:38 2017 +0000
@@ -0,0 +1,176 @@
+/*
+ * M-Stack USB Device Stack Implementation
+ * Copyright (C) 2013 Alan Ott <alan@signal11.us>
+ * Copyright (C) 2013 Signal 11 Software
+ *
+ * Initial version for PIC18, 2008-02-24
+ * PIC24 port, 2013-08-13
+ *
+ * M-Stack is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, version 3; or the Apache License, version 2.0
+ * as published by the Apache Software Foundation. If you have purchased a
+ * commercial license for this software from Signal 11 Software, your
+ * commerical license superceeds the information in this header.
+ *
+ * M-Stack 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You should have received a copy of the Apache License, verion 2.0 along
+ * with this software. If not, see <http://www.apache.org/licenses/>.
+ */
+
+#include <usb_config.h>
+
+#include <usb_ch9.h>
+#include <usb.h>
+#include <usb_hid.h>
+
+#define MIN(x,y) (((x)<(y))?(x):(y))
+
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct hid_descriptor), 9);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct hid_optional_descriptor), 3);
+
+#ifdef MULTI_CLASS_DEVICE
+static uint8_t *hid_interfaces;
+static uint8_t num_hid_interfaces;
+
+void hid_set_interface_list(uint8_t *interfaces, uint8_t num_interfaces)
+{
+ hid_interfaces = interfaces;
+ num_hid_interfaces = num_interfaces;
+}
+#endif
+
+uint8_t process_hid_setup_request(const struct setup_packet *setup)
+{
+ /* The following comes from the HID spec 1.11, section 7.1.1 */
+
+ uint8_t interface = setup->wIndex;
+
+#ifdef MULTI_CLASS_DEVICE
+ /* Check the interface first to make sure the destination is a
+ * HID interface. Composite devices will need to call
+ * hid_set_interface_list() first.
+ */
+ uint8_t i;
+ for (i = 0; i < num_hid_interfaces; i++) {
+ if (interface == hid_interfaces[i])
+ break;
+ }
+
+ /* Return if interface is not in the list of HID interfaces. */
+ if (i == num_hid_interfaces)
+ return -1;
+#endif
+
+ if (setup->bRequest == GET_DESCRIPTOR &&
+ setup->REQUEST.bmRequestType == 0x81) {
+ uint8_t descriptor = ((setup->wValue >> 8) & 0x00ff);
+
+ const void *desc;
+ int16_t len = -1;
+
+ if (descriptor == DESC_HID) {
+ len = USB_HID_DESCRIPTOR_FUNC(interface, &desc);
+ }
+ else if (descriptor == DESC_REPORT) {
+ len = USB_HID_REPORT_DESCRIPTOR_FUNC(interface, &desc);
+ }
+#ifdef USB_HID_PHYSICAL_DESCRIPTOR_FUNC
+ else if (descriptor == DESC_PHYSICAL) {
+ uint8_t descriptor_index = setup->wValue & 0x00ff;
+ len = USB_HID_PHYSICAL_DESCRIPTOR_FUNC(interface, descriptor_index, &desc);
+ }
+#endif
+ if (len < 0)
+ return -1;
+
+ usb_send_data_stage((void*) desc, min(len, setup->wLength), NULL, NULL);
+ return 0;
+ }
+
+ /* No support for Set_Descriptor */
+
+#ifdef HID_GET_REPORT_CALLBACK
+ const void *desc;
+ int16_t len = -1;
+ usb_ep0_data_stage_callback callback;
+ void *context;
+ if (setup->bRequest == HID_GET_REPORT &&
+ setup->REQUEST.bmRequestType == 0xa1) {
+ uint8_t report_type = (setup->wValue >> 8) & 0x00ff;
+ uint8_t report_id = setup->wValue & 0x00ff;
+ len = HID_GET_REPORT_CALLBACK(interface/*interface*/,
+ report_type, report_id,
+ &desc, &callback, &context);
+ if (len < 0)
+ return -1;
+
+ usb_send_data_stage((void*)desc, min(len, setup->wLength), callback, context);
+ return 0;
+ }
+#endif
+
+#ifdef HID_SET_REPORT_CALLBACK
+ if (setup->bRequest == HID_SET_REPORT &&
+ setup->REQUEST.bmRequestType == 0x21) {
+ uint8_t report_type = (setup->wValue >> 8) & 0x00ff;
+ uint8_t report_id = setup->wValue & 0x00ff;
+ int8_t res = HID_SET_REPORT_CALLBACK(interface,
+ report_type, report_id);
+ return res;
+ }
+#endif
+
+#ifdef HID_GET_IDLE_CALLBACK
+ if (setup->bRequest == HID_GET_IDLE &&
+ setup->REQUEST.bmRequestType == 0xa1) {
+ uint8_t report_id = setup->wValue & 0x00ff;
+ uint8_t res = HID_GET_IDLE_CALLBACK(interface, report_id);
+
+ usb_send_data_stage((char*)&res, 1, NULL, NULL);
+ return 0;
+ }
+#endif
+
+#ifdef HID_SET_IDLE_CALLBACK
+ if (setup->bRequest == HID_SET_IDLE &&
+ setup->REQUEST.bmRequestType == 0x21) {
+ uint8_t duration = (setup->wValue >> 8) & 0x00ff;
+ uint8_t report_id = setup->wValue & 0x00ff;
+ uint8_t res = HID_SET_IDLE_CALLBACK(interface, report_id,
+ duration);
+
+ return res;
+ }
+#endif
+
+#ifdef HID_GET_PROTOCOL_CALLBACK
+ if (setup->bRequest == HID_GET_PROTOCOL &&
+ setup->REQUEST.bmRequestType == 0xa1) {
+ int8_t res = HID_GET_PROTOCOL_CALLBACK(interface);
+ if (res < 0)
+ return -1;
+
+ usb_send_data_stage((char*)&res, 1, NULL, NULL);
+ return 0;
+ }
+#endif
+
+#ifdef HID_SET_PROTOCOL_CALLBACK
+ if (setup->bRequest == HID_SET_PROTOCOL &&
+ setup->REQUEST.bmRequestType == 0x21) {
+ int8_t res = HID_SET_PROTOCOL_CALLBACK(interface,
+ setup->wValue);
+ return res;
+ }
+#endif
+
+ return -1;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/src/usb_msc.c Sat Feb 18 16:25:38 2017 +0000
@@ -0,0 +1,1357 @@
+/*
+ * M-Stack USB Device Stack Implementation
+ * Copyright (C) 2014 Alan Ott <alan@signal11.us>
+ * Copyright (C) 2014 Signal 11 Software
+ *
+ * 2014-06-04
+ *
+ * M-Stack is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, version 3; or the Apache License, version 2.0
+ * as published by the Apache Software Foundation. If you have purchased a
+ * commercial license for this software from Signal 11 Software, your
+ * commerical license superceeds the information in this header.
+ *
+ * M-Stack 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You should have received a copy of the Apache License, verion 2.0 along
+ * with this software. If not, see <http://www.apache.org/licenses/>.
+ */
+
+#include <usb_config.h>
+
+#include <usb_ch9.h>
+#include <usb.h>
+#include <usb_msc.h>
+#include "usb_priv.h"
+
+#include <string.h>
+
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#define MAX(x,y) (((x)>(y))?(x):(y))
+
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_command_block_wrapper), 31);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_command_status_wrapper), 13);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_inquiry_command), 6);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_request_sense_command), 6);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_mode_sense_6_command), 6);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_start_stop_unit), 6);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_read_10_command), 10);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct msc_scsi_write_10_command), 10);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct scsi_inquiry_response), 36);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct scsi_capacity_response), 8);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct scsi_mode_sense_response), 4);
+STATIC_SIZE_CHECK_EQUAL(sizeof(struct scsi_sense_response), 18);
+
+
+#if MSC_MAX_LUNS_PER_INTERFACE > 0x10
+ #error Too many LUNs per interface. Max is 16 LUNs per interface
+#endif
+
+#if MSC_MAX_LUNS_PER_INTERFACE == 0
+ #error At least one LUN must be supported.
+#endif
+
+static struct msc_application_data *g_application_data;
+#ifdef MSC_SUPPORT_MULTIPLE_MSC_INTERFACES
+static uint8_t g_application_data_count;
+#endif
+
+static inline void swap(uint8_t *v1, uint8_t *v2)
+{
+ uint8_t tmp;
+ tmp = *v1;
+ *v1 = *v2;
+ *v2 = tmp;
+}
+
+static void swap4(uint32_t *val)
+{
+ union swapper {
+ uint32_t val;
+ uint8_t v_bytes[4];
+ };
+
+ union swapper *sw = (union swapper *) val;
+ swap(&sw->v_bytes[0], &sw->v_bytes[3]);
+ swap(&sw->v_bytes[1], &sw->v_bytes[2]);
+}
+
+static void swap2(uint16_t *val)
+{
+ union swapper {
+ uint16_t val;
+ uint8_t v_bytes[2];
+ };
+
+ union swapper *sw = (union swapper *) val;
+ swap(&sw->v_bytes[0], &sw->v_bytes[1]);
+}
+
+static bool direction_is_in(uint8_t flags)
+{
+ return flags & MSC_DIRECTION_IN_BIT;
+}
+
+static bool direction_is_out(uint8_t flags)
+{
+ return ~flags & MSC_DIRECTION_IN_BIT;
+}
+
+#ifdef MSC_SUPPORT_MULTIPLE_MSC_INTERFACES
+/* Lookup applicaiton data by interface number. */
+static struct msc_application_data *get_app_data(uint8_t interface)
+{
+ uint8_t i;
+
+ for (i = 0; i < g_application_data_count; i++) {
+ struct msc_application_data *d = &g_application_data[i];
+ if (d->interface == interface) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
+/* Lookup application data by endpoint. */
+static struct msc_application_data *get_app_data_by_endpoint(
+ uint8_t endpoint_num, uint8_t direction)
+{
+ uint8_t i;
+
+ for (i = 0; i < g_application_data_count; i++) {
+ struct msc_application_data *d = &g_application_data[i];
+ /* The if-else inside the loop is slower than doing it
+ * outside the loop, but it's less code, and since this
+ * doesn't happen very often (just in the clear_halt case
+ * which only happens on error), this is an acceptable
+ * tradeoff. */
+ if (direction && d->in_endpoint == endpoint_num) {
+ return d;
+ }
+ else if (d->out_endpoint == endpoint_num) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+#else
+/* Lookup applicaiton data by interface number. */
+static inline struct msc_application_data *get_app_data(uint8_t interface)
+{
+ if (interface == g_application_data[0].interface)
+ return g_application_data;
+
+ return NULL;
+}
+
+/* Lookup application data by endpoint. */
+static inline struct msc_application_data *get_app_data_by_endpoint(
+ uint8_t endpoint_num, uint8_t direction)
+{
+ if (direction && g_application_data[0].in_endpoint == endpoint_num) {
+ return g_application_data;
+ }
+ else if (g_application_data[0].out_endpoint == endpoint_num) {
+ return g_application_data;
+ }
+
+ return NULL;
+}
+#endif
+
+/* Stall the IN endpoint and set the status which will be returned by the
+ * next CSW. */
+static void stall_in_and_set_status(struct msc_application_data *msc,
+ uint32_t residue, uint8_t status)
+{
+ msc->residue = residue;
+ msc->status = status;
+ usb_halt_ep_in(msc->in_endpoint);
+ msc->state = MSC_CSW;
+}
+
+/* Stall the OUT endpoint and set the status which will be returned by the
+ * next CSW. */
+static void stall_out_and_set_status(struct msc_application_data *msc,
+ uint32_t residue, uint8_t status)
+{
+ msc->residue = residue;
+ msc->status = status;
+ usb_halt_ep_out(msc->out_endpoint);
+ msc->state = MSC_CSW;
+}
+
+/* Send a Command Status Word (CSW) */
+static int8_t send_csw(struct msc_application_data *msc,
+ uint32_t residue, uint8_t status)
+{
+ struct msc_command_status_wrapper *csw;
+
+ /* Make sure endpoint is free */
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ return -1;
+
+ csw = (struct msc_command_status_wrapper *)
+ usb_get_in_buffer(msc->in_endpoint);
+ csw->dCSWSignature = 0x53425355;
+ csw->dCSWTag = msc->current_tag;
+ csw->dCSWDataResidue = residue;
+ csw->bCSWStatus = status;
+
+ usb_send_in_buffer(msc->in_endpoint, sizeof(*csw));
+
+ /* Reset states and status */
+ msc->state = MSC_IDLE;
+ msc->status = MSC_STATUS_PASSED;
+ msc->residue = 0;
+
+ return 0;
+}
+
+static void set_scsi_sense(struct msc_application_data *msc,
+ enum MSCReturnCodes code)
+{
+ if (code == MSC_ERROR_MEDIUM_NOT_PRESENT) {
+ msc->sense_key = SCSI_SENSE_KEY_NOT_READY;
+ msc->additional_sense_code = SCSI_ASC_MEDIUM_NOT_PRESENT;
+ }
+ else if (code == MSC_ERROR_INVALID_LUN) {
+ msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST;
+ msc->additional_sense_code =
+ SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED;
+ }
+ else if (code == MSC_ERROR_INVALID_ADDRESS) {
+ msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST;
+ msc->additional_sense_code =
+ SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ }
+ else if (code == MSC_ERROR_WRITE_PROTECTED) {
+ msc->sense_key = SCSI_SENSE_KEY_DATA_PROTECT;
+ msc->additional_sense_code = SCSI_ASC_WRITE_PROTECTED;
+ }
+ else if (code == MSC_ERROR_READ) {
+ msc->sense_key = SCSI_SENSE_KEY_MEDIUM_ERROR;
+ msc->additional_sense_code = SCSI_ASC_UNRECOVERED_READ_ERROR;
+ }
+ else if (code == MSC_ERROR_WRITE) {
+ msc->sense_key = SCSI_SENSE_KEY_MEDIUM_ERROR;
+ msc->additional_sense_code =
+ SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT;
+ }
+ else if (code == MSC_ERROR_MEDIUM) {
+ msc->sense_key = SCSI_SENSE_KEY_MEDIUM_ERROR;
+ msc->additional_sense_code = 0;
+ }
+}
+
+/* Whether the CBW is valid and meaningful, per the MSC BOT spec */
+static bool msc_cbw_valid_and_meaningful(struct msc_application_data *msc,
+ const uint8_t *data, uint16_t len)
+{
+ const struct msc_command_block_wrapper *cbw = (const void*) data;
+
+ /* Test whether the CBW isvalid. Conforms to BOT: 6.2.1 */
+
+ if (len != sizeof(struct msc_command_block_wrapper))
+ return false;
+
+ if (cbw->dCBWSignature != 0x43425355)
+ return false;
+
+ /* Test whether the CBW is meaningful. Conforms to BOT: 6.2.2 */
+
+ if (msc->state != MSC_IDLE)
+ return false;
+
+ if (cbw->bCBWCBLength < 1 || cbw->bCBWCBLength > 16)
+ return false;
+
+ if (cbw->bmCBWFlags & 0x7F)
+ return false;
+
+ if (cbw->bCBWLUN > msc->max_lun ||
+ cbw->bCBWLUN >= MSC_MAX_LUNS_PER_INTERFACE)
+ return false;
+
+ return true;
+}
+
+static void stall_in_and_phase_error(struct msc_application_data *msc)
+{
+ stall_in_and_set_status(msc, 0, MSC_STATUS_PHASE_ERROR);
+}
+
+static void stall_out_and_phase_error(struct msc_application_data *msc)
+{
+ stall_out_and_set_status(msc, 0, MSC_STATUS_PHASE_ERROR);
+}
+
+static void phase_error(struct msc_application_data *msc)
+{
+ send_csw(msc, 0, MSC_STATUS_PHASE_ERROR);
+}
+
+/** Set the state of the Data-IN endpoint after sending data to the host
+ *
+ * Set the state of the Data-In endpoint based on the amount of data requested
+ * by the host in the CBW packet (cbw_length) and the amount of data actually
+ * sent (sent_length).
+ *
+ * If the cbw_length is more than the sent_length, the endpoint must be
+ * stalled (BOT: 6.7.2, Device Requirements, case 5).
+ *
+ * If the requested length and the number of bytes sent match (the most common
+ * case, Case 6), the EP does not have to be stalled and a CSW can be sent.
+ *
+ * This function should be called after data has been sent to the host for
+ * short transports where the device expects to send data (Di) (such as
+ * INQUIRY, REQUEST_SENSE, etc.). It is not used for READ, which has a
+ * different mechanism for handling status.
+ */
+static void set_data_in_endpoint_state(struct msc_application_data *msc,
+ uint32_t cbw_length,
+ size_t sent_length)
+{
+ msc->status = MSC_STATUS_PASSED;
+
+ if (cbw_length > sent_length) {
+ /* Case 5 (Hi > Di): Stall the IN EP and set residue */
+ msc->residue = cbw_length - sent_length;
+ msc->state = MSC_STALL;
+ }
+ else {
+ /* Case 6 (Hi = Di): OK. Host will want the CSW next */
+ msc->residue = 0;
+ msc->state = MSC_CSW;
+ }
+}
+
+/* Check the Di cases: 2, 5, 6, 7, and 10 (BOT section 6.7). Case 6 is
+ * correct, and 5 is acceptable with residue. 2, 7, and 10 result in a phase
+ * error with an endpoint (IN or OUT) potentially stalled.
+ *
+ * This function returns 0 if case 5 or 6, or -1 if case 2, 7, or 10.
+ *
+ * The calling function should:
+ * for return value 0:
+ * proceed with returning data to the host, properly handling residue,
+ * for return value -1:
+ * do nothing further in response to the received command.
+ */
+static int8_t check_di_cases(struct msc_application_data *msc,
+ const struct msc_command_block_wrapper *cbw,
+ uint32_t intended_length)
+{
+ const uint32_t cbw_length = cbw->dCBWDataTransferLength;
+ const bool direc_is_out = direction_is_out(cbw->bmCBWFlags);
+
+ /* Case 2 (Hn < Di): set phase error (no stall) */
+ if (cbw_length == 0) {
+ phase_error(msc);
+ return -1;
+ }
+
+ /* Case 7 (Hi < Di): stall IN and phase error */
+ if (cbw_length < intended_length) {
+ stall_in_and_phase_error(msc);
+ return -1;
+ }
+
+ /* Case 10 (Ho <> Di): stall OUT and phase error */
+ if (direc_is_out) {
+ stall_out_and_phase_error(msc);
+ return -1;
+ }
+
+ /* Case 5 (Hi > Di): OK, with residue.
+ * Case 6 (Hi = Di): OK */
+ return 0;
+}
+
+/* Check the Dn cases: 1, 4, and 9 (BOT section 6.7). Case 1 is correct, and
+ * cases 4 and 9 will result in a stall of an appropriate endpoint.
+ *
+ * This function returns 0 if case 1 or returns -1 for cases 4 and 9.
+ *
+ * The calling function should:
+ * for return value 0:
+ * proceed to the CSW state (no data needs to be returned or handled),
+ * for return value -1:
+ * do nothing further in response to the received command.
+ *
+ */
+static int8_t check_dn_cases(struct msc_application_data *msc,
+ const struct msc_command_block_wrapper *cbw)
+{
+ const uint32_t cbw_length = cbw->dCBWDataTransferLength;
+ const bool direc_is_out = direction_is_out(cbw->bmCBWFlags);
+
+ /* Case 9 (Ho > Dn): stall OUT and set status FAILED */
+ if (direc_is_out && cbw_length > 0) {
+ stall_out_and_set_status(msc, cbw_length, MSC_STATUS_FAILED);
+ return -1;
+ }
+
+ /* Case 4 (Hi > Dn): Stall IN and set status FAILED. */
+ if (cbw_length > 0) {
+ stall_in_and_set_status(msc, cbw_length, MSC_STATUS_FAILED);
+ return -1;
+ }
+
+ /* Case 1 (Hn = Dn): OK */
+ return 0;
+}
+
+#ifdef MSC_WRITE_SUPPORT
+/* Check the Do cases: 3, 8, 11, 12, and 13 (BOT section 6.7). Case 12 is
+ * correct. Other cases stall appropriate endpoints.
+ *
+ * This function returns 0 for case 12 or returns -1 for all other cases.
+ *
+ * The calling function should:
+ * for return value 0:
+ * proceed with receiving data from the host,
+ * for return value -1:
+ * do nothing further in response to the received command.
+ */
+static int8_t check_do_cases(struct msc_application_data *msc,
+ const struct msc_command_block_wrapper *cbw,
+ uint32_t intended_length)
+{
+ const uint32_t cbw_length = cbw->dCBWDataTransferLength;
+ const bool direc_is_in = direction_is_in(cbw->bmCBWFlags);
+
+ /* Case 3 (Hn < Do): set phase error (no stall) */
+ if (cbw_length == 0) {
+ phase_error(msc);
+ return -1;
+ }
+
+ /* Case 8 (Hi <> Do): stall IN and phase error */
+ if (direc_is_in) {
+ stall_in_and_phase_error(msc);
+ return -1;
+ }
+
+ /* Case 13 (Ho < Do): stall OUT and phase error */
+ if (cbw_length < intended_length) {
+ stall_out_and_phase_error(msc);
+ return -1;
+ }
+
+ /* Case 11 (Ho > Do): Stall OUT and set status to FAILED
+ *
+ * Note that BOT 6.7.3:device:case_11 says that the device shall
+ * receive the intended data and then stall the bulk-OUT endpoint.
+ * BOT does not define "intended data," but appears to use it
+ * to mean the amount of data that is specified (in our case) in the
+ * SCSI packet. This is the definition used most often in this
+ * implementation of the Mass Storage class, but in this case, for
+ * simplicity, the device will "intend" to receve 0 bytes if the CBW
+ * length and the SCSI length don't match.
+ *
+ * This means instead of setting up to receive intended_length bytes
+ * and then stalling once that amount of data has been received and
+ * then dealing with returning residue, the OUT endpoint will simply
+ * be stalled here.
+ *
+ * This passes the USBCV Case 11 test. */
+ if (cbw_length > intended_length) {
+ stall_out_and_set_status(msc, cbw_length, MSC_STATUS_FAILED);
+ return -1;
+ }
+
+ /* Case 12 (Ho = Do): OK */
+ return 0;
+}
+#endif
+
+#ifdef MULTI_CLASS_DEVICE
+static uint8_t *msc_interfaces;
+static uint8_t num_msc_interfaces;
+
+void msc_set_interface_list(uint8_t *interfaces, uint8_t num_interfaces)
+{
+ msc_interfaces = interfaces;
+ num_msc_interfaces = num_interfaces;
+}
+
+static bool interface_is_msc(uint8_t interface)
+{
+ uint8_t i;
+ for (i = 0; i < num_msc_interfaces; i++) {
+ if (interface == msc_interfaces[i])
+ break;
+ }
+
+ /* Return if interface is not in the list of CDC interfaces. */
+ if (i == num_msc_interfaces)
+ return false;
+
+ return true;
+}
+#endif
+
+uint8_t msc_init(struct msc_application_data *app_data, uint8_t count)
+{
+ uint8_t i;
+
+#ifndef MSC_SUPPORT_MULTIPLE_MSC_INTERFACES
+ if (count > 1)
+ return -1;
+#endif
+
+ for (i = 0; i < count; i++) {
+ struct msc_application_data *d = &app_data[i];
+
+ /* Validate struct members. */
+ if (d->max_lun > 0x0f)
+ return -1;
+ if (d->max_lun >= MSC_MAX_LUNS_PER_INTERFACE)
+ return -1;
+ if (d->in_endpoint_size != 64)
+ return -1;
+ if (d->in_endpoint > 15)
+ return -1;
+ if (d->out_endpoint > 15)
+ return -1;
+
+ /* Initialize the MSC-Class-Controlled members.*/
+ d->state = MSC_IDLE;
+ d->sense_key = 0;
+ d->additional_sense_code = 0;
+ d->residue = 0;
+ d->status = 0;
+ d->requested_bytes = 0;
+ d->transferred_bytes = 0;
+ d->tx_buf = NULL;
+ d->tx_len_remaining = 0;
+#ifdef MSC_WRITE_SUPPORT
+ d->rx_buf_cur = NULL;
+ d->rx_buf_len = 0;
+ d->out_ep_missed_transactions = 0;
+#endif
+ d->operation_complete_callback = NULL;
+ memset(d->block_size, 0, sizeof(d->block_size));
+ }
+
+ g_application_data = app_data;
+#ifdef MSC_SUPPORT_MULTIPLE_MSC_INTERFACES
+ g_application_data_count = count;
+#endif
+
+ return 0;
+}
+
+int8_t process_msc_setup_request(const struct setup_packet *setup)
+{
+ uint8_t interface = setup->wIndex;
+ struct msc_application_data *msc;
+
+
+#ifdef MULTI_CLASS_DEVICE
+ /* Check the interface first to make sure the destination is an
+ * MSC interface. Multi-class devices will need to call
+ * msc_set_interface_list() first.
+ */
+ if (!interface_is_msc(interface))
+ return -1;
+#endif
+
+ /* Get application data for this interface */
+ msc = get_app_data(interface);
+ if (!msc)
+ return -1;
+
+ /* BOT 3.2 says the GET_MAX_LUN request is optional if there's only one
+ * LUN, but stalling this request will cause Windows 7 to hang for 18
+ * seconds when a device is first connected. */
+ if (setup->bRequest == MSC_GET_MAX_LUN &&
+ setup->REQUEST.bmRequestType == 0xa1) {
+
+ /* Stall invalid value/length */
+ if (setup->wValue != 0 ||
+ setup->wLength != 1)
+ return -1;
+
+ usb_send_data_stage((void*)&msc->max_lun,
+ 1, NULL, 0);
+ return 0;
+ }
+
+ if (setup->bRequest == MSC_BULK_ONLY_MASS_STORAGE_RESET &&
+ setup->REQUEST.bmRequestType == 0x21) {
+ struct msc_application_data *d = get_app_data(interface);
+
+ /* Stall invalid value/length */
+ if (setup->wValue != 0 ||
+ setup->wLength != 0)
+ return -1;
+
+ if (d)
+ d->state = MSC_IDLE;
+ else
+ return -1;
+
+#ifdef MSC_BULK_ONLY_MASS_STORAGE_RESET_CALLBACK
+ int8_t res = 0;
+
+ res = MSC_BULK_ONLY_MASS_STORAGE_RESET_CALLBACK(interface);
+ if (res < 0)
+ return -1;
+#endif
+ /* Clear the NEEDS_RESET_RECOVERY state */
+ msc->state = MSC_IDLE;
+
+ /* Return zero-length packet. No data stage. */
+ usb_send_data_stage(NULL, 0, NULL, NULL);
+
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MSC_WRITE_SUPPORT
+/* Copy data to the application's buffer. If the application's buffer
+ * is full, return -1.
+ *
+ * It will be common for this function to be called when the buffer is full,
+ * since writing to the medium is slower than USB, and since the host relies
+ * on the device to throttle the connection as appropriate.
+ *
+ * If -1 is returned from this function, it will be up to the caller to make
+ * sure that this function is re-called later, when there is buffer space
+ * available. */
+static inline uint8_t receive_data(struct msc_application_data *msc,
+ const uint8_t *data, uint16_t len)
+{
+ /* Make sure this doesn't take us off the end of the
+ * application's buffer. */
+ if (msc->rx_buf_cur + len > msc->rx_buf + msc->rx_buf_len)
+ return -1;
+
+ /* Ignore this data if it is more than was expected. This indicates
+ * an error on the host side, but it can be handled here by just
+ * ignoring the extra data. The difference will be reflected in the
+ * residue in the CSW. */
+ if (msc->transferred_bytes >= msc->requested_bytes)
+ return 0;
+
+ /* Copy to the application's buffer. */
+ memcpy(msc->rx_buf_cur, data, len);
+ msc->rx_buf_cur += len;
+
+ /* If this is the last piece of the data block, notify the
+ * application that it's buffer is now full and that it can start
+ * writing to the medium. */
+ if (msc->rx_buf_cur >= msc->rx_buf + msc->rx_buf_len) {
+ msc->operation_complete_callback(msc, true);
+ }
+
+ return 0;
+}
+#endif
+
+/* Send the next transaction containing data from the medium to the host. */
+static int8_t send_next_data_transaction(struct msc_application_data *msc)
+{
+ const uint8_t *cur = msc->tx_buf;
+ const uint16_t len = msc->tx_len_remaining;
+
+ if (!usb_is_configured() || usb_in_endpoint_busy(msc->in_endpoint))
+ return -1;
+
+ if (len > 0) {
+ /* There is data to send; send one packet worth. */
+ uint8_t *buf;
+ uint16_t to_copy;
+
+ buf = usb_get_in_buffer(msc->in_endpoint);
+ to_copy = MIN(len, msc->in_endpoint_size);
+ memcpy(buf, cur, to_copy);
+
+ usb_send_in_buffer(msc->in_endpoint, to_copy);
+
+ msc->transferred_bytes += to_copy;
+ msc->tx_buf += to_copy;
+ msc->tx_len_remaining -= to_copy;
+ }
+ else {
+ /* Transfer of block has completed */
+ msc->operation_complete_callback(msc, true);
+ msc->operation_complete_callback = NULL;
+ msc->tx_buf = NULL;
+ }
+
+ return 0;
+}
+
+uint8_t msc_start_send_to_host(struct msc_application_data *msc,
+ const uint8_t *data, uint16_t len,
+ msc_completion_callback completion_callback)
+{
+ int8_t res;
+
+ usb_disable_transaction_interrupt();
+
+ if (msc->state != MSC_DATA_TRANSPORT_IN || len == 0) {
+ res = -1;
+ goto out;
+ }
+
+ msc->tx_buf = data;
+ msc->tx_len_remaining = len;
+ msc->operation_complete_callback = completion_callback;
+
+ /* Kick off the transmission. */
+ res = send_next_data_transaction(msc);
+
+out:
+ usb_enable_transaction_interrupt();
+ return res;
+}
+
+void msc_notify_read_operation_complete(
+ struct msc_application_data *app_data,
+ bool passed)
+{
+ usb_disable_transaction_interrupt();
+
+ if (app_data->state != MSC_DATA_TRANSPORT_IN) {
+ goto out;
+ }
+
+ uint32_t residue = app_data->requested_bytes_cbw -
+ app_data->transferred_bytes;
+
+ if (!passed) {
+ /* Save off the error codes. These will be read by the host
+ * with a REQUEST_SENSE SCSI command. */
+ set_scsi_sense(app_data, MSC_ERROR_READ);
+ }
+
+ uint8_t status = passed? MSC_STATUS_PASSED: MSC_STATUS_FAILED;
+
+ if (residue > 0) {
+ stall_in_and_set_status(app_data, residue, status);
+ }
+ else {
+ send_csw(app_data, residue, status);
+ app_data->state = MSC_IDLE;
+ }
+
+out:
+ usb_enable_transaction_interrupt();
+}
+
+#ifdef MSC_WRITE_SUPPORT
+/* Must be called with the transaction interrupt disabled */
+static void handle_missed_out_transactions(struct msc_application_data *msc)
+{
+ /* Handle any pending data. There's a good chance the USB
+ * peripheral received data while the write was occurring. That data
+ * was held in the endpoint buffer(s) by
+ * msc_out_transaction_complete(). Process that data now. Make sure to
+ * _only_ call msc_out_transaction_complete() the exact number of
+ * times a transaction completed in order to avoid a race condition
+ * window (more below).
+ *
+ * usb_endpoint_has_data() can't be used to determine how many
+ * transactions are pending because since this function runs
+ * with interrupts disabled. Doing so would open up a race condition
+ * window where the endpoint would be read too many times if a
+ * transaction were to complete during this function. For this reason
+ * msc->out_ep_missed_transactions is used to keep track of how many
+ * OUT transactions are pending (with data on the endpoint buffers).
+ *
+ * Make a new variable for count below because
+ * msc->out_ep_missed_transactions is also manipulated by
+ * msc_out_transaction_complete(), and if the application buffer is
+ * short (eg: smaller the length of two transactions), there's a
+ * possibility that one call to msc_out_transaction_complete() could
+ * fail (if the application buffer is full), in which case
+ * msc_out_transaction_complete() would re-increment
+ * msc->out_ep_missed_transactions. */
+
+ uint8_t i, count;
+
+ count = msc->out_ep_missed_transactions;
+ for (i = 0; i < count; i++) {
+ msc_out_transaction_complete(msc->out_endpoint);
+ msc->out_ep_missed_transactions--;
+ }
+}
+
+void msc_notify_write_data_handled(struct msc_application_data *msc)
+{
+ usb_disable_transaction_interrupt();
+
+ if (msc->state != MSC_DATA_TRANSPORT_OUT) {
+ goto out;
+ }
+
+ /* Ignore if this function has been called too many times. */
+ if (msc->transferred_bytes >= msc->requested_bytes)
+ goto out;
+
+ msc->transferred_bytes += msc->rx_buf_len;
+
+ if (msc->transferred_bytes < msc->requested_bytes) {
+ /* Still more data left to transfer and write. Reset the
+ * buffer pointer to the beginning of the buffer to prepare
+ * to receive more data from the host. */
+ msc->rx_buf_cur = msc->rx_buf;
+ }
+
+out:
+ handle_missed_out_transactions(msc);
+
+ usb_enable_transaction_interrupt();
+}
+
+void msc_notify_write_operation_complete(struct msc_application_data *msc,
+ bool passed,
+ uint32_t bytes_processed)
+{
+ uint32_t residue;
+
+ usb_disable_transaction_interrupt();
+
+ if (msc->state != MSC_DATA_TRANSPORT_OUT) {
+ goto out;
+ }
+
+ residue = msc->requested_bytes_cbw - bytes_processed;
+
+ if (!passed) {
+ /* Save off the error codes. These will be read by the host
+ * with a REQUEST_SENSE SCSI command. */
+ set_scsi_sense(msc, MSC_ERROR_WRITE);
+
+ stall_out_and_set_status(msc, residue, MSC_STATUS_FAILED);
+ msc->out_ep_missed_transactions = 0;
+ goto fail;
+ }
+
+ if (msc->transferred_bytes < msc->requested_bytes) {
+ /* The application is ending the Data Transport before the
+ * host has sent all the data it expects to send. This
+ * becomes case 11 (Ho > Do). Stall the OUT endpoint, but set
+ * the status to PASSED, because the device processed all the
+ * data it intended to process. */
+ stall_out_and_set_status(msc, residue, MSC_STATUS_PASSED);
+ msc->out_ep_missed_transactions = 0;
+ goto fail;
+ }
+ else {
+ /* No more data left to transfer */
+ send_csw(msc, residue, MSC_STATUS_PASSED);
+ msc->state = MSC_IDLE;
+ }
+
+out:
+ handle_missed_out_transactions(msc);
+fail:
+ usb_enable_transaction_interrupt();
+}
+#endif /* MSC_WRITE_SUPPORT */
+
+static void process_msc_command(struct msc_application_data *msc,
+ const uint8_t *data, uint16_t len)
+{
+ const struct msc_command_block_wrapper *cbw = (const void *) data;
+ const uint8_t command = cbw->CBWCB[0];
+ const uint8_t lun = cbw->bCBWLUN;
+ const uint32_t cbw_length = cbw->dCBWDataTransferLength;
+ int8_t res;
+
+ /* Check the Command Block Wrapper (CBW) */
+ if (!msc_cbw_valid_and_meaningful(msc, data,len))
+ goto bad_cbw;
+
+ msc->current_tag = cbw->dCBWTag;
+
+ if (command == MSC_SCSI_INQUIRY) {
+ uint32_t scsi_request_len;
+ struct msc_scsi_inquiry_command *cmd =
+ (struct msc_scsi_inquiry_command *) cbw->CBWCB;
+ struct scsi_inquiry_response *resp =
+ (struct scsi_inquiry_response *)
+ usb_get_in_buffer(msc->in_endpoint);
+
+ swap2(&cmd->allocation_length);
+
+ /* The host may request just the first part of the inquiry
+ * response structure. */
+ scsi_request_len = MIN(cmd->allocation_length, sizeof(*resp));
+
+ /* INQUIRY: Device indends to send data to the host (Di). */
+ res = check_di_cases(msc, cbw, scsi_request_len);
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ /* Send INQUIRY response */
+ memset(resp, 0, sizeof(*resp));
+ resp->peripheral = 0x0;
+ resp->rmb = (msc->media_is_removable_mask & (1<<lun))? 0x80: 0;
+ resp->version = MSC_SCSI_SPC_VERSION_2;
+ resp->response_data_format = 0x2;
+ resp->additional_length = sizeof(*resp) - 4;
+ strncpy(resp->vendor, msc->vendor, sizeof(resp->vendor));
+ strncpy(resp->product, msc->product, sizeof(resp->product));
+ strncpy(resp->revision, msc->revision, sizeof(resp->revision));
+
+ usb_send_in_buffer(msc->in_endpoint, scsi_request_len);
+
+ set_data_in_endpoint_state(msc, cbw_length, scsi_request_len);
+ }
+ else if (command == MSC_SCSI_TEST_UNIT_READY) {
+ /* TEST_UNIT_READY: Device intends to transfer no data (Dn). */
+ res = check_dn_cases(msc, cbw);
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ res = MSC_UNIT_READY(msc, lun);
+ if (res < 0) {
+ /* Set error */
+ set_scsi_sense(msc, res);
+ send_csw(msc, cbw_length, MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+ send_csw(msc, cbw_length, MSC_STATUS_PASSED);
+ }
+ else if (command == MSC_SCSI_READ_CAPACITY_10) {
+ struct scsi_capacity_response *resp =
+ (struct scsi_capacity_response *)
+ usb_get_in_buffer(msc->in_endpoint);
+ uint32_t block_size, num_blocks;
+ bool write_protect;
+
+ /* Read Capacity 10: Device intends to send data
+ * to the host (Di) */
+ res = check_di_cases(msc, cbw, sizeof(*resp));
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ res = MSC_GET_STORAGE_INFORMATION(
+ msc, lun,
+ &block_size, &num_blocks, &write_protect);
+ if (res < 0) {
+ /* Stall and set error */
+ set_scsi_sense(msc, res);
+ stall_in_and_set_status(
+ msc, cbw_length, MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+ /* Pack and send the response buffer */
+ resp->last_block = num_blocks - 1;
+ resp->block_length = block_size;
+ swap4(&resp->last_block);
+ swap4(&resp->block_length);
+ usb_send_in_buffer(msc->in_endpoint, sizeof(*resp));
+
+ /* Save off block_size */
+ msc->block_size[lun] = block_size;
+
+ set_data_in_endpoint_state(msc, cbw_length, sizeof(*resp));
+ }
+ else if (command == MSC_SCSI_REQUEST_SENSE) {
+ uint32_t scsi_request_len;
+ struct msc_scsi_request_sense_command *cmd =
+ (struct msc_scsi_request_sense_command *) cbw->CBWCB;
+ struct scsi_sense_response *resp =
+ (struct scsi_sense_response *)
+ usb_get_in_buffer(msc->in_endpoint);
+
+ scsi_request_len = MIN(cmd->allocation_length, sizeof(*resp));
+
+ /* REQUEST_SENSE: Device intends to send data
+ * to the host (Di) */
+ res = check_di_cases(msc, cbw, scsi_request_len);
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ memset(resp, 0, sizeof(*resp));
+ resp->response_code = SCSI_SENSE_CURRENT_ERRORS;
+ resp->flags = msc->sense_key;
+ resp->additional_sense_length = 0xa;
+ resp->additional_sense_code = msc->additional_sense_code;
+
+ usb_send_in_buffer(msc->in_endpoint, scsi_request_len);
+
+ set_data_in_endpoint_state(msc, cbw_length, scsi_request_len);
+ }
+ else if (command == MSC_SCSI_MODE_SENSE_6) {
+ uint32_t block_size, num_blocks;
+ int8_t res;
+ bool write_protect;
+
+ struct msc_scsi_mode_sense_6_command *cmd =
+ (struct msc_scsi_mode_sense_6_command *) cbw->CBWCB;
+ struct scsi_mode_sense_response *resp =
+ (struct scsi_mode_sense_response *)
+ usb_get_in_buffer(msc->in_endpoint);
+
+ /* MODE_SENSE(6): Device intends to send data
+ * to the host (Di) */
+ res = check_di_cases(msc, cbw, sizeof(*resp));
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ /* Look for page code 0x3f, subpage code 0x0. */
+ if (cmd->pc_page_code != 0x3f || cmd->subpage_code != 0) {
+ msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST;
+ msc->additional_sense_code =
+ SCSI_ASC_INVALID_FIELD_IN_COMMAND_PACKET;
+
+ /* Stall and send the status after the stall. */
+ stall_in_and_set_status(msc,
+ cbw_length,
+ MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+ res = MSC_GET_STORAGE_INFORMATION(msc, lun, &block_size,
+ &num_blocks, &write_protect);
+ if (res < 0) {
+ /* Stall and set error */
+ set_scsi_sense(msc, res);
+ stall_in_and_set_status(
+ msc, cbw_length, MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+#ifndef MSC_WRITE_SUPPORT
+ /* Force write-protect on if write is not supported */
+ write_protect = true;
+#endif
+ resp->mode_data_length =
+ sizeof(struct scsi_mode_sense_response) - 1;
+ resp->medium_type = 0x0; /* 0 = SBC */
+ resp->device_specific_parameter = (write_protect)? 0x80: 0;
+ resp->block_descriptor_length = 0;
+
+ usb_send_in_buffer(msc->in_endpoint, sizeof(*resp));
+ set_data_in_endpoint_state(msc, cbw_length, sizeof(*resp));
+ }
+ else if (command == MSC_SCSI_START_STOP_UNIT) {
+ int8_t res;
+ bool start, load_eject;
+
+ struct msc_scsi_start_stop_unit *cmd =
+ (struct msc_scsi_start_stop_unit *) cbw->CBWCB;
+
+ /* START STOP UNIT: Device intends to not send or receive
+ * any data (Dn). */
+ res = check_dn_cases(msc, cbw);
+ if (res < 0)
+ goto fail;
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ /* Only accept power condition 0x0, START_VALID */
+ if ((cmd->command & 0xf0) != 0)
+ goto fail;
+
+ start = ((cmd->command & 0x1) != 0);
+ load_eject = ((cmd->command & 0x2) != 0);
+
+ res = MSC_START_STOP_UNIT(msc, lun, start, load_eject);
+ if (res < 0) {
+ set_scsi_sense(msc, res);
+ send_csw(msc, cbw_length, MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+ send_csw(msc, 0, MSC_STATUS_PASSED);
+ }
+ else if (command == MSC_SCSI_READ_10) {
+ uint32_t scsi_request_len;
+ struct msc_scsi_read_10_command *cmd =
+ (struct msc_scsi_read_10_command *) cbw->CBWCB;
+
+ swap4(&cmd->logical_block_address);
+ swap2(&cmd->transfer_length); /* length in blocks */
+
+ if (usb_in_endpoint_busy(msc->in_endpoint))
+ goto fail;
+
+ scsi_request_len = cmd->transfer_length * msc->block_size[lun];
+
+ /* Handle the nonsensical, but possible case of the host
+ * asking to read 0 bytes in the SCSI. That actually makes
+ * this a Dn case rather than a Di case. */
+ if (scsi_request_len == 0) {
+ res = check_dn_cases(msc, cbw);
+ if (res < 0)
+ goto fail;
+
+ /* If check_dn_cases() succeeded, then the host is
+ * not expecting any data, so send the CSW*/
+ send_csw(msc, 0, MSC_STATUS_PASSED);
+ goto fail; /* Not a failure, but handled the same */
+ }
+
+ /* READ(10): Device intends to send data to the host (Di) */
+ res = check_di_cases(msc, cbw, scsi_request_len);
+ if (res < 0)
+ goto fail;
+
+ /* Set up the transport state. It's important that this is
+ * done before the call to the MSC_START_READ() callback
+ * below, because the application could concievably start
+ * calling msc_send_to_host() from the callback. */
+ msc->requested_bytes = MIN(cbw_length, scsi_request_len);
+ msc->requested_bytes_cbw = cbw_length;
+ msc->transferred_bytes = 0;
+ msc->state = MSC_DATA_TRANSPORT_IN;
+
+ /* Start the Data-Transport. After receiving the call to
+ * MSC_START_READ() the application will repeatedly call
+ * msc_send_to_host() with data read from the medium
+ * and then call msc_data_complete() when finished. */
+ res = MSC_START_READ(msc, lun,
+ cmd->logical_block_address,
+ cmd->transfer_length);
+ if (res < 0) {
+ set_scsi_sense(msc, res);
+ stall_in_and_set_status(msc,
+ cbw_length,
+ MSC_STATUS_FAILED);
+
+ /* Reset the state */
+ msc->requested_bytes = 0;
+ msc->requested_bytes_cbw = 0;
+ msc->state = MSC_IDLE;
+ goto fail;
+ }
+ }
+#ifdef MSC_WRITE_SUPPORT
+ else if (command == MSC_SCSI_WRITE_10) {
+ uint32_t scsi_request_len;
+ int8_t res;
+ struct msc_scsi_write_10_command *cmd =
+ (struct msc_scsi_write_10_command *) cbw->CBWCB;
+
+ swap4(&cmd->logical_block_address);
+ swap2(&cmd->transfer_length); /* length in blocks */
+
+ scsi_request_len = cmd->transfer_length * msc->block_size[lun];
+
+ /* Handle the nonsensical, but possible case of the host
+ * asking to write 0 bytes in the SCSI command. That actually
+ * makes this a Dn case rather than a Do case, and is
+ * required by the USBCV test. */
+ if (scsi_request_len == 0) {
+ res = check_dn_cases(msc, cbw);
+ if (res < 0)
+ goto fail;
+
+ /* If check_dn_cases() succeeded, then the host is
+ * not expecting to write any data, so send the CSW */
+ send_csw(msc, 0, MSC_STATUS_PASSED);
+ goto fail; /* Not a failure, but handled the same */
+ }
+
+ /* Write(10): Device intends to receive data
+ * from the host (Do) */
+ res = check_do_cases(msc, cbw, scsi_request_len);
+ if (res < 0)
+ goto fail;
+
+ /* Start the Data-Transport. The application will give
+ * a buffer to put the data into. */
+ res = MSC_START_WRITE(msc,
+ lun,
+ cmd->logical_block_address,
+ cmd->transfer_length,
+ &msc->rx_buf,
+ &msc->rx_buf_len,
+ &msc->operation_complete_callback);
+
+ if (res < 0) {
+ set_scsi_sense(msc, res);
+ stall_out_and_set_status(msc,
+ cbw_length,
+ MSC_STATUS_FAILED);
+ goto fail;
+ }
+
+ /* Initialize the data transport */
+ msc->requested_bytes = scsi_request_len;
+ msc->requested_bytes_cbw = cbw_length;
+ msc->transferred_bytes = 0;
+ msc->rx_buf_cur = msc->rx_buf;
+ msc->state = MSC_DATA_TRANSPORT_OUT;
+ }
+#endif /* MSC_WRITE_SUPPORT */
+ else {
+ /* Unsupported command. See Axelson, page 69. */
+ const bool direc_is_in = direction_is_in(cbw->bmCBWFlags);
+
+ /* Set error codes which will be requested with REQUEST_SENSE
+ * by the host later. */
+ msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST;
+ msc->additional_sense_code =
+ SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
+
+ /* Stall appropriate endpoint and send FAILED for the CSW. */
+ if (direc_is_in || cbw_length == 0)
+ stall_in_and_set_status(msc,
+ cbw_length, MSC_STATUS_FAILED);
+ else
+ stall_out_and_set_status(msc,
+ cbw_length, MSC_STATUS_FAILED);
+
+ goto fail;
+ }
+
+ return;
+
+bad_cbw:
+ /* If the CBW is not valid or is not meaningful, then stall both
+ * endpoints until a Reset Recovery (BOT 5.3.4) procedure is
+ * completed by the host (BOT 5.3, Figure 2). */
+ usb_halt_ep_in(msc->in_endpoint);
+ usb_halt_ep_out(msc->out_endpoint);
+ msc->state = MSC_NEEDS_RESET_RECOVERY;
+fail:
+ return;
+}
+
+void msc_clear_halt(uint8_t endpoint, uint8_t direction)
+{
+ struct msc_application_data *msc;
+
+ msc = get_app_data_by_endpoint(endpoint, direction);
+ if (!msc)
+ return;
+
+ if (msc->state == MSC_CSW) {
+ send_csw(msc, msc->residue, msc->status);
+ msc->state = MSC_IDLE;
+ }
+ else if (msc->state == MSC_NEEDS_RESET_RECOVERY) {
+ /* The device needs a Reset Recovery (BOT 5.3.4) but the
+ * host has not performed a Bulk-Only Mass Storage Reset
+ * (BOT 3.1) control transfer yet, so the endpoint must
+ * remain stalled. (BOT 5.3, Figure 2) */
+ if (direction)
+ usb_halt_ep_in(endpoint);
+ else
+ usb_halt_ep_out(endpoint);
+ }
+}
+
+void msc_in_transaction_complete(uint8_t endpoint)
+{
+ struct msc_application_data *msc;
+
+ msc = get_app_data_by_endpoint(endpoint, 1/*IN*/);
+ if (!msc)
+ return;
+
+ if (msc->state == MSC_DATA_TRANSPORT_IN) {
+ send_next_data_transaction(msc);
+ }
+ else if (msc->state == MSC_STALL) {
+ usb_halt_ep_in(msc->in_endpoint);
+ msc->state = MSC_CSW;
+ }
+ else if (msc->state == MSC_CSW) {
+ send_csw(msc, msc->residue, msc->status);
+ msc->state = MSC_IDLE;
+ }
+}
+
+void msc_out_transaction_complete(uint8_t endpoint)
+{
+ struct msc_application_data *msc;
+ const unsigned char *out_buf;
+ uint16_t out_buf_len;
+#ifdef MSC_WRITE_SUPPORT
+ uint8_t res;
+#endif
+
+ msc = get_app_data_by_endpoint(endpoint, 0/*OUT*/);
+ if (!msc)
+ return;
+
+ /* If the OUT endpoint is halted because of a failed write (from
+ * msc_notify_write_operation_complete(), while interrupts are
+ * disabled), it's possible that a transaction completed before the
+ * endpoint was halted, and that interrupt would cause this
+ * function to be called on a halted endpoint. Since in that case
+ * this is a stray interrupt, there's no need to re-arm the
+ * endpoint. */
+ if (usb_out_endpoint_halted(endpoint))
+ return;
+
+ out_buf_len = usb_get_out_buffer(endpoint, &out_buf);
+
+#ifdef MSC_WRITE_SUPPORT
+ if (msc->state == MSC_DATA_TRANSPORT_OUT) {
+ /* In the DATA_TRANSPORT_OUT state, treat this transaction
+ * as data which is to be written to the medium. This call
+ * may fail if the application's buffer is full, in which
+ * case the endpoint will not be re-armed, and the data
+ * will remain in the endpoint buffer until there is space
+ * in the application's buffer. */
+ res = receive_data(msc, out_buf, out_buf_len);
+ }
+ else if (msc->state == MSC_IDLE) {
+ process_msc_command(msc, out_buf, out_buf_len);
+ res = 0;
+ }
+ else {
+ /* OUT transaction completed when the device is not in a
+ * state that can handle it. Ignore the data. */
+ res = 0;
+ }
+
+ /* If the data could not be handled by the application yet (because
+ * the application's buffer is full), don't re-arm the endpoint now.
+ * Keep the received data in the endpoint's buffer until the
+ * application has reset its own buffer (and has signaled such by
+ * calling msc_write_complete()). At that point, the endpoint's
+ * data will be handled and the endpoint re-armed. */
+ if (res == 0)
+ usb_arm_out_endpoint(endpoint);
+ else
+ msc->out_ep_missed_transactions++;
+#else
+ /* If read-only, then OUT transactions are always processed
+ * and fully handled, leaving no reason to have missed
+ * transactions, as above. */
+ if (msc->state == MSC_IDLE)
+ process_msc_command(msc, out_buf, out_buf_len);
+ usb_arm_out_endpoint(endpoint);
+#endif
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/src/usb_priv.h Sat Feb 18 16:25:38 2017 +0000
@@ -0,0 +1,44 @@
+/*
+ * M-Stack Private Header File - Don't use outside of this directory
+ * Copyright (C) 2013 Alan Ott <alan@signal11.us>
+ * Copyright (C) 2013 Signal 11 Software
+ *
+ * 2015-04-04
+ *
+ * M-Stack is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, version 3; or the Apache License, version 2.0
+ * as published by the Apache Software Foundation. If you have purchased a
+ * commercial license for this software from Signal 11 Software, your
+ * commerical license superceeds the information in this header.
+ *
+ * M-Stack 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * You should have received a copy of the Apache License, verion 2.0 along
+ * with this software. If not, see <http://www.apache.org/licenses/>.
+ */
+
+#ifndef USB_PRIV_H__
+#define USB_PRIV_H__
+
+#include <usb_config.h>
+
+/* Private M-Stack functions */
+#ifdef USB_USE_INTERRUPTS
+/* Manipulate the transaction (token) interrupt. There is no stack used
+ * here, so care must be used to ensure that calls to these functions are
+ * not nested. */
+void usb_disable_transaction_interrupt();
+void usb_enable_transaction_interrupt();
+#else
+#define usb_disable_transaction_interrupt
+#define usb_enable_transaction_interrupt
+#endif
+
+#endif /* USB_PRIV_H__ */
--- a/usb/usb_config.h Sat Feb 18 16:14:32 2017 +0000
+++ b/usb/usb_config.h Sat Feb 18 16:25:38 2017 +0000
@@ -110,4 +110,12 @@
#define CDC_SEND_BREAK_CALLBACK app_send_break_callback
#endif
+/* Define min/max for usb_cdc.c */
+#ifndef min
+#define min(X,Y) (((X) < (Y)) ? (X) : (Y))
+#endif
+#ifndef max
+#define max(X,Y) (((X) > (Y)) ? (X) : (Y))
+#endif
+
#endif /* USB_CONFIG_H__ */