diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fe2447..b5b027c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,6 @@ # The directories below contain optional target libraries add_subdirectory(drivers/device_key EXCLUDE_FROM_ALL) -add_subdirectory(drivers/usb EXCLUDE_FROM_ALL) add_subdirectory(features EXCLUDE_FROM_ALL) add_subdirectory(cmsis/CMSIS_5/CMSIS/RTOS2 EXCLUDE_FROM_ALL) add_subdirectory(cmsis/device/rtos EXCLUDE_FROM_ALL) diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index dc5c85e..d74e1eb 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -53,3 +53,4 @@ source/Watchdog.cpp ) +add_subdirectory(usb) diff --git a/drivers/usb/CMakeLists.txt b/drivers/usb/CMakeLists.txt index 98f51d2..bfc3578 100644 --- a/drivers/usb/CMakeLists.txt +++ b/drivers/usb/CMakeLists.txt @@ -1,17 +1,14 @@ # Copyright (c) 2020 ARM Limited. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -add_library(mbed-usb INTERFACE) +if(MBED_ENABLE_OS_INTERNAL_TESTS) + if(MBED_BUILD_GREENTEA_TESTS) + add_subdirectory(tests/TESTS) + endif() +endif() -target_include_directories(mbed-usb - INTERFACE - include - include/usb - include/usb/internal -) - -target_sources(mbed-usb - INTERFACE +if("DEVICE_USBDEVICE=1" IN_LIST MBED_TARGET_DEFINITIONS) + add_library(mbed-usb STATIC EXCLUDE_FROM_ALL source/AsyncOp.cpp source/ByteBuffer.cpp source/EndpointResolver.cpp @@ -21,15 +18,43 @@ source/TaskBase.cpp source/USBAudio.cpp source/USBCDC.cpp - source/USBCDC_ECM.cpp source/USBDevice.cpp source/USBHID.cpp source/USBKeyboard.cpp source/USBMIDI.cpp - source/USBMSD.cpp source/USBMouse.cpp source/USBMouseKeyboard.cpp - source/USBSerial.cpp -) + source/USBSerial.cpp) -target_link_libraries(mbed-usb INTERFACE mbed-storage) + target_include_directories(mbed-usb + PUBLIC + include + include/usb + include/usb/internal + ) + + target_link_libraries(mbed-usb PUBLIC mbed-core-flags) + + # USB Mass Storage Device library is separate because it pulls in a dependency on mbed-storage-blockdevice + add_library(mbed-usb-msd STATIC EXCLUDE_FROM_ALL + source/msd/USBMSD.cpp) + + target_include_directories(mbed-usb-msd + PUBLIC + include/usb/msd + ) + + target_link_libraries(mbed-usb-msd PUBLIC mbed-usb mbed-storage-blockdevice) + + # USB CDC ECM library is separate because it pulls in a dependency on mbed-rtos-flags + add_library(mbed-usb-cdc-ecm STATIC EXCLUDE_FROM_ALL + source/cdc_ecm/USBCDC_ECM.cpp) + + + target_include_directories(mbed-usb-cdc-ecm + PUBLIC + include/usb/cdc_ecm + ) + + target_link_libraries(mbed-usb-cdc-ecm PUBLIC mbed-usb mbed-rtos-flags) +endif() \ No newline at end of file diff --git a/drivers/usb/include/usb/USBCDC_ECM.h b/drivers/usb/include/usb/USBCDC_ECM.h deleted file mode 100644 index 6f01053..0000000 --- a/drivers/usb/include/usb/USBCDC_ECM.h +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2018-2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef USBCDC_ECM_H -#define USBCDC_ECM_H - -#if defined(MBED_CONF_RTOS_PRESENT) || defined(DOXYGEN_ONLY) -#include "USBDescriptor.h" -#include "USBDevice.h" -#include "ByteBuffer.h" -#include "rtos/Mutex.h" -#include "EventFlags.h" -#include "events/EventQueue.h" -#include "rtos/Thread.h" -#include "Callback.h" - -#define MAX_PACKET_SIZE_INT (64) -#define MAX_PACKET_SIZE_BULK (64) -#define MAX_PACKET_SIZE_EP0 (64) -#define DEFAULT_CONFIGURATION (1) - -#define PACKET_TYPE_PROMISCUOUS (1<<0) -#define PACKET_TYPE_ALL_MULTICAST (1<<1) -#define PACKET_TYPE_DIRECTED (1<<2) -#define PACKET_TYPE_BROADCAST (1<<3) -#define PACKET_TYPE_MULTICAST (1<<4) - -/** - * \defgroup drivers_USBCDC_ECM USBCDC_ECM class - * \ingroup drivers-public-api-usb - * @{ - * @note Bare metal profile: This class is not supported. - */ - -class USBCDC_ECM: public USBDevice { -public: - - /** - * Basic constructor - * - * Construct this object optionally connecting and blocking until it is ready. - * - * @note Do not use this constructor in derived classes. - * - * @param connect_blocking true to perform a blocking connect, false to start in a disconnected state - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - - USBCDC_ECM(bool connect_blocking = true, uint16_t vendor_id = 0x0700, uint16_t product_id = 0x0101, uint16_t product_release = 0x0001); - - /** - * Fully featured constructor - * - * Construct this object with the supplied USBPhy and parameters. The user - * this object is responsible for calling connect() or init(). - * - * @note Derived classes must use this constructor and call init() or - * connect() themselves. Derived classes should also call deinit() in - * their destructor. This ensures that no interrupts can occur when the - * object is partially constructed or destroyed. - * - * @param phy USB phy to use - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBCDC_ECM(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ - virtual ~USBCDC_ECM(); - - /** - * Check if this class is ready - * - * @return true if configured, false otherwise - */ - bool ready(); - - /** - * Block until this device is configured - */ - void wait_ready(); - - /** - * Send a buffer - * - * This function blocks until the full contents have been sent. - * - * @param buffer buffer to be sent - * @param size length of the buffer - * @returns true if successful false if interrupted due to a state change - */ - bool send(uint8_t *buffer, uint32_t size); - - /** - * Read from the receive buffer - * - * @param buffer buffer to fill with data - * @param size maximum number of bytes read - * @param actual a pointer to where to store the number of bytes actually received - */ - void receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual); - - /** - * Return ethernet packet filter bitmap - * - * The Packet Filter is the inclusive OR of the bitmap - * D0: PACKET_TYPE_PROMISCUOUS - * D1: PACKET_TYPE_ALL_MULTICAST - * D2: PACKET_TYPE_DIRECTED - * D3: PACKET_TYPE_BROADCAST - * D4: PACKET_TYPE_MULTICAST - * D5-D15: Reserved (zero) - * - * @return ethernet packet filter bitmap - */ - uint16_t read_packet_filter(); - - /** - * Attach a callback for when an ethernet packet is received - * - * @param cb code to call when a packet is received - */ - void attach_rx(mbed::Callback cb); - - /** - * Attach a callback for when a request to configure device ethernet - * packet filter is received - * - * @param cb code to call when a packet filter request is received - */ - void attach_filter(mbed::Callback cb); - -protected: - - /* - * Called when USB changes state - * - * @param new_state The new state of the USBDevice - * - * Warning: Called in ISR context - */ - virtual void callback_state_change(DeviceState new_state); - - /* - * This is used to handle extensions to standard requests - * and class specific requests with a data phase - */ - virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); - - /* - * Called by USBDevice layer. Set configuration of the device. - * For instance, you can add all endpoints that you need on this function. - * - * @param configuration Number of the configuration - * @returns true if class handles this request - */ - virtual void callback_set_configuration(uint8_t configuration); - - /* - * Called by USBDevice layer in response to set_interface. - * - * Upon reception of this command endpoints of any previous interface - * if any must be removed with endpoint_remove and new endpoint added with - * endpoint_add. - * - * @param configuration Number of the configuration - * - * Warning: Called in ISR context - */ - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - - /* - * Get device descriptor. - * - * @returns pointer to the device descriptor - */ - virtual const uint8_t *device_desc(); - - /* - * Get string product descriptor - * - * @returns pointer to the string product descriptor - */ - virtual const uint8_t *string_iproduct_desc(); - - /* - * Get string configuration descriptor - * - * @returns pointer to the string configuration descriptor - */ - virtual const uint8_t *string_iconfiguration_desc(); - - /* - * Get string serial descriptor - * - * @returns pointer to the string serial descriptor - */ - virtual const uint8_t *string_iserial_desc(); - - /* - * Get configuration descriptor - * - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - - /* - * This is used to handle extensions to standard requests - * and class specific requests - */ - virtual void callback_request(const setup_packet_t *setup); - - /* - * Called by USBDevice layer on bus reset. - * - * complete_reset must be called after - * the device is fully reset. - * - * Warning: Called in ISR context - */ - virtual void callback_reset(); - - uint8_t device_descriptor[18]; - -private: - - usb_ep_t _int_in; - usb_ep_t _bulk_in; - usb_ep_t _bulk_out; - - uint8_t _config_descriptor[80]; - uint8_t _string_imac_addr[26]; - - uint8_t _bulk_buf[MAX_PACKET_SIZE_BULK]; - uint16_t _packet_filter; - ByteBuffer _rx_queue; - - rtos::EventFlags _flags; - rtos::Mutex _write_mutex; - - events::EventQueue _queue; - rtos::Thread _thread; - mbed::Callback _callback_rx; - mbed::Callback _callback_filter; - - void _init(); - void _int_callback(); - void _bulk_in_callback(); - void _bulk_out_callback(); - bool _notify_network_connection(uint8_t value); - bool _notify_connection_speed_change(uint32_t up, uint32_t down); - bool _write_bulk(uint8_t *buffer, uint32_t size); - void _notify_connect(); -}; - -/** @}*/ -#endif // defined(MBED_CONF_RTOS_PRESENT) -#endif diff --git a/drivers/usb/include/usb/USBMSD.h b/drivers/usb/include/usb/USBMSD.h deleted file mode 100644 index a570df7..0000000 --- a/drivers/usb/include/usb/USBMSD.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2018-2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef USBMSD_H -#define USBMSD_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" -#include "platform/Callback.h" -#include "usb/internal/PolledQueue.h" -#include "usb/internal/Task.h" -#include "BlockDevice.h" -#include "rtos/Mutex.h" - -#include "USBDevice.h" - -/** - * \defgroup drivers_USBMSD USBMSD class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** - * USBMSD class: generic class in order to use all kinds of blocks storage chip - * - * Introduction - * - * USBMSD implements the MSD protocol. It permits to access a block device (flash, SD Card,...) - * from a computer over USB. - * - * @code - * #include "mbed.h" - * #include "SDBlockDevice.h" - * #include "USBMSD.h" - * - * SDBlockDevice sd(PTE3, PTE1, PTE2, PTE4); - * USBMSD usb(&sd); - * - * int main() { - * - * while(true) { - * usb.process(); - * } - * - * return 0; - * } - * @endcode - */ -class USBMSD: public USBDevice { -public: - - /** - * Constructor - * - * This creates a new USBMSD object with the given block device. Connect must be called - * for the block device to connect. - * - * @param bd BlockDevice to mount as a USB drive - * @param connect_blocking true to perform a blocking connect, false to start in a disconnected state - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your preoduct_release - */ - USBMSD(mbed::BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001); - - /** - * Fully featured constructor - * - * Construct this object with the supplied USBPhy and parameters. The user - * this object is responsible for calling connect() or init(). - * - * @note Derived classes must use this constructor and call init() or - * connect() themselves. Derived classes should also call deinit() in - * their destructor. This ensures that no interrupts can occur when the - * object is partially constructed or destroyed. - * - * @param phy USB phy to use - * @param bd BlockDevice to mount as a USB drive - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your preoduct_release - */ - USBMSD(USBPhy *phy, mbed::BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call disconnect - * before this destructor runs. - */ - virtual ~USBMSD(); - - /** - * Connect the USB MSD device. - * - * @returns true if successful - */ - bool connect(); - - /** - * Disconnect the USB MSD device. - */ - void disconnect(); - - /** - * Perform USB processing - */ - void process(); - - /** - * Called when USBMSD needs to perform processing - * - * @param cb Callback called when USBMSD needs process() to be called - */ - void attach(mbed::Callback cb); - - /** - * Check if MSD device was removed/unmounted on the host side. - * - * @returns true if device was removed/unmounted on the host side - */ - bool media_removed(); - -protected: - - /* - * read one or more blocks on a storage chip - * - * @param data pointer where will be stored read data - * @param block starting block number - * @param count number of blocks to read - * @returns 0 if successful - */ - virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count); - - /* - * write one or more blocks on a storage chip - * - * @param data data to write - * @param block starting block number - * @param count number of blocks to write - * @returns 0 if successful - */ - virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count); - - /* - * Disk initilization - */ - virtual int disk_initialize(); - - /* - * Return the number of blocks - * - * @returns number of blocks - */ - virtual uint64_t disk_sectors(); - - /* - * Return memory size - * - * @returns memory size - */ - virtual uint64_t disk_size(); - - /* - * To check the status of the storage chip - * - * @returns status: 0: OK, 1: disk not initialized, 2: no medium in the drive, 4: write protected - */ - virtual int disk_status(); - -private: - - // MSC Bulk-only Stage - enum Stage { - READ_CBW, // wait a CBW - ERROR, // error - PROCESS_CBW, // process a CBW request - SEND_CSW, // send a CSW - }; - - // Bulk-only CBW - typedef MBED_PACKED(struct) - { - uint32_t Signature; - uint32_t Tag; - uint32_t DataLength; - uint8_t Flags; - uint8_t LUN; - uint8_t CBLength; - uint8_t CB[16]; - } CBW; - - // Bulk-only CSW - typedef MBED_PACKED(struct) - { - uint32_t Signature; - uint32_t Tag; - uint32_t DataResidue; - uint8_t Status; - } CSW; - - // If this class has been initialized - bool _initialized; - - // If msd device has been unmounted by host - volatile bool _media_removed; - - //state of the bulk-only state machine - Stage _stage; - - // current CBW - CBW _cbw; - - // CSW which will be sent - CSW _csw; - - // addr where will be read or written data - uint32_t _addr; - - // length of a reading or writing - uint32_t _length; - - // memory OK (after a memoryVerify) - bool _mem_ok; - - // cache in RAM before writing in memory. Useful also to read a block. - uint8_t *_page; - - int _block_size; - uint64_t _memory_size; - uint64_t _block_count; - - // endpoints - usb_ep_t _bulk_in; - usb_ep_t _bulk_out; - uint8_t _bulk_in_buf[64]; - uint8_t _bulk_out_buf[64]; - bool _out_ready; - bool _in_ready; - uint32_t _bulk_out_size; - - // Interrupt to thread deferral - events::PolledQueue _queue; - events::Task _in_task; - events::Task _out_task; - events::Task _reset_task; - events::Task _control_task; - events::Task _configure_task; - - mbed::BlockDevice *_bd; - rtos::Mutex _mutex_init; - rtos::Mutex _mutex; - - // space for config descriptor - uint8_t _configuration_descriptor[32]; - - virtual const uint8_t *string_iproduct_desc(); - virtual const uint8_t *string_iinterface_desc(); - virtual const uint8_t *configuration_desc(uint8_t index); - virtual void callback_set_configuration(uint8_t configuration); - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - virtual void callback_state_change(DeviceState new_state); - virtual void callback_request(const setup_packet_t *setup); - virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); - - void _isr_out(); - void _isr_in(); - - void _out(); - void _in(); - void _reset(); - void _control(const setup_packet_t *request); - void _configure(); - - void _init(); - void _process(); - void _write_next(uint8_t *data, uint32_t size); - void _read_next(); - - void CBWDecode(uint8_t *buf, uint16_t size); - void sendCSW(void); - bool inquiryRequest(void); - bool write(uint8_t *buf, uint16_t size); - bool readFormatCapacity(); - bool readCapacity(void); - bool infoTransfer(void); - void memoryRead(void); - bool modeSense6(void); - bool modeSense10(void); - void testUnitReady(void); - bool requestSense(void); - void memoryVerify(uint8_t *buf, uint16_t size); - void memoryWrite(uint8_t *buf, uint16_t size); - void msd_reset(); - void fail(); -}; - -/** @}*/ - -#endif diff --git a/drivers/usb/include/usb/cdc_ecm/USBCDC_ECM.h b/drivers/usb/include/usb/cdc_ecm/USBCDC_ECM.h new file mode 100644 index 0000000..6f01053 --- /dev/null +++ b/drivers/usb/include/usb/cdc_ecm/USBCDC_ECM.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBCDC_ECM_H +#define USBCDC_ECM_H + +#if defined(MBED_CONF_RTOS_PRESENT) || defined(DOXYGEN_ONLY) +#include "USBDescriptor.h" +#include "USBDevice.h" +#include "ByteBuffer.h" +#include "rtos/Mutex.h" +#include "EventFlags.h" +#include "events/EventQueue.h" +#include "rtos/Thread.h" +#include "Callback.h" + +#define MAX_PACKET_SIZE_INT (64) +#define MAX_PACKET_SIZE_BULK (64) +#define MAX_PACKET_SIZE_EP0 (64) +#define DEFAULT_CONFIGURATION (1) + +#define PACKET_TYPE_PROMISCUOUS (1<<0) +#define PACKET_TYPE_ALL_MULTICAST (1<<1) +#define PACKET_TYPE_DIRECTED (1<<2) +#define PACKET_TYPE_BROADCAST (1<<3) +#define PACKET_TYPE_MULTICAST (1<<4) + +/** + * \defgroup drivers_USBCDC_ECM USBCDC_ECM class + * \ingroup drivers-public-api-usb + * @{ + * @note Bare metal profile: This class is not supported. + */ + +class USBCDC_ECM: public USBDevice { +public: + + /** + * Basic constructor + * + * Construct this object optionally connecting and blocking until it is ready. + * + * @note Do not use this constructor in derived classes. + * + * @param connect_blocking true to perform a blocking connect, false to start in a disconnected state + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + + USBCDC_ECM(bool connect_blocking = true, uint16_t vendor_id = 0x0700, uint16_t product_id = 0x0101, uint16_t product_release = 0x0001); + + /** + * Fully featured constructor + * + * Construct this object with the supplied USBPhy and parameters. The user + * this object is responsible for calling connect() or init(). + * + * @note Derived classes must use this constructor and call init() or + * connect() themselves. Derived classes should also call deinit() in + * their destructor. This ensures that no interrupts can occur when the + * object is partially constructed or destroyed. + * + * @param phy USB phy to use + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBCDC_ECM(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ + virtual ~USBCDC_ECM(); + + /** + * Check if this class is ready + * + * @return true if configured, false otherwise + */ + bool ready(); + + /** + * Block until this device is configured + */ + void wait_ready(); + + /** + * Send a buffer + * + * This function blocks until the full contents have been sent. + * + * @param buffer buffer to be sent + * @param size length of the buffer + * @returns true if successful false if interrupted due to a state change + */ + bool send(uint8_t *buffer, uint32_t size); + + /** + * Read from the receive buffer + * + * @param buffer buffer to fill with data + * @param size maximum number of bytes read + * @param actual a pointer to where to store the number of bytes actually received + */ + void receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual); + + /** + * Return ethernet packet filter bitmap + * + * The Packet Filter is the inclusive OR of the bitmap + * D0: PACKET_TYPE_PROMISCUOUS + * D1: PACKET_TYPE_ALL_MULTICAST + * D2: PACKET_TYPE_DIRECTED + * D3: PACKET_TYPE_BROADCAST + * D4: PACKET_TYPE_MULTICAST + * D5-D15: Reserved (zero) + * + * @return ethernet packet filter bitmap + */ + uint16_t read_packet_filter(); + + /** + * Attach a callback for when an ethernet packet is received + * + * @param cb code to call when a packet is received + */ + void attach_rx(mbed::Callback cb); + + /** + * Attach a callback for when a request to configure device ethernet + * packet filter is received + * + * @param cb code to call when a packet filter request is received + */ + void attach_filter(mbed::Callback cb); + +protected: + + /* + * Called when USB changes state + * + * @param new_state The new state of the USBDevice + * + * Warning: Called in ISR context + */ + virtual void callback_state_change(DeviceState new_state); + + /* + * This is used to handle extensions to standard requests + * and class specific requests with a data phase + */ + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + + /* + * Called by USBDevice layer. Set configuration of the device. + * For instance, you can add all endpoints that you need on this function. + * + * @param configuration Number of the configuration + * @returns true if class handles this request + */ + virtual void callback_set_configuration(uint8_t configuration); + + /* + * Called by USBDevice layer in response to set_interface. + * + * Upon reception of this command endpoints of any previous interface + * if any must be removed with endpoint_remove and new endpoint added with + * endpoint_add. + * + * @param configuration Number of the configuration + * + * Warning: Called in ISR context + */ + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + /* + * Get device descriptor. + * + * @returns pointer to the device descriptor + */ + virtual const uint8_t *device_desc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string configuration descriptor + * + * @returns pointer to the string configuration descriptor + */ + virtual const uint8_t *string_iconfiguration_desc(); + + /* + * Get string serial descriptor + * + * @returns pointer to the string serial descriptor + */ + virtual const uint8_t *string_iserial_desc(); + + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + + /* + * This is used to handle extensions to standard requests + * and class specific requests + */ + virtual void callback_request(const setup_packet_t *setup); + + /* + * Called by USBDevice layer on bus reset. + * + * complete_reset must be called after + * the device is fully reset. + * + * Warning: Called in ISR context + */ + virtual void callback_reset(); + + uint8_t device_descriptor[18]; + +private: + + usb_ep_t _int_in; + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + + uint8_t _config_descriptor[80]; + uint8_t _string_imac_addr[26]; + + uint8_t _bulk_buf[MAX_PACKET_SIZE_BULK]; + uint16_t _packet_filter; + ByteBuffer _rx_queue; + + rtos::EventFlags _flags; + rtos::Mutex _write_mutex; + + events::EventQueue _queue; + rtos::Thread _thread; + mbed::Callback _callback_rx; + mbed::Callback _callback_filter; + + void _init(); + void _int_callback(); + void _bulk_in_callback(); + void _bulk_out_callback(); + bool _notify_network_connection(uint8_t value); + bool _notify_connection_speed_change(uint32_t up, uint32_t down); + bool _write_bulk(uint8_t *buffer, uint32_t size); + void _notify_connect(); +}; + +/** @}*/ +#endif // defined(MBED_CONF_RTOS_PRESENT) +#endif diff --git a/drivers/usb/include/usb/msd/USBMSD.h b/drivers/usb/include/usb/msd/USBMSD.h new file mode 100644 index 0000000..a570df7 --- /dev/null +++ b/drivers/usb/include/usb/msd/USBMSD.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBMSD_H +#define USBMSD_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" +#include "platform/Callback.h" +#include "usb/internal/PolledQueue.h" +#include "usb/internal/Task.h" +#include "BlockDevice.h" +#include "rtos/Mutex.h" + +#include "USBDevice.h" + +/** + * \defgroup drivers_USBMSD USBMSD class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** + * USBMSD class: generic class in order to use all kinds of blocks storage chip + * + * Introduction + * + * USBMSD implements the MSD protocol. It permits to access a block device (flash, SD Card,...) + * from a computer over USB. + * + * @code + * #include "mbed.h" + * #include "SDBlockDevice.h" + * #include "USBMSD.h" + * + * SDBlockDevice sd(PTE3, PTE1, PTE2, PTE4); + * USBMSD usb(&sd); + * + * int main() { + * + * while(true) { + * usb.process(); + * } + * + * return 0; + * } + * @endcode + */ +class USBMSD: public USBDevice { +public: + + /** + * Constructor + * + * This creates a new USBMSD object with the given block device. Connect must be called + * for the block device to connect. + * + * @param bd BlockDevice to mount as a USB drive + * @param connect_blocking true to perform a blocking connect, false to start in a disconnected state + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + */ + USBMSD(mbed::BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001); + + /** + * Fully featured constructor + * + * Construct this object with the supplied USBPhy and parameters. The user + * this object is responsible for calling connect() or init(). + * + * @note Derived classes must use this constructor and call init() or + * connect() themselves. Derived classes should also call deinit() in + * their destructor. This ensures that no interrupts can occur when the + * object is partially constructed or destroyed. + * + * @param phy USB phy to use + * @param bd BlockDevice to mount as a USB drive + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + */ + USBMSD(USBPhy *phy, mbed::BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call disconnect + * before this destructor runs. + */ + virtual ~USBMSD(); + + /** + * Connect the USB MSD device. + * + * @returns true if successful + */ + bool connect(); + + /** + * Disconnect the USB MSD device. + */ + void disconnect(); + + /** + * Perform USB processing + */ + void process(); + + /** + * Called when USBMSD needs to perform processing + * + * @param cb Callback called when USBMSD needs process() to be called + */ + void attach(mbed::Callback cb); + + /** + * Check if MSD device was removed/unmounted on the host side. + * + * @returns true if device was removed/unmounted on the host side + */ + bool media_removed(); + +protected: + + /* + * read one or more blocks on a storage chip + * + * @param data pointer where will be stored read data + * @param block starting block number + * @param count number of blocks to read + * @returns 0 if successful + */ + virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count); + + /* + * write one or more blocks on a storage chip + * + * @param data data to write + * @param block starting block number + * @param count number of blocks to write + * @returns 0 if successful + */ + virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count); + + /* + * Disk initilization + */ + virtual int disk_initialize(); + + /* + * Return the number of blocks + * + * @returns number of blocks + */ + virtual uint64_t disk_sectors(); + + /* + * Return memory size + * + * @returns memory size + */ + virtual uint64_t disk_size(); + + /* + * To check the status of the storage chip + * + * @returns status: 0: OK, 1: disk not initialized, 2: no medium in the drive, 4: write protected + */ + virtual int disk_status(); + +private: + + // MSC Bulk-only Stage + enum Stage { + READ_CBW, // wait a CBW + ERROR, // error + PROCESS_CBW, // process a CBW request + SEND_CSW, // send a CSW + }; + + // Bulk-only CBW + typedef MBED_PACKED(struct) + { + uint32_t Signature; + uint32_t Tag; + uint32_t DataLength; + uint8_t Flags; + uint8_t LUN; + uint8_t CBLength; + uint8_t CB[16]; + } CBW; + + // Bulk-only CSW + typedef MBED_PACKED(struct) + { + uint32_t Signature; + uint32_t Tag; + uint32_t DataResidue; + uint8_t Status; + } CSW; + + // If this class has been initialized + bool _initialized; + + // If msd device has been unmounted by host + volatile bool _media_removed; + + //state of the bulk-only state machine + Stage _stage; + + // current CBW + CBW _cbw; + + // CSW which will be sent + CSW _csw; + + // addr where will be read or written data + uint32_t _addr; + + // length of a reading or writing + uint32_t _length; + + // memory OK (after a memoryVerify) + bool _mem_ok; + + // cache in RAM before writing in memory. Useful also to read a block. + uint8_t *_page; + + int _block_size; + uint64_t _memory_size; + uint64_t _block_count; + + // endpoints + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + uint8_t _bulk_in_buf[64]; + uint8_t _bulk_out_buf[64]; + bool _out_ready; + bool _in_ready; + uint32_t _bulk_out_size; + + // Interrupt to thread deferral + events::PolledQueue _queue; + events::Task _in_task; + events::Task _out_task; + events::Task _reset_task; + events::Task _control_task; + events::Task _configure_task; + + mbed::BlockDevice *_bd; + rtos::Mutex _mutex_init; + rtos::Mutex _mutex; + + // space for config descriptor + uint8_t _configuration_descriptor[32]; + + virtual const uint8_t *string_iproduct_desc(); + virtual const uint8_t *string_iinterface_desc(); + virtual const uint8_t *configuration_desc(uint8_t index); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + + void _isr_out(); + void _isr_in(); + + void _out(); + void _in(); + void _reset(); + void _control(const setup_packet_t *request); + void _configure(); + + void _init(); + void _process(); + void _write_next(uint8_t *data, uint32_t size); + void _read_next(); + + void CBWDecode(uint8_t *buf, uint16_t size); + void sendCSW(void); + bool inquiryRequest(void); + bool write(uint8_t *buf, uint16_t size); + bool readFormatCapacity(); + bool readCapacity(void); + bool infoTransfer(void); + void memoryRead(void); + bool modeSense6(void); + bool modeSense10(void); + void testUnitReady(void); + bool requestSense(void); + void memoryVerify(uint8_t *buf, uint16_t size); + void memoryWrite(uint8_t *buf, uint16_t size); + void msd_reset(); + void fail(); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/source/USBCDC_ECM.cpp b/drivers/usb/source/USBCDC_ECM.cpp deleted file mode 100644 index 45e97ad..0000000 --- a/drivers/usb/source/USBCDC_ECM.cpp +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2018, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#if defined(MBED_CONF_RTOS_PRESENT) -#include -#include "USBCDC_ECM.h" -#include "EndpointResolver.h" -#include "usb_phy_api.h" -#include "mbed_interface.h" -#include "mbed_assert.h" - -#ifndef MAX_SEGMENT_SIZE -#define MAX_SEGMENT_SIZE (1514) -#endif - -#define FLAG_WRITE_DONE (1 << 0) -#define FLAG_DISCONNECT (1 << 1) -#define FLAG_CONNECT (1 << 2) -#define FLAG_INT_DONE (1 << 3) - -#define SET_ETHERNET_MULTICAST_FILTERS 0x40 -#define SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x41 -#define GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x42 -#define SET_ETHERNET_PACKET_FILTER 0x43 -#define GET_ETHERNET_STATISTIC 0x44 - -#define CS_INTERFACE 0x24 -#define NETWORK_CONNECTION 0x00 -#define CONNECTION_SPEED_CHANGE 0x2A -#define LINK_SPEED (10000000) - -USBCDC_ECM::USBCDC_ECM(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(get_usb_phy(), vendor_id, product_id, product_release), _packet_filter(0), _queue(4 * EVENTS_EVENT_SIZE) -{ - _init(); - - if (connect_blocking) { - init(); - USBDevice::connect(); - wait_ready(); - } else { - init(); - } -} - -USBCDC_ECM::USBCDC_ECM(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(phy, vendor_id, product_id, product_release), _packet_filter(0), _queue(4 * EVENTS_EVENT_SIZE) -{ - - _init(); -} - -USBCDC_ECM::~USBCDC_ECM() -{ - deinit(); -} - -void USBCDC_ECM::_init() -{ - EndpointResolver resolver(endpoint_table()); - resolver.endpoint_ctrl(MAX_PACKET_SIZE_EP0); - _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, MAX_PACKET_SIZE_INT); - _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET_SIZE_BULK); - _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET_SIZE_BULK); - - MBED_ASSERT(resolver.valid()); - - _thread.start(callback(&_queue, &events::EventQueue::dispatch_forever)); - _rx_queue.resize(MAX_SEGMENT_SIZE); -} - -void USBCDC_ECM::callback_reset() -{ - assert_locked(); - /* Called in ISR context */ -} - -void USBCDC_ECM::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - assert_locked(); - /* Called in ISR context */ - - complete_request_xfer_done(false); -} - -void USBCDC_ECM::callback_set_configuration(uint8_t configuration) -{ - assert_locked(); - /* Called in ISR context */ - - bool ret = false; - if (configuration == DEFAULT_CONFIGURATION) { - ret = true; - } - - complete_set_configuration(ret); -} - -bool USBCDC_ECM::ready() -{ - return _flags.get() & FLAG_CONNECT ? true : false; -} - -void USBCDC_ECM::wait_ready() -{ - _flags.wait_any(FLAG_CONNECT, osWaitForever, false); -} - -bool USBCDC_ECM::_notify_network_connection(uint8_t value) -{ - _write_mutex.lock(); - - bool ret = true; - uint8_t request[8] = {0}; - - request[0] = 0xA1; - request[1] = NETWORK_CONNECTION; - request[2] = value; - request[3] = 0x00; - - _flags.clear(FLAG_INT_DONE); - USBDevice::write_start(_int_in, request, sizeof(request)); - uint32_t flags = _flags.wait_any(FLAG_INT_DONE | FLAG_DISCONNECT, osWaitForever, false); - if (flags & FLAG_DISCONNECT) { - ret = false; - } - USBDevice::write_finish(_int_in); - - _write_mutex.unlock(); - return ret; -} - -bool USBCDC_ECM::_notify_connection_speed_change(uint32_t up, uint32_t down) -{ - _write_mutex.lock(); - - bool ret = true; - struct notification_t { - uint8_t request[8]; - uint32_t up; - uint32_t down; - }; - - notification_t notification; - memset(¬ification, 0, sizeof(notification)); - - notification.request[0] = 0xA1; - notification.request[1] = CONNECTION_SPEED_CHANGE; - notification.request[2] = 0x00; - notification.request[3] = 0x00; - notification.request[6] = 0x08; - notification.up = up; - notification.down = down; - - _flags.clear(FLAG_INT_DONE); - USBDevice::write_start(_int_in, (uint8_t *)¬ification, sizeof(notification)); - uint32_t flags = _flags.wait_any(FLAG_INT_DONE | FLAG_DISCONNECT, osWaitForever, false); - if (flags & FLAG_DISCONNECT) { - ret = false; - } - USBDevice::write_finish(_int_in); - - _write_mutex.unlock(); - return ret; -} - -void USBCDC_ECM::_notify_connect() -{ - _notify_network_connection(1); - _notify_connection_speed_change(LINK_SPEED, LINK_SPEED); -} - -bool USBCDC_ECM::_write_bulk(uint8_t *buffer, uint32_t size) -{ - bool ret = true; - - _flags.clear(FLAG_WRITE_DONE); - USBDevice::write_start(_bulk_in, buffer, size); - uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false); - if (flags & FLAG_DISCONNECT) { - ret = false; - } - - USBDevice::write_finish(_bulk_in); - return ret; -} - -bool USBCDC_ECM::send(uint8_t *buffer, uint32_t size) -{ - _write_mutex.lock(); - bool ret = true; - uint32_t sent = 0; - uint32_t data_size = 0; - - if (size > MAX_SEGMENT_SIZE) { - _write_mutex.unlock(); - mbed_error_printf("Buffer size is too large\n"); - return false; - } - - uint32_t max_packet = USBDevice::endpoint_max_packet_size(_bulk_in); - - while (size - sent > 0) { - data_size = (size - sent > max_packet) ? max_packet : size - sent; - if (_write_bulk(buffer + sent, data_size)) { - sent += data_size; - } else { - _write_mutex.unlock(); - return false; - } - } - - /* Send zero length packet */ - if (size % max_packet == 0) { - uint8_t buf = 0; - ret = _write_bulk(&buf, 0); - } - - _write_mutex.unlock(); - return ret; -} - -void USBCDC_ECM::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual) -{ - lock(); - - uint32_t available = _rx_queue.size(); - uint32_t copy_size = available > size ? size : available; - _rx_queue.read(buffer, copy_size); - *actual = copy_size; - - unlock(); -} - -void USBCDC_ECM::attach_rx(mbed::Callback cb) -{ - lock(); - - _callback_rx = cb; - - unlock(); -} - -void USBCDC_ECM::attach_filter(mbed::Callback cb) -{ - lock(); - - _callback_filter = cb; - - unlock(); -} - -uint16_t USBCDC_ECM::read_packet_filter() -{ - return _packet_filter; -} - -void USBCDC_ECM::callback_request(const setup_packet_t *setup) -{ - assert_locked(); - - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - if (setup->bmRequestType.Type == CLASS_TYPE) { - //printf("In USBCallback_request: CLASS specific Request: %02x\n", setup->bRequest); - switch (setup->bRequest) { - case SET_ETHERNET_MULTICAST_FILTERS: - /* TODO: Support is optional, not implemented here */ - break; - case SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: - /* TODO: Support is optional, not implemented here */ - break; - case GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: - /* TODO: Support is optional, not implemented here */ - break; - case SET_ETHERNET_PACKET_FILTER: - if (_packet_filter != setup->wValue) { - _packet_filter = setup->wValue; - // Signal that packet filter configuration is changed - if (_callback_filter) { - _callback_filter(); - } - } - result = Success; - break; - case GET_ETHERNET_STATISTIC: - /* TODO: Support is optional, not implemented here */ - break; - default: - result = Failure; - break; - } - } - - complete_request(result, data, size); -} - -void USBCDC_ECM::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - assert_locked(); - /* Called in ISR context */ - - if (alternate) { - _packet_filter = 0; - - endpoint_add(_int_in, MAX_PACKET_SIZE_INT, USB_EP_TYPE_INT, &USBCDC_ECM::_int_callback); - endpoint_add(_bulk_in, MAX_PACKET_SIZE_BULK, USB_EP_TYPE_BULK, &USBCDC_ECM::_bulk_in_callback); - endpoint_add(_bulk_out, MAX_PACKET_SIZE_BULK, USB_EP_TYPE_BULK, &USBCDC_ECM::_bulk_out_callback); - - read_start(_bulk_out, _bulk_buf, MAX_PACKET_SIZE_BULK); - - _queue.call(static_cast(this), &USBCDC_ECM::_notify_connect); - } - - complete_set_interface(true); -} - -void USBCDC_ECM::callback_state_change(DeviceState new_state) -{ - assert_locked(); - /* Called in ISR context */ - - if (new_state == Configured) { - _flags.set(FLAG_CONNECT); - _flags.clear(FLAG_DISCONNECT); - } else { - _flags.set(FLAG_DISCONNECT); - _flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE | FLAG_INT_DONE); - } -} - -const uint8_t *USBCDC_ECM::device_desc() -{ - uint8_t ep0_size = endpoint_max_packet_size(0x00); - uint8_t device_descriptor_temp[] = { - DEVICE_DESCRIPTOR_LENGTH, // bLength - DEVICE_DESCRIPTOR, // bDescriptorType - 0x00, 0x02, // bcdUSB 2.0 - 0x02, // bDeviceClass - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - ep0_size, // bMaxPacketSize0 - (uint8_t)(LSB(vendor_id)), - (uint8_t)(MSB(vendor_id)), // idVendor - (uint8_t)(LSB(product_id)), - (uint8_t)(MSB(product_id)), // idProduct - (uint8_t)(LSB(product_release)), - (uint8_t)(MSB(product_release)),// bcdDevice - STRING_OFFSET_IMANUFACTURER, // iManufacturer - STRING_OFFSET_IPRODUCT, // iProduct - STRING_OFFSET_ISERIAL, // iSerialNumber - 0x01 // bNumConfigurations - }; - MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); - memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); - return device_descriptor; -} - -const uint8_t *USBCDC_ECM::string_iproduct_desc() -{ - static const uint8_t string_iproduct_descriptor[] = { - 26, - STRING_DESCRIPTOR, - 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'E', 0, 't', 0, 'h', 0, 'e', 0, 'r', 0, 'n', 0, 'e', 0, 't', 0 - }; - return string_iproduct_descriptor; -} - -const uint8_t *USBCDC_ECM::string_iconfiguration_desc() -{ - uint8_t string_imac_addr_temp[26] = {0}; - const char unicodes[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F' - }; - char mac[6]; - - mbed_mac_address(mac); - - string_imac_addr_temp[0] = 26; - string_imac_addr_temp[1] = STRING_DESCRIPTOR; - /* Convert MAC address to USB CDC string format */ - for (int i = 0; i < 6; i++) { - string_imac_addr_temp[i * 4 + 2] = unicodes[mac[i] >> 4]; - string_imac_addr_temp[i * 4 + 4] = unicodes[mac[i] & 0xF]; - } - - MBED_ASSERT(sizeof(string_imac_addr_temp) == sizeof(_string_imac_addr)); - memcpy(_string_imac_addr, string_imac_addr_temp, sizeof(string_imac_addr_temp)); - return _string_imac_addr; -} - -const uint8_t *USBCDC_ECM::string_iserial_desc() -{ - static const uint8_t string_iserial_descriptor[] = { - 26, - STRING_DESCRIPTOR, - '0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, 'A', 0, 'B', 0 - }; - return string_iserial_descriptor; -} - -#define CONFIG_DESC_SIZE (9+9+5+5+13+7+9+9+7+7) - -const uint8_t *USBCDC_ECM::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - - uint8_t config_descriptor_temp[] = { - // configuration descriptor, USB spec 9.6.3, page 264-265, Table 9-10 - 0x09, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(CONFIG_DESC_SIZE), // wTotalLength (LSB) - MSB(CONFIG_DESC_SIZE), // wTotalLength (MSB) - 2, // bNumInterfaces - 1, // bConfigurationValue - 0, // iConfiguration - 0xC0, // bmAttributes - 50, // bMaxPower - - // Communication interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - 0x09, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0, // bInterfaceNumber - 0, // bAlternateSetting - 1, // bNumEndpoints - 0x02, // bInterfaceClass - 0x06, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0, // iInterface - - // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 15 - 0x05, // bFunctionLength - CS_INTERFACE, // bDescriptorType - 0x00, // bDescriptorSubtype - 0x20, 0x01, // bcdCDC CDC 1.20 - - // CDC Union Functional Descriptor, CDC Spec 5.2.3.2, Table 16 - 0x05, // bFunctionLength - CS_INTERFACE, // bDescriptorType - 0x06, // bDescriptorSubType - 0, // bControlInterface - 1, // bSubordinateInterface0 - - // CDC Ethernet Networking Functional Descriptor, ECM Spec 5.4, Table 3 - 0x0D, // bFunctionLenght - CS_INTERFACE, // bDescriptorType - 0x0F, // bDescriptorSubtype - STRING_OFFSET_ICONFIGURATION, // iMacAddress - 0, 0, 0, 0, // bmEthernetStatistics - (uint8_t) LSB(MAX_SEGMENT_SIZE), // wMaxSegmentSize (LSB) - (uint8_t) MSB(MAX_SEGMENT_SIZE), // wMaxSegmentSize (MSB) - 0, 0, // wNumberMCFilters - 0, // bNumberPowerFilters - - // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes (0x03=intr) - (uint8_t) LSB(MAX_PACKET_SIZE_INT), // wMaxPacketSize (LSB) - (uint8_t) MSB(MAX_PACKET_SIZE_INT), // wMaxPacketSize (MSB) - 16, // bInterval - - // Default data interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - 0x09, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 0, // bAlternateSetting - 0, // bNumEndpoints - 0x0A, // bInterfaceClass - 0x00, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0, // iInterface - - // Data interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - 0x09, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 1, // bInterfaceNumber - 1, // bAlternateSetting - 2, // bNumEndpoints - 0x0A, // bInterfaceClass - 0x00, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0, // iInterface - - // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _bulk_in, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - (uint8_t) LSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (LSB) - (uint8_t) MSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (MSB) - 0, // bInterval - - // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _bulk_out, // bEndpointAddress - E_BULK, // bmAttributes (0x02=bulk) - (uint8_t) LSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (LSB) - (uint8_t) MSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (MSB) - 0 // bInterval - }; - - MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); - memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp)); - return _config_descriptor; -} - -void USBCDC_ECM::_int_callback() -{ - assert_locked(); - - _flags.set(FLAG_INT_DONE); -} - -void USBCDC_ECM::_bulk_in_callback() -{ - assert_locked(); - - _flags.set(FLAG_WRITE_DONE); -} - -void USBCDC_ECM::_bulk_out_callback() -{ - assert_locked(); - - uint32_t read_size = read_finish(_bulk_out); - - if (read_size <= _rx_queue.free()) { - // Copy data over - _rx_queue.write(_bulk_buf, read_size); - } - - // Signal that there is ethernet packet available - if (_callback_rx && (read_size < USBDevice::endpoint_max_packet_size(_bulk_out))) { - _callback_rx(); - } - - read_start(_bulk_out, _bulk_buf, MAX_PACKET_SIZE_BULK); -} -#endif // defined(MBED_CONF_RTOS_PRESENT) diff --git a/drivers/usb/source/USBMSD.cpp b/drivers/usb/source/USBMSD.cpp deleted file mode 100644 index fcf416c..0000000 --- a/drivers/usb/source/USBMSD.cpp +++ /dev/null @@ -1,970 +0,0 @@ -/* - * Copyright (c) 2018-2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "USBMSD.h" -#include "EndpointResolver.h" -#include "usb_phy_api.h" - -#define DISK_OK 0x00 -#define NO_INIT 0x01 -#define NO_DISK 0x02 -#define WRITE_PROTECT 0x04 - -#define CBW_Signature 0x43425355 -#define CSW_Signature 0x53425355 - -// SCSI Commands -#define TEST_UNIT_READY 0x00 -#define REQUEST_SENSE 0x03 -#define FORMAT_UNIT 0x04 -#define INQUIRY 0x12 -#define MODE_SELECT6 0x15 -#define MODE_SENSE6 0x1A -#define START_STOP_UNIT 0x1B -#define MEDIA_REMOVAL 0x1E -#define READ_FORMAT_CAPACITIES 0x23 -#define READ_CAPACITY 0x25 -#define READ10 0x28 -#define WRITE10 0x2A -#define VERIFY10 0x2F -#define READ12 0xA8 -#define WRITE12 0xAA -#define MODE_SELECT10 0x55 -#define MODE_SENSE10 0x5A - -// MSC class specific requests -#define MSC_REQUEST_RESET 0xFF -#define MSC_REQUEST_GET_MAX_LUN 0xFE - -#define DEFAULT_CONFIGURATION (1) - -// max packet size -#define MAX_PACKET 64 - -// CSW Status -enum Status { - CSW_PASSED, - CSW_FAILED, - CSW_ERROR, -}; - -USBMSD::USBMSD(mbed::BlockDevice *bd, bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(get_usb_phy(), vendor_id, product_id, product_release), - _initialized(false), _media_removed(false), - _addr(0), _length(0), _mem_ok(false), _block_size(0), _memory_size(0), _block_count(0), - _out_ready(false), _in_ready(false), _bulk_out_size(0), - _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), - _configure_task(&_queue), _bd(bd) -{ - _init(); - if (connect_blocking) { - connect(); - } else { - init(); - } -} - -USBMSD::USBMSD(USBPhy *phy, mbed::BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(phy, vendor_id, product_id, product_release), - _initialized(false), _media_removed(false), - _addr(0), _length(0), _mem_ok(false), _block_size(0), _memory_size(0), _block_count(0), - _out_ready(false), _in_ready(false), _bulk_out_size(0), - _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), - _configure_task(&_queue), _bd(bd) -{ - _init(); -} - - -void USBMSD::_init() -{ - _bd->init(); - - _in_task = mbed::callback(this, &USBMSD::_in); - _out_task = mbed::callback(this, &USBMSD::_out); - _reset_task = mbed::callback(this, &USBMSD::_reset); - _control_task = mbed::callback(this, &USBMSD::_control); - _configure_task = mbed::callback(this, &USBMSD::_configure); - - EndpointResolver resolver(endpoint_table()); - - resolver.endpoint_ctrl(64); - _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET); - _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET); - MBED_ASSERT(resolver.valid()); - - _stage = READ_CBW; - memset((void *)&_cbw, 0, sizeof(CBW)); - memset((void *)&_csw, 0, sizeof(CSW)); - _page = NULL; -} - -USBMSD::~USBMSD() -{ - disconnect(); - _bd->deinit(); - deinit(); -} - -bool USBMSD::connect() -{ - _mutex_init.lock(); - _mutex.lock(); - - // already initialized - if (_initialized) { - _mutex.unlock(); - _mutex_init.unlock(); - return false; - } - - //disk initialization - if (disk_status() & NO_INIT) { - if (disk_initialize()) { - _mutex.unlock(); - _mutex_init.unlock(); - return false; - } - } - - // get number of blocks - _block_count = disk_sectors(); - - // get memory size - _memory_size = disk_size(); - - if (_block_count > 0) { - _block_size = _memory_size / _block_count; - if (_block_size != 0) { - free(_page); - _page = (uint8_t *)malloc(_block_size * sizeof(uint8_t)); - if (_page == NULL) { - _mutex.unlock(); - _mutex_init.unlock(); - return false; - } - } - } else { - _mutex.unlock(); - _mutex_init.unlock(); - return false; - } - - //connect the device - USBDevice::connect(); - _initialized = true; - _media_removed = false; - _mutex.unlock(); - _mutex_init.unlock(); - return true; -} - -void USBMSD::disconnect() -{ - _mutex_init.lock(); - _mutex.lock(); - - USBDevice::disconnect(); - _initialized = false; - - _in_task.cancel(); - _out_task.cancel(); - _reset_task.cancel(); - _control_task.cancel(); - _configure_task.cancel(); - - _mutex.unlock(); - - // object mutex must be unlocked for waiting - _in_task.wait(); - _out_task.wait(); - _reset_task.wait(); - _control_task.wait(); - _configure_task.wait(); - - _mutex.lock(); - - //De-allocate MSD page size: - free(_page); - _page = NULL; - - _mutex.unlock(); - _mutex_init.unlock(); -} - -void USBMSD::process() -{ - _queue.dispatch(); -} - -void USBMSD::attach(mbed::Callback cb) -{ - lock(); - - _queue.attach(cb); - - unlock(); -} - -bool USBMSD::media_removed() -{ - return _media_removed; -} - -int USBMSD::disk_read(uint8_t *data, uint64_t block, uint8_t count) -{ - mbed::bd_addr_t addr = block * _bd->get_erase_size(); - mbed::bd_size_t size = count * _bd->get_erase_size(); - return _bd->read(data, addr, size); -} - -int USBMSD::disk_write(const uint8_t *data, uint64_t block, uint8_t count) -{ - mbed::bd_addr_t addr = block * _bd->get_erase_size(); - mbed::bd_size_t size = count * _bd->get_erase_size(); - int ret = _bd->erase(addr, size); - if (ret != 0) { - return ret; - } - - return _bd->program(data, addr, size); -} - -int USBMSD::disk_initialize() -{ - return 0; -} - -uint64_t USBMSD::disk_sectors() -{ - return _bd->size() / _bd->get_erase_size(); -} - -uint64_t USBMSD::disk_size() -{ - return _bd->size(); -} - - -int USBMSD::disk_status() -{ - return 0; -} - -void USBMSD::_isr_out() -{ - _out_task.call(); -} - -void USBMSD::_isr_in() -{ - _in_task.call(); -} - -void USBMSD::callback_state_change(DeviceState new_state) -{ - // called in ISR context - - if (new_state != Configured) { - _reset_task.cancel(); - _reset_task.call(); - } -} - -void USBMSD::callback_request(const setup_packet_t *setup) -{ - // called in ISR context - - if (setup->bmRequestType.Type == CLASS_TYPE) { - _control_task.call(setup); - } else { - complete_request(PassThrough, NULL, 0); - } -} - -void USBMSD::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - // called in ISR context - - bool success = setup->bRequest == MSC_REQUEST_GET_MAX_LUN; - complete_request_xfer_done(success); -} - -void USBMSD::callback_set_configuration(uint8_t configuration) -{ - // called in ISR context - - if (configuration != DEFAULT_CONFIGURATION) { - complete_set_configuration(false); - return; - } - _configure_task.call(); -} - -void USBMSD::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - // called in ISR context - - bool success = (interface == 0) && (alternate == 0); - complete_set_interface(success); -} - - -const uint8_t *USBMSD::string_iinterface_desc() -{ - static const uint8_t string_iinterface_descriptor[] = { - 0x08, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'M', 0, 'S', 0, 'D', 0 //bString iInterface - MSD - }; - return string_iinterface_descriptor; -} - -const uint8_t *USBMSD::string_iproduct_desc() -{ - static const uint8_t string_iproduct_descriptor[] = { - 0x12, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'M', 0, 'S', 0, 'D', 0 //bString iProduct - Mbed Audio - }; - return string_iproduct_descriptor; -} - - -const uint8_t *USBMSD::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - - uint8_t config_descriptor_temp[] = { - - // Configuration 1 - 9, // bLength - 2, // bDescriptorType - LSB(9 + 9 + 7 + 7), // wTotalLength - MSB(9 + 9 + 7 + 7), - 0x01, // bNumInterfaces - 0x01, // bConfigurationValue: 0x01 is used to select this configuration - 0x00, // iConfiguration: no string to describe this configuration - 0xC0, // bmAttributes - 100, // bMaxPower, device power consumption is 100 mA - - // Interface 0, Alternate Setting 0, MSC Class - 9, // bLength - 4, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - 0x08, // bInterfaceClass - 0x06, // bInterfaceSubClass - 0x50, // bInterfaceProtocol - 0x04, // iInterface - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - 7, // bLength - 5, // bDescriptorType - _bulk_in, // bEndpointAddress - 0x02, // bmAttributes (0x02=bulk) - LSB(MAX_PACKET), // wMaxPacketSize (LSB) - MSB(MAX_PACKET), // wMaxPacketSize (MSB) - 0, // bInterval - - // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 - 7, // bLength - 5, // bDescriptorType - _bulk_out, // bEndpointAddress - 0x02, // bmAttributes (0x02=bulk) - LSB(MAX_PACKET), // wMaxPacketSize (LSB) - MSB(MAX_PACKET), // wMaxPacketSize (MSB) - 0 // bInterval - }; - MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_configuration_descriptor)); - memcpy(_configuration_descriptor, config_descriptor_temp, sizeof(_configuration_descriptor)); - return _configuration_descriptor; -} - -void USBMSD::_out() -{ - _mutex.lock(); - - _bulk_out_size = read_finish(_bulk_out); - _out_ready = true; - _process(); - - _mutex.unlock(); -} - -void USBMSD::_in() -{ - _mutex.lock(); - - write_finish(_bulk_in); - _in_ready = true; - _process(); - - _mutex.unlock(); -} - -void USBMSD::_reset() -{ - _mutex.lock(); - - msd_reset(); - - _mutex.unlock(); -} - -void USBMSD::_control(const setup_packet_t *setup) -{ - _mutex.lock(); - - static const uint8_t maxLUN[1] = {0}; - - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - if (setup->bmRequestType.Type == CLASS_TYPE) { - switch (setup->bRequest) { - case MSC_REQUEST_RESET: - result = Success; - msd_reset(); - break; - case MSC_REQUEST_GET_MAX_LUN: - result = Send; - data = (uint8_t *)maxLUN; - size = 1; - break; - default: - break; - } - } - - complete_request(result, data, size); - - _mutex.unlock(); -} - -void USBMSD::_configure() -{ - _mutex.lock(); - - // Configure endpoints > 0 - endpoint_add(_bulk_in, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_in); - endpoint_add(_bulk_out, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_out); - MBED_ASSERT(sizeof(_bulk_out_buf) == MAX_PACKET); - MBED_ASSERT(sizeof(_bulk_in_buf) == MAX_PACKET); - - _out_ready = false; - _in_ready = true; - - //activate readings - read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); - complete_set_configuration(true); - - _mutex.unlock(); -} - -void USBMSD::_process() -{ - // Mutex must be locked by caller - - switch (_stage) { - // the device has to decode the CBW received - case READ_CBW: - if (!_out_ready) { - break; - } - CBWDecode(_bulk_out_buf, _bulk_out_size); - _read_next(); - break; - - - case PROCESS_CBW: - switch (_cbw.CB[0]) { - // the device has to receive data from the host - case WRITE10: - case WRITE12: - if (!_out_ready) { - break; - } - memoryWrite(_bulk_out_buf, _bulk_out_size); - _read_next(); - break; - case VERIFY10: - if (!_out_ready) { - break; - } - memoryVerify(_bulk_out_buf, _bulk_out_size); - _read_next(); - break; - // the device has to send data to the host - case READ10: - case READ12: - if (!_in_ready) { - break; - } - memoryRead(); - break; - } - break; - - //the device has to send a CSW - case SEND_CSW: - if (!_in_ready) { - break; - } - sendCSW(); - break; - - // an error has occurred: stall endpoint and send CSW - default: - endpoint_stall(_bulk_out); - endpoint_stall(_bulk_in); - _csw.Status = CSW_ERROR; - sendCSW(); - break; - } -} - -void USBMSD::_write_next(uint8_t *data, uint32_t size) -{ - lock(); - - MBED_ASSERT(size <= MAX_PACKET); - MBED_ASSERT(_in_ready); - uint32_t send_size = MAX_PACKET > size ? size : MAX_PACKET; - memcpy(_bulk_in_buf, data, send_size); - write_start(_bulk_in, _bulk_in_buf, send_size); - _in_ready = false; - - unlock(); -} - -void USBMSD::_read_next() -{ - lock(); - - MBED_ASSERT(_out_ready); - read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); - _out_ready = false; - - unlock(); -} - -void USBMSD::memoryWrite(uint8_t *buf, uint16_t size) -{ - // Max sized packets are required to be sent until the transfer is complete - MBED_ASSERT(_block_size % MAX_PACKET == 0); - if ((size != MAX_PACKET) && (size != 0)) { - _stage = ERROR; - endpoint_stall(_bulk_out); - return; - } - - if ((_addr + size) > _memory_size) { - size = _memory_size - _addr; - _stage = ERROR; - endpoint_stall(_bulk_out); - } - - // we fill an array in RAM of 1 block before writing it in memory - for (int i = 0; i < size; i++) { - _page[_addr % _block_size + i] = buf[i]; - } - - // if the array is filled, write it in memory - if (!((_addr + size) % _block_size)) { - if (!(disk_status() & WRITE_PROTECT)) { - disk_write(_page, _addr / _block_size, 1); - } - } - - _addr += size; - _length -= size; - _csw.DataResidue -= size; - - if ((!_length) || (_stage != PROCESS_CBW)) { - _csw.Status = (_stage == ERROR) ? CSW_FAILED : CSW_PASSED; - sendCSW(); - } -} - -void USBMSD::memoryVerify(uint8_t *buf, uint16_t size) -{ - uint32_t n; - - if ((_addr + size) > _memory_size) { - size = _memory_size - _addr; - _stage = ERROR; - endpoint_stall(_bulk_out); - } - - // beginning of a new block -> load a whole block in RAM - if (!(_addr % _block_size)) { - disk_read(_page, _addr / _block_size, 1); - } - - // info are in RAM -> no need to re-read memory - for (n = 0; n < size; n++) { - if (_page[_addr % _block_size + n] != buf[n]) { - _mem_ok = false; - break; - } - } - - _addr += size; - _length -= size; - _csw.DataResidue -= size; - - if (!_length || (_stage != PROCESS_CBW)) { - _csw.Status = (_mem_ok && (_stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED; - sendCSW(); - } -} - - -bool USBMSD::inquiryRequest(void) -{ - uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01, - 36 - 4, 0x80, 0x00, 0x00, - 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G', - 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ', - '1', '.', '0', ' ', - }; - if (!write(inquiry, sizeof(inquiry))) { - return false; - } - return true; -} - - -bool USBMSD::readFormatCapacity() -{ - uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08, - (uint8_t)((_block_count >> 24) & 0xff), - (uint8_t)((_block_count >> 16) & 0xff), - (uint8_t)((_block_count >> 8) & 0xff), - (uint8_t)((_block_count >> 0) & 0xff), - - 0x02, - (uint8_t)((_block_size >> 16) & 0xff), - (uint8_t)((_block_size >> 8) & 0xff), - (uint8_t)((_block_size >> 0) & 0xff), - }; - if (!write(capacity, sizeof(capacity))) { - return false; - } - return true; -} - - -bool USBMSD::readCapacity(void) -{ - uint8_t capacity[] = { - (uint8_t)(((_block_count - 1) >> 24) & 0xff), - (uint8_t)(((_block_count - 1) >> 16) & 0xff), - (uint8_t)(((_block_count - 1) >> 8) & 0xff), - (uint8_t)(((_block_count - 1) >> 0) & 0xff), - - (uint8_t)((_block_size >> 24) & 0xff), - (uint8_t)((_block_size >> 16) & 0xff), - (uint8_t)((_block_size >> 8) & 0xff), - (uint8_t)((_block_size >> 0) & 0xff), - }; - if (!write(capacity, sizeof(capacity))) { - return false; - } - return true; -} - -bool USBMSD::write(uint8_t *buf, uint16_t size) -{ - - if (size >= _cbw.DataLength) { - size = _cbw.DataLength; - } - _stage = SEND_CSW; - - _write_next(buf, size); - - _csw.DataResidue -= size; - _csw.Status = CSW_PASSED; - return true; -} - - -bool USBMSD::modeSense6(void) -{ - uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 }; - if (!write(sense6, sizeof(sense6))) { - return false; - } - return true; -} - -bool USBMSD::modeSense10(void) -{ - uint8_t sense10[] = { 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - if (!write(sense10, sizeof(sense10))) { - return false; - } - return true; -} - -void USBMSD::sendCSW() -{ - _csw.Signature = CSW_Signature; - _write_next((uint8_t *)&_csw, sizeof(CSW)); - _stage = READ_CBW; -} - -bool USBMSD::requestSense(void) -{ - uint8_t request_sense[] = { - 0x70, - 0x00, - 0x05, // Sense Key: illegal request - 0x00, - 0x00, - 0x00, - 0x00, - 0x0A, - 0x00, - 0x00, - 0x00, - 0x00, - 0x30, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - }; - - if (!write(request_sense, sizeof(request_sense))) { - return false; - } - - return true; -} - -void USBMSD::fail() -{ - _csw.Status = CSW_FAILED; - sendCSW(); -} - - -void USBMSD::CBWDecode(uint8_t *buf, uint16_t size) -{ - if (size == sizeof(_cbw)) { - memcpy((uint8_t *)&_cbw, buf, size); - if (_cbw.Signature == CBW_Signature) { - _csw.Tag = _cbw.Tag; - _csw.DataResidue = _cbw.DataLength; - if ((_cbw.CBLength < 1) || (_cbw.CBLength > 16)) { - fail(); - } else { - switch (_cbw.CB[0]) { - case TEST_UNIT_READY: - testUnitReady(); - break; - case REQUEST_SENSE: - requestSense(); - break; - case INQUIRY: - inquiryRequest(); - break; - case MODE_SENSE6: - modeSense6(); - break; - case READ_FORMAT_CAPACITIES: - readFormatCapacity(); - break; - case READ_CAPACITY: - readCapacity(); - break; - case READ10: - case READ12: - if (infoTransfer()) { - if ((_cbw.Flags & 0x80)) { - _stage = PROCESS_CBW; - memoryRead(); - } else { - endpoint_stall(_bulk_out); - _csw.Status = CSW_ERROR; - sendCSW(); - } - } - break; - case WRITE10: - case WRITE12: - if (infoTransfer()) { - if (!(_cbw.Flags & 0x80)) { - _stage = PROCESS_CBW; - } else { - endpoint_stall(_bulk_in); - _csw.Status = CSW_ERROR; - sendCSW(); - } - } - break; - case VERIFY10: - if (!(_cbw.CB[1] & 0x02)) { - _csw.Status = CSW_PASSED; - sendCSW(); - break; - } - if (infoTransfer()) { - if (!(_cbw.Flags & 0x80)) { - _stage = PROCESS_CBW; - _mem_ok = true; - } else { - endpoint_stall(_bulk_in); - _csw.Status = CSW_ERROR; - sendCSW(); - } - } - break; - case MEDIA_REMOVAL: - _csw.Status = CSW_PASSED; - sendCSW(); - _media_removed = true; - break; - case MODE_SENSE10: - modeSense10(); - break; - default: - fail(); - break; - } - } - } - } -} - -void USBMSD::testUnitReady(void) -{ - - if (_cbw.DataLength != 0) { - if ((_cbw.Flags & 0x80) != 0) { - endpoint_stall(_bulk_in); - } else { - endpoint_stall(_bulk_out); - } - } - - _csw.Status = CSW_PASSED; - sendCSW(); -} - - -void USBMSD::memoryRead(void) -{ - uint32_t n; - - n = (_length > MAX_PACKET) ? MAX_PACKET : _length; - - if (_addr > (_memory_size - n)) { - n = _addr < _memory_size ? _memory_size - _addr : 0; - _stage = ERROR; - } - - if (n > 0) { - // we read an entire block - if (!(_addr % _block_size)) { - disk_read(_page, _addr / _block_size, 1); - } - - // write data which are in RAM - _write_next(&_page[_addr % _block_size], MAX_PACKET); - - _addr += n; - _length -= n; - - _csw.DataResidue -= n; - } - - if (!_length || (_stage != PROCESS_CBW)) { - _csw.Status = (_stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED; - _stage = (_stage == PROCESS_CBW) ? SEND_CSW : _stage; - } -} - - -bool USBMSD::infoTransfer(void) -{ - uint32_t addr_block; - - // Logical Block Address of First Block - addr_block = (_cbw.CB[2] << 24) | (_cbw.CB[3] << 16) | (_cbw.CB[4] << 8) | (_cbw.CB[5] << 0); - - _addr = addr_block * _block_size; - - if ((addr_block >= _block_count) || (_addr >= _memory_size)) { - _csw.Status = CSW_FAILED; - sendCSW(); - return false; - } - - uint32_t length_blocks = 0; - // Number of Blocks to transfer - switch (_cbw.CB[0]) { - case READ10: - case WRITE10: - case VERIFY10: - length_blocks = (_cbw.CB[7] << 8) | (_cbw.CB[8] << 0); - break; - - case READ12: - case WRITE12: - length_blocks = (_cbw.CB[6] << 24) | (_cbw.CB[7] << 16) | (_cbw.CB[8] << 8) | (_cbw.CB[9] << 0); - break; - } - - _length = length_blocks * _block_size; - - if (!_cbw.DataLength || !length_blocks || (length_blocks > _block_count - addr_block) || (_length > _memory_size - _addr)) { // host requests no data or wrong length - _csw.Status = CSW_FAILED; - sendCSW(); - return false; - } - - if (_cbw.DataLength != _length) { - if ((_cbw.Flags & 0x80) != 0) { - endpoint_stall(_bulk_in); - } else { - endpoint_stall(_bulk_out); - } - - _csw.Status = CSW_FAILED; - sendCSW(); - return false; - } - - return true; -} - -void USBMSD::msd_reset() -{ - _stage = READ_CBW; -} diff --git a/drivers/usb/source/cdc_ecm/USBCDC_ECM.cpp b/drivers/usb/source/cdc_ecm/USBCDC_ECM.cpp new file mode 100644 index 0000000..45e97ad --- /dev/null +++ b/drivers/usb/source/cdc_ecm/USBCDC_ECM.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2018, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(MBED_CONF_RTOS_PRESENT) +#include +#include "USBCDC_ECM.h" +#include "EndpointResolver.h" +#include "usb_phy_api.h" +#include "mbed_interface.h" +#include "mbed_assert.h" + +#ifndef MAX_SEGMENT_SIZE +#define MAX_SEGMENT_SIZE (1514) +#endif + +#define FLAG_WRITE_DONE (1 << 0) +#define FLAG_DISCONNECT (1 << 1) +#define FLAG_CONNECT (1 << 2) +#define FLAG_INT_DONE (1 << 3) + +#define SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x41 +#define GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x42 +#define SET_ETHERNET_PACKET_FILTER 0x43 +#define GET_ETHERNET_STATISTIC 0x44 + +#define CS_INTERFACE 0x24 +#define NETWORK_CONNECTION 0x00 +#define CONNECTION_SPEED_CHANGE 0x2A +#define LINK_SPEED (10000000) + +USBCDC_ECM::USBCDC_ECM(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(get_usb_phy(), vendor_id, product_id, product_release), _packet_filter(0), _queue(4 * EVENTS_EVENT_SIZE) +{ + _init(); + + if (connect_blocking) { + init(); + USBDevice::connect(); + wait_ready(); + } else { + init(); + } +} + +USBCDC_ECM::USBCDC_ECM(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release), _packet_filter(0), _queue(4 * EVENTS_EVENT_SIZE) +{ + + _init(); +} + +USBCDC_ECM::~USBCDC_ECM() +{ + deinit(); +} + +void USBCDC_ECM::_init() +{ + EndpointResolver resolver(endpoint_table()); + resolver.endpoint_ctrl(MAX_PACKET_SIZE_EP0); + _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, MAX_PACKET_SIZE_INT); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET_SIZE_BULK); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET_SIZE_BULK); + + MBED_ASSERT(resolver.valid()); + + _thread.start(callback(&_queue, &events::EventQueue::dispatch_forever)); + _rx_queue.resize(MAX_SEGMENT_SIZE); +} + +void USBCDC_ECM::callback_reset() +{ + assert_locked(); + /* Called in ISR context */ +} + +void USBCDC_ECM::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + assert_locked(); + /* Called in ISR context */ + + complete_request_xfer_done(false); +} + +void USBCDC_ECM::callback_set_configuration(uint8_t configuration) +{ + assert_locked(); + /* Called in ISR context */ + + bool ret = false; + if (configuration == DEFAULT_CONFIGURATION) { + ret = true; + } + + complete_set_configuration(ret); +} + +bool USBCDC_ECM::ready() +{ + return _flags.get() & FLAG_CONNECT ? true : false; +} + +void USBCDC_ECM::wait_ready() +{ + _flags.wait_any(FLAG_CONNECT, osWaitForever, false); +} + +bool USBCDC_ECM::_notify_network_connection(uint8_t value) +{ + _write_mutex.lock(); + + bool ret = true; + uint8_t request[8] = {0}; + + request[0] = 0xA1; + request[1] = NETWORK_CONNECTION; + request[2] = value; + request[3] = 0x00; + + _flags.clear(FLAG_INT_DONE); + USBDevice::write_start(_int_in, request, sizeof(request)); + uint32_t flags = _flags.wait_any(FLAG_INT_DONE | FLAG_DISCONNECT, osWaitForever, false); + if (flags & FLAG_DISCONNECT) { + ret = false; + } + USBDevice::write_finish(_int_in); + + _write_mutex.unlock(); + return ret; +} + +bool USBCDC_ECM::_notify_connection_speed_change(uint32_t up, uint32_t down) +{ + _write_mutex.lock(); + + bool ret = true; + struct notification_t { + uint8_t request[8]; + uint32_t up; + uint32_t down; + }; + + notification_t notification; + memset(¬ification, 0, sizeof(notification)); + + notification.request[0] = 0xA1; + notification.request[1] = CONNECTION_SPEED_CHANGE; + notification.request[2] = 0x00; + notification.request[3] = 0x00; + notification.request[6] = 0x08; + notification.up = up; + notification.down = down; + + _flags.clear(FLAG_INT_DONE); + USBDevice::write_start(_int_in, (uint8_t *)¬ification, sizeof(notification)); + uint32_t flags = _flags.wait_any(FLAG_INT_DONE | FLAG_DISCONNECT, osWaitForever, false); + if (flags & FLAG_DISCONNECT) { + ret = false; + } + USBDevice::write_finish(_int_in); + + _write_mutex.unlock(); + return ret; +} + +void USBCDC_ECM::_notify_connect() +{ + _notify_network_connection(1); + _notify_connection_speed_change(LINK_SPEED, LINK_SPEED); +} + +bool USBCDC_ECM::_write_bulk(uint8_t *buffer, uint32_t size) +{ + bool ret = true; + + _flags.clear(FLAG_WRITE_DONE); + USBDevice::write_start(_bulk_in, buffer, size); + uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false); + if (flags & FLAG_DISCONNECT) { + ret = false; + } + + USBDevice::write_finish(_bulk_in); + return ret; +} + +bool USBCDC_ECM::send(uint8_t *buffer, uint32_t size) +{ + _write_mutex.lock(); + bool ret = true; + uint32_t sent = 0; + uint32_t data_size = 0; + + if (size > MAX_SEGMENT_SIZE) { + _write_mutex.unlock(); + mbed_error_printf("Buffer size is too large\n"); + return false; + } + + uint32_t max_packet = USBDevice::endpoint_max_packet_size(_bulk_in); + + while (size - sent > 0) { + data_size = (size - sent > max_packet) ? max_packet : size - sent; + if (_write_bulk(buffer + sent, data_size)) { + sent += data_size; + } else { + _write_mutex.unlock(); + return false; + } + } + + /* Send zero length packet */ + if (size % max_packet == 0) { + uint8_t buf = 0; + ret = _write_bulk(&buf, 0); + } + + _write_mutex.unlock(); + return ret; +} + +void USBCDC_ECM::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual) +{ + lock(); + + uint32_t available = _rx_queue.size(); + uint32_t copy_size = available > size ? size : available; + _rx_queue.read(buffer, copy_size); + *actual = copy_size; + + unlock(); +} + +void USBCDC_ECM::attach_rx(mbed::Callback cb) +{ + lock(); + + _callback_rx = cb; + + unlock(); +} + +void USBCDC_ECM::attach_filter(mbed::Callback cb) +{ + lock(); + + _callback_filter = cb; + + unlock(); +} + +uint16_t USBCDC_ECM::read_packet_filter() +{ + return _packet_filter; +} + +void USBCDC_ECM::callback_request(const setup_packet_t *setup) +{ + assert_locked(); + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + if (setup->bmRequestType.Type == CLASS_TYPE) { + //printf("In USBCallback_request: CLASS specific Request: %02x\n", setup->bRequest); + switch (setup->bRequest) { + case SET_ETHERNET_MULTICAST_FILTERS: + /* TODO: Support is optional, not implemented here */ + break; + case SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + /* TODO: Support is optional, not implemented here */ + break; + case GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + /* TODO: Support is optional, not implemented here */ + break; + case SET_ETHERNET_PACKET_FILTER: + if (_packet_filter != setup->wValue) { + _packet_filter = setup->wValue; + // Signal that packet filter configuration is changed + if (_callback_filter) { + _callback_filter(); + } + } + result = Success; + break; + case GET_ETHERNET_STATISTIC: + /* TODO: Support is optional, not implemented here */ + break; + default: + result = Failure; + break; + } + } + + complete_request(result, data, size); +} + +void USBCDC_ECM::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + /* Called in ISR context */ + + if (alternate) { + _packet_filter = 0; + + endpoint_add(_int_in, MAX_PACKET_SIZE_INT, USB_EP_TYPE_INT, &USBCDC_ECM::_int_callback); + endpoint_add(_bulk_in, MAX_PACKET_SIZE_BULK, USB_EP_TYPE_BULK, &USBCDC_ECM::_bulk_in_callback); + endpoint_add(_bulk_out, MAX_PACKET_SIZE_BULK, USB_EP_TYPE_BULK, &USBCDC_ECM::_bulk_out_callback); + + read_start(_bulk_out, _bulk_buf, MAX_PACKET_SIZE_BULK); + + _queue.call(static_cast(this), &USBCDC_ECM::_notify_connect); + } + + complete_set_interface(true); +} + +void USBCDC_ECM::callback_state_change(DeviceState new_state) +{ + assert_locked(); + /* Called in ISR context */ + + if (new_state == Configured) { + _flags.set(FLAG_CONNECT); + _flags.clear(FLAG_DISCONNECT); + } else { + _flags.set(FLAG_DISCONNECT); + _flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE | FLAG_INT_DONE); + } +} + +const uint8_t *USBCDC_ECM::device_desc() +{ + uint8_t ep0_size = endpoint_max_packet_size(0x00); + uint8_t device_descriptor_temp[] = { + DEVICE_DESCRIPTOR_LENGTH, // bLength + DEVICE_DESCRIPTOR, // bDescriptorType + 0x00, 0x02, // bcdUSB 2.0 + 0x02, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + ep0_size, // bMaxPacketSize0 + (uint8_t)(LSB(vendor_id)), + (uint8_t)(MSB(vendor_id)), // idVendor + (uint8_t)(LSB(product_id)), + (uint8_t)(MSB(product_id)), // idProduct + (uint8_t)(LSB(product_release)), + (uint8_t)(MSB(product_release)),// bcdDevice + STRING_OFFSET_IMANUFACTURER, // iManufacturer + STRING_OFFSET_IPRODUCT, // iProduct + STRING_OFFSET_ISERIAL, // iSerialNumber + 0x01 // bNumConfigurations + }; + MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); + memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); + return device_descriptor; +} + +const uint8_t *USBCDC_ECM::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 26, + STRING_DESCRIPTOR, + 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'E', 0, 't', 0, 'h', 0, 'e', 0, 'r', 0, 'n', 0, 'e', 0, 't', 0 + }; + return string_iproduct_descriptor; +} + +const uint8_t *USBCDC_ECM::string_iconfiguration_desc() +{ + uint8_t string_imac_addr_temp[26] = {0}; + const char unicodes[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' + }; + char mac[6]; + + mbed_mac_address(mac); + + string_imac_addr_temp[0] = 26; + string_imac_addr_temp[1] = STRING_DESCRIPTOR; + /* Convert MAC address to USB CDC string format */ + for (int i = 0; i < 6; i++) { + string_imac_addr_temp[i * 4 + 2] = unicodes[mac[i] >> 4]; + string_imac_addr_temp[i * 4 + 4] = unicodes[mac[i] & 0xF]; + } + + MBED_ASSERT(sizeof(string_imac_addr_temp) == sizeof(_string_imac_addr)); + memcpy(_string_imac_addr, string_imac_addr_temp, sizeof(string_imac_addr_temp)); + return _string_imac_addr; +} + +const uint8_t *USBCDC_ECM::string_iserial_desc() +{ + static const uint8_t string_iserial_descriptor[] = { + 26, + STRING_DESCRIPTOR, + '0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, 'A', 0, 'B', 0 + }; + return string_iserial_descriptor; +} + +#define CONFIG_DESC_SIZE (9+9+5+5+13+7+9+9+7+7) + +const uint8_t *USBCDC_ECM::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + + uint8_t config_descriptor_temp[] = { + // configuration descriptor, USB spec 9.6.3, page 264-265, Table 9-10 + 0x09, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(CONFIG_DESC_SIZE), // wTotalLength (LSB) + MSB(CONFIG_DESC_SIZE), // wTotalLength (MSB) + 2, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xC0, // bmAttributes + 50, // bMaxPower + + // Communication interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 0x09, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x06, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 15 + 0x05, // bFunctionLength + CS_INTERFACE, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x20, 0x01, // bcdCDC CDC 1.20 + + // CDC Union Functional Descriptor, CDC Spec 5.2.3.2, Table 16 + 0x05, // bFunctionLength + CS_INTERFACE, // bDescriptorType + 0x06, // bDescriptorSubType + 0, // bControlInterface + 1, // bSubordinateInterface0 + + // CDC Ethernet Networking Functional Descriptor, ECM Spec 5.4, Table 3 + 0x0D, // bFunctionLenght + CS_INTERFACE, // bDescriptorType + 0x0F, // bDescriptorSubtype + STRING_OFFSET_ICONFIGURATION, // iMacAddress + 0, 0, 0, 0, // bmEthernetStatistics + (uint8_t) LSB(MAX_SEGMENT_SIZE), // wMaxSegmentSize (LSB) + (uint8_t) MSB(MAX_SEGMENT_SIZE), // wMaxSegmentSize (MSB) + 0, 0, // wNumberMCFilters + 0, // bNumberPowerFilters + + // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=intr) + (uint8_t) LSB(MAX_PACKET_SIZE_INT), // wMaxPacketSize (LSB) + (uint8_t) MSB(MAX_PACKET_SIZE_INT), // wMaxPacketSize (MSB) + 16, // bInterval + + // Default data interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 0x09, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + + // Data interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 0x09, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 1, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + + // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + (uint8_t) LSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (LSB) + (uint8_t) MSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (MSB) + 0, // bInterval + + // Endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + (uint8_t) LSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (LSB) + (uint8_t) MSB(MAX_PACKET_SIZE_BULK), // wMaxPacketSize (MSB) + 0 // bInterval + }; + + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); + memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp)); + return _config_descriptor; +} + +void USBCDC_ECM::_int_callback() +{ + assert_locked(); + + _flags.set(FLAG_INT_DONE); +} + +void USBCDC_ECM::_bulk_in_callback() +{ + assert_locked(); + + _flags.set(FLAG_WRITE_DONE); +} + +void USBCDC_ECM::_bulk_out_callback() +{ + assert_locked(); + + uint32_t read_size = read_finish(_bulk_out); + + if (read_size <= _rx_queue.free()) { + // Copy data over + _rx_queue.write(_bulk_buf, read_size); + } + + // Signal that there is ethernet packet available + if (_callback_rx && (read_size < USBDevice::endpoint_max_packet_size(_bulk_out))) { + _callback_rx(); + } + + read_start(_bulk_out, _bulk_buf, MAX_PACKET_SIZE_BULK); +} +#endif // defined(MBED_CONF_RTOS_PRESENT) diff --git a/drivers/usb/source/msd/USBMSD.cpp b/drivers/usb/source/msd/USBMSD.cpp new file mode 100644 index 0000000..fcf416c --- /dev/null +++ b/drivers/usb/source/msd/USBMSD.cpp @@ -0,0 +1,970 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "USBMSD.h" +#include "EndpointResolver.h" +#include "usb_phy_api.h" + +#define DISK_OK 0x00 +#define NO_INIT 0x01 +#define NO_DISK 0x02 +#define WRITE_PROTECT 0x04 + +#define CBW_Signature 0x43425355 +#define CSW_Signature 0x53425355 + +// SCSI Commands +#define TEST_UNIT_READY 0x00 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define INQUIRY 0x12 +#define MODE_SELECT6 0x15 +#define MODE_SENSE6 0x1A +#define START_STOP_UNIT 0x1B +#define MEDIA_REMOVAL 0x1E +#define READ_FORMAT_CAPACITIES 0x23 +#define READ_CAPACITY 0x25 +#define READ10 0x28 +#define WRITE10 0x2A +#define VERIFY10 0x2F +#define READ12 0xA8 +#define WRITE12 0xAA +#define MODE_SELECT10 0x55 +#define MODE_SENSE10 0x5A + +// MSC class specific requests +#define MSC_REQUEST_RESET 0xFF +#define MSC_REQUEST_GET_MAX_LUN 0xFE + +#define DEFAULT_CONFIGURATION (1) + +// max packet size +#define MAX_PACKET 64 + +// CSW Status +enum Status { + CSW_PASSED, + CSW_FAILED, + CSW_ERROR, +}; + +USBMSD::USBMSD(mbed::BlockDevice *bd, bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(get_usb_phy(), vendor_id, product_id, product_release), + _initialized(false), _media_removed(false), + _addr(0), _length(0), _mem_ok(false), _block_size(0), _memory_size(0), _block_count(0), + _out_ready(false), _in_ready(false), _bulk_out_size(0), + _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), + _configure_task(&_queue), _bd(bd) +{ + _init(); + if (connect_blocking) { + connect(); + } else { + init(); + } +} + +USBMSD::USBMSD(USBPhy *phy, mbed::BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release), + _initialized(false), _media_removed(false), + _addr(0), _length(0), _mem_ok(false), _block_size(0), _memory_size(0), _block_count(0), + _out_ready(false), _in_ready(false), _bulk_out_size(0), + _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), + _configure_task(&_queue), _bd(bd) +{ + _init(); +} + + +void USBMSD::_init() +{ + _bd->init(); + + _in_task = mbed::callback(this, &USBMSD::_in); + _out_task = mbed::callback(this, &USBMSD::_out); + _reset_task = mbed::callback(this, &USBMSD::_reset); + _control_task = mbed::callback(this, &USBMSD::_control); + _configure_task = mbed::callback(this, &USBMSD::_configure); + + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET); + MBED_ASSERT(resolver.valid()); + + _stage = READ_CBW; + memset((void *)&_cbw, 0, sizeof(CBW)); + memset((void *)&_csw, 0, sizeof(CSW)); + _page = NULL; +} + +USBMSD::~USBMSD() +{ + disconnect(); + _bd->deinit(); + deinit(); +} + +bool USBMSD::connect() +{ + _mutex_init.lock(); + _mutex.lock(); + + // already initialized + if (_initialized) { + _mutex.unlock(); + _mutex_init.unlock(); + return false; + } + + //disk initialization + if (disk_status() & NO_INIT) { + if (disk_initialize()) { + _mutex.unlock(); + _mutex_init.unlock(); + return false; + } + } + + // get number of blocks + _block_count = disk_sectors(); + + // get memory size + _memory_size = disk_size(); + + if (_block_count > 0) { + _block_size = _memory_size / _block_count; + if (_block_size != 0) { + free(_page); + _page = (uint8_t *)malloc(_block_size * sizeof(uint8_t)); + if (_page == NULL) { + _mutex.unlock(); + _mutex_init.unlock(); + return false; + } + } + } else { + _mutex.unlock(); + _mutex_init.unlock(); + return false; + } + + //connect the device + USBDevice::connect(); + _initialized = true; + _media_removed = false; + _mutex.unlock(); + _mutex_init.unlock(); + return true; +} + +void USBMSD::disconnect() +{ + _mutex_init.lock(); + _mutex.lock(); + + USBDevice::disconnect(); + _initialized = false; + + _in_task.cancel(); + _out_task.cancel(); + _reset_task.cancel(); + _control_task.cancel(); + _configure_task.cancel(); + + _mutex.unlock(); + + // object mutex must be unlocked for waiting + _in_task.wait(); + _out_task.wait(); + _reset_task.wait(); + _control_task.wait(); + _configure_task.wait(); + + _mutex.lock(); + + //De-allocate MSD page size: + free(_page); + _page = NULL; + + _mutex.unlock(); + _mutex_init.unlock(); +} + +void USBMSD::process() +{ + _queue.dispatch(); +} + +void USBMSD::attach(mbed::Callback cb) +{ + lock(); + + _queue.attach(cb); + + unlock(); +} + +bool USBMSD::media_removed() +{ + return _media_removed; +} + +int USBMSD::disk_read(uint8_t *data, uint64_t block, uint8_t count) +{ + mbed::bd_addr_t addr = block * _bd->get_erase_size(); + mbed::bd_size_t size = count * _bd->get_erase_size(); + return _bd->read(data, addr, size); +} + +int USBMSD::disk_write(const uint8_t *data, uint64_t block, uint8_t count) +{ + mbed::bd_addr_t addr = block * _bd->get_erase_size(); + mbed::bd_size_t size = count * _bd->get_erase_size(); + int ret = _bd->erase(addr, size); + if (ret != 0) { + return ret; + } + + return _bd->program(data, addr, size); +} + +int USBMSD::disk_initialize() +{ + return 0; +} + +uint64_t USBMSD::disk_sectors() +{ + return _bd->size() / _bd->get_erase_size(); +} + +uint64_t USBMSD::disk_size() +{ + return _bd->size(); +} + + +int USBMSD::disk_status() +{ + return 0; +} + +void USBMSD::_isr_out() +{ + _out_task.call(); +} + +void USBMSD::_isr_in() +{ + _in_task.call(); +} + +void USBMSD::callback_state_change(DeviceState new_state) +{ + // called in ISR context + + if (new_state != Configured) { + _reset_task.cancel(); + _reset_task.call(); + } +} + +void USBMSD::callback_request(const setup_packet_t *setup) +{ + // called in ISR context + + if (setup->bmRequestType.Type == CLASS_TYPE) { + _control_task.call(setup); + } else { + complete_request(PassThrough, NULL, 0); + } +} + +void USBMSD::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + // called in ISR context + + bool success = setup->bRequest == MSC_REQUEST_GET_MAX_LUN; + complete_request_xfer_done(success); +} + +void USBMSD::callback_set_configuration(uint8_t configuration) +{ + // called in ISR context + + if (configuration != DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + return; + } + _configure_task.call(); +} + +void USBMSD::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + // called in ISR context + + bool success = (interface == 0) && (alternate == 0); + complete_set_interface(success); +} + + +const uint8_t *USBMSD::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x08, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'M', 0, 'S', 0, 'D', 0 //bString iInterface - MSD + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBMSD::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 0x12, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'M', 0, 'S', 0, 'D', 0 //bString iProduct - Mbed Audio + }; + return string_iproduct_descriptor; +} + + +const uint8_t *USBMSD::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + + uint8_t config_descriptor_temp[] = { + + // Configuration 1 + 9, // bLength + 2, // bDescriptorType + LSB(9 + 9 + 7 + 7), // wTotalLength + MSB(9 + 9 + 7 + 7), + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue: 0x01 is used to select this configuration + 0x00, // iConfiguration: no string to describe this configuration + 0xC0, // bmAttributes + 100, // bMaxPower, device power consumption is 100 mA + + // Interface 0, Alternate Setting 0, MSC Class + 9, // bLength + 4, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x08, // bInterfaceClass + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol + 0x04, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + _bulk_in, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MAX_PACKET), // wMaxPacketSize (LSB) + MSB(MAX_PACKET), // wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + _bulk_out, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MAX_PACKET), // wMaxPacketSize (LSB) + MSB(MAX_PACKET), // wMaxPacketSize (MSB) + 0 // bInterval + }; + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_configuration_descriptor)); + memcpy(_configuration_descriptor, config_descriptor_temp, sizeof(_configuration_descriptor)); + return _configuration_descriptor; +} + +void USBMSD::_out() +{ + _mutex.lock(); + + _bulk_out_size = read_finish(_bulk_out); + _out_ready = true; + _process(); + + _mutex.unlock(); +} + +void USBMSD::_in() +{ + _mutex.lock(); + + write_finish(_bulk_in); + _in_ready = true; + _process(); + + _mutex.unlock(); +} + +void USBMSD::_reset() +{ + _mutex.lock(); + + msd_reset(); + + _mutex.unlock(); +} + +void USBMSD::_control(const setup_packet_t *setup) +{ + _mutex.lock(); + + static const uint8_t maxLUN[1] = {0}; + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + if (setup->bmRequestType.Type == CLASS_TYPE) { + switch (setup->bRequest) { + case MSC_REQUEST_RESET: + result = Success; + msd_reset(); + break; + case MSC_REQUEST_GET_MAX_LUN: + result = Send; + data = (uint8_t *)maxLUN; + size = 1; + break; + default: + break; + } + } + + complete_request(result, data, size); + + _mutex.unlock(); +} + +void USBMSD::_configure() +{ + _mutex.lock(); + + // Configure endpoints > 0 + endpoint_add(_bulk_in, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_in); + endpoint_add(_bulk_out, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_out); + MBED_ASSERT(sizeof(_bulk_out_buf) == MAX_PACKET); + MBED_ASSERT(sizeof(_bulk_in_buf) == MAX_PACKET); + + _out_ready = false; + _in_ready = true; + + //activate readings + read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); + complete_set_configuration(true); + + _mutex.unlock(); +} + +void USBMSD::_process() +{ + // Mutex must be locked by caller + + switch (_stage) { + // the device has to decode the CBW received + case READ_CBW: + if (!_out_ready) { + break; + } + CBWDecode(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + + + case PROCESS_CBW: + switch (_cbw.CB[0]) { + // the device has to receive data from the host + case WRITE10: + case WRITE12: + if (!_out_ready) { + break; + } + memoryWrite(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + case VERIFY10: + if (!_out_ready) { + break; + } + memoryVerify(_bulk_out_buf, _bulk_out_size); + _read_next(); + break; + // the device has to send data to the host + case READ10: + case READ12: + if (!_in_ready) { + break; + } + memoryRead(); + break; + } + break; + + //the device has to send a CSW + case SEND_CSW: + if (!_in_ready) { + break; + } + sendCSW(); + break; + + // an error has occurred: stall endpoint and send CSW + default: + endpoint_stall(_bulk_out); + endpoint_stall(_bulk_in); + _csw.Status = CSW_ERROR; + sendCSW(); + break; + } +} + +void USBMSD::_write_next(uint8_t *data, uint32_t size) +{ + lock(); + + MBED_ASSERT(size <= MAX_PACKET); + MBED_ASSERT(_in_ready); + uint32_t send_size = MAX_PACKET > size ? size : MAX_PACKET; + memcpy(_bulk_in_buf, data, send_size); + write_start(_bulk_in, _bulk_in_buf, send_size); + _in_ready = false; + + unlock(); +} + +void USBMSD::_read_next() +{ + lock(); + + MBED_ASSERT(_out_ready); + read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf)); + _out_ready = false; + + unlock(); +} + +void USBMSD::memoryWrite(uint8_t *buf, uint16_t size) +{ + // Max sized packets are required to be sent until the transfer is complete + MBED_ASSERT(_block_size % MAX_PACKET == 0); + if ((size != MAX_PACKET) && (size != 0)) { + _stage = ERROR; + endpoint_stall(_bulk_out); + return; + } + + if ((_addr + size) > _memory_size) { + size = _memory_size - _addr; + _stage = ERROR; + endpoint_stall(_bulk_out); + } + + // we fill an array in RAM of 1 block before writing it in memory + for (int i = 0; i < size; i++) { + _page[_addr % _block_size + i] = buf[i]; + } + + // if the array is filled, write it in memory + if (!((_addr + size) % _block_size)) { + if (!(disk_status() & WRITE_PROTECT)) { + disk_write(_page, _addr / _block_size, 1); + } + } + + _addr += size; + _length -= size; + _csw.DataResidue -= size; + + if ((!_length) || (_stage != PROCESS_CBW)) { + _csw.Status = (_stage == ERROR) ? CSW_FAILED : CSW_PASSED; + sendCSW(); + } +} + +void USBMSD::memoryVerify(uint8_t *buf, uint16_t size) +{ + uint32_t n; + + if ((_addr + size) > _memory_size) { + size = _memory_size - _addr; + _stage = ERROR; + endpoint_stall(_bulk_out); + } + + // beginning of a new block -> load a whole block in RAM + if (!(_addr % _block_size)) { + disk_read(_page, _addr / _block_size, 1); + } + + // info are in RAM -> no need to re-read memory + for (n = 0; n < size; n++) { + if (_page[_addr % _block_size + n] != buf[n]) { + _mem_ok = false; + break; + } + } + + _addr += size; + _length -= size; + _csw.DataResidue -= size; + + if (!_length || (_stage != PROCESS_CBW)) { + _csw.Status = (_mem_ok && (_stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED; + sendCSW(); + } +} + + +bool USBMSD::inquiryRequest(void) +{ + uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01, + 36 - 4, 0x80, 0x00, 0x00, + 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G', + 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ', + '1', '.', '0', ' ', + }; + if (!write(inquiry, sizeof(inquiry))) { + return false; + } + return true; +} + + +bool USBMSD::readFormatCapacity() +{ + uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08, + (uint8_t)((_block_count >> 24) & 0xff), + (uint8_t)((_block_count >> 16) & 0xff), + (uint8_t)((_block_count >> 8) & 0xff), + (uint8_t)((_block_count >> 0) & 0xff), + + 0x02, + (uint8_t)((_block_size >> 16) & 0xff), + (uint8_t)((_block_size >> 8) & 0xff), + (uint8_t)((_block_size >> 0) & 0xff), + }; + if (!write(capacity, sizeof(capacity))) { + return false; + } + return true; +} + + +bool USBMSD::readCapacity(void) +{ + uint8_t capacity[] = { + (uint8_t)(((_block_count - 1) >> 24) & 0xff), + (uint8_t)(((_block_count - 1) >> 16) & 0xff), + (uint8_t)(((_block_count - 1) >> 8) & 0xff), + (uint8_t)(((_block_count - 1) >> 0) & 0xff), + + (uint8_t)((_block_size >> 24) & 0xff), + (uint8_t)((_block_size >> 16) & 0xff), + (uint8_t)((_block_size >> 8) & 0xff), + (uint8_t)((_block_size >> 0) & 0xff), + }; + if (!write(capacity, sizeof(capacity))) { + return false; + } + return true; +} + +bool USBMSD::write(uint8_t *buf, uint16_t size) +{ + + if (size >= _cbw.DataLength) { + size = _cbw.DataLength; + } + _stage = SEND_CSW; + + _write_next(buf, size); + + _csw.DataResidue -= size; + _csw.Status = CSW_PASSED; + return true; +} + + +bool USBMSD::modeSense6(void) +{ + uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 }; + if (!write(sense6, sizeof(sense6))) { + return false; + } + return true; +} + +bool USBMSD::modeSense10(void) +{ + uint8_t sense10[] = { 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if (!write(sense10, sizeof(sense10))) { + return false; + } + return true; +} + +void USBMSD::sendCSW() +{ + _csw.Signature = CSW_Signature; + _write_next((uint8_t *)&_csw, sizeof(CSW)); + _stage = READ_CBW; +} + +bool USBMSD::requestSense(void) +{ + uint8_t request_sense[] = { + 0x70, + 0x00, + 0x05, // Sense Key: illegal request + 0x00, + 0x00, + 0x00, + 0x00, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x30, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + }; + + if (!write(request_sense, sizeof(request_sense))) { + return false; + } + + return true; +} + +void USBMSD::fail() +{ + _csw.Status = CSW_FAILED; + sendCSW(); +} + + +void USBMSD::CBWDecode(uint8_t *buf, uint16_t size) +{ + if (size == sizeof(_cbw)) { + memcpy((uint8_t *)&_cbw, buf, size); + if (_cbw.Signature == CBW_Signature) { + _csw.Tag = _cbw.Tag; + _csw.DataResidue = _cbw.DataLength; + if ((_cbw.CBLength < 1) || (_cbw.CBLength > 16)) { + fail(); + } else { + switch (_cbw.CB[0]) { + case TEST_UNIT_READY: + testUnitReady(); + break; + case REQUEST_SENSE: + requestSense(); + break; + case INQUIRY: + inquiryRequest(); + break; + case MODE_SENSE6: + modeSense6(); + break; + case READ_FORMAT_CAPACITIES: + readFormatCapacity(); + break; + case READ_CAPACITY: + readCapacity(); + break; + case READ10: + case READ12: + if (infoTransfer()) { + if ((_cbw.Flags & 0x80)) { + _stage = PROCESS_CBW; + memoryRead(); + } else { + endpoint_stall(_bulk_out); + _csw.Status = CSW_ERROR; + sendCSW(); + } + } + break; + case WRITE10: + case WRITE12: + if (infoTransfer()) { + if (!(_cbw.Flags & 0x80)) { + _stage = PROCESS_CBW; + } else { + endpoint_stall(_bulk_in); + _csw.Status = CSW_ERROR; + sendCSW(); + } + } + break; + case VERIFY10: + if (!(_cbw.CB[1] & 0x02)) { + _csw.Status = CSW_PASSED; + sendCSW(); + break; + } + if (infoTransfer()) { + if (!(_cbw.Flags & 0x80)) { + _stage = PROCESS_CBW; + _mem_ok = true; + } else { + endpoint_stall(_bulk_in); + _csw.Status = CSW_ERROR; + sendCSW(); + } + } + break; + case MEDIA_REMOVAL: + _csw.Status = CSW_PASSED; + sendCSW(); + _media_removed = true; + break; + case MODE_SENSE10: + modeSense10(); + break; + default: + fail(); + break; + } + } + } + } +} + +void USBMSD::testUnitReady(void) +{ + + if (_cbw.DataLength != 0) { + if ((_cbw.Flags & 0x80) != 0) { + endpoint_stall(_bulk_in); + } else { + endpoint_stall(_bulk_out); + } + } + + _csw.Status = CSW_PASSED; + sendCSW(); +} + + +void USBMSD::memoryRead(void) +{ + uint32_t n; + + n = (_length > MAX_PACKET) ? MAX_PACKET : _length; + + if (_addr > (_memory_size - n)) { + n = _addr < _memory_size ? _memory_size - _addr : 0; + _stage = ERROR; + } + + if (n > 0) { + // we read an entire block + if (!(_addr % _block_size)) { + disk_read(_page, _addr / _block_size, 1); + } + + // write data which are in RAM + _write_next(&_page[_addr % _block_size], MAX_PACKET); + + _addr += n; + _length -= n; + + _csw.DataResidue -= n; + } + + if (!_length || (_stage != PROCESS_CBW)) { + _csw.Status = (_stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED; + _stage = (_stage == PROCESS_CBW) ? SEND_CSW : _stage; + } +} + + +bool USBMSD::infoTransfer(void) +{ + uint32_t addr_block; + + // Logical Block Address of First Block + addr_block = (_cbw.CB[2] << 24) | (_cbw.CB[3] << 16) | (_cbw.CB[4] << 8) | (_cbw.CB[5] << 0); + + _addr = addr_block * _block_size; + + if ((addr_block >= _block_count) || (_addr >= _memory_size)) { + _csw.Status = CSW_FAILED; + sendCSW(); + return false; + } + + uint32_t length_blocks = 0; + // Number of Blocks to transfer + switch (_cbw.CB[0]) { + case READ10: + case WRITE10: + case VERIFY10: + length_blocks = (_cbw.CB[7] << 8) | (_cbw.CB[8] << 0); + break; + + case READ12: + case WRITE12: + length_blocks = (_cbw.CB[6] << 24) | (_cbw.CB[7] << 16) | (_cbw.CB[8] << 8) | (_cbw.CB[9] << 0); + break; + } + + _length = length_blocks * _block_size; + + if (!_cbw.DataLength || !length_blocks || (length_blocks > _block_count - addr_block) || (_length > _memory_size - _addr)) { // host requests no data or wrong length + _csw.Status = CSW_FAILED; + sendCSW(); + return false; + } + + if (_cbw.DataLength != _length) { + if ((_cbw.Flags & 0x80) != 0) { + endpoint_stall(_bulk_in); + } else { + endpoint_stall(_bulk_out); + } + + _csw.Status = CSW_FAILED; + sendCSW(); + return false; + } + + return true; +} + +void USBMSD::msd_reset() +{ + _stage = READ_CBW; +} diff --git a/drivers/usb/tests/TESTS/CMakeLists.txt b/drivers/usb/tests/TESTS/CMakeLists.txt new file mode 100644 index 0000000..294b423 --- /dev/null +++ b/drivers/usb/tests/TESTS/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(usb_device) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/host_tests/usb_device_serial.py b/drivers/usb/tests/TESTS/host_tests/usb_device_serial.py index d462d76..cf281bc 100644 --- a/drivers/usb/tests/TESTS/host_tests/usb_device_serial.py +++ b/drivers/usb/tests/TESTS/host_tests/usb_device_serial.py @@ -27,6 +27,12 @@ import six import mbed_host_tests +# Pyserial 3.5 has a compatibility breaking capitalization change :(( +try: + from serial import portNotOpenError as PortNotOpenError +except ImportError: + from serial import PortNotOpenError as PortNotOpenError + MSG_KEY_DEVICE_READY = 'ready' MSG_KEY_SERIAL_NUMBER = 'usb_dev_sn' @@ -130,7 +136,11 @@ def port_open_wait(self): """Open the serial and wait until it's closed by the device.""" - mbed_serial = serial.Serial(dsrdtr=False) + + # Note: Need to set dsrdtr on open to true to avoid exception on Linux + # https://github.com/pyserial/pyserial/issues/67 + mbed_serial = serial.Serial(dsrdtr=True) + mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( @@ -148,12 +158,12 @@ mbed_serial.dtr = True try: mbed_serial.read() # wait until closed - except (serial.portNotOpenError, serial.SerialException): + except (PortNotOpenError, serial.SerialException): pass def port_open_close(self): """Open the serial and close it with a delay.""" - mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False) + mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=True) mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( @@ -179,7 +189,7 @@ chunk_size defines the size of data sent in each write operation. The input buffer content is discarded. """ - mbed_serial = serial.Serial(write_timeout=0.1, dsrdtr=False) + mbed_serial = serial.Serial(write_timeout=0.1, dsrdtr=True) try: mbed_serial.port = retry_fun_call( fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable @@ -214,7 +224,7 @@ def loopback(self): """Open the serial and send back every byte received.""" - mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False) + mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=True) mbed_serial.dtr = False try: mbed_serial.port = retry_fun_call( diff --git a/drivers/usb/tests/TESTS/usb_device/CMakeLists.txt b/drivers/usb/tests/TESTS/usb_device/CMakeLists.txt new file mode 100644 index 0000000..6783c60 --- /dev/null +++ b/drivers/usb/tests/TESTS/usb_device/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(basic) +add_subdirectory(hid) +add_subdirectory(msd) +add_subdirectory(serial) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/basic/CMakeLists.txt b/drivers/usb/tests/TESTS/usb_device/basic/CMakeLists.txt new file mode 100644 index 0000000..17cbc9e --- /dev/null +++ b/drivers/usb/tests/TESTS/usb_device/basic/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2022 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +if(NOT "DEVICE_USBDEVICE=1" IN_LIST MBED_TARGET_DEFINITIONS) + set(TEST_SKIPPED "USB Device is not supported for this target") +endif() + +mbed_greentea_add_test( + TEST_NAME + mbed-usb-device-basic + TEST_SOURCES + main.cpp + USBEndpointTester.cpp + USBTester.cpp + HOST_TESTS_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../host_tests" + TEST_SKIPPED + ${TEST_SKIPPED} + TEST_REQUIRED_LIBS + mbed-usb +) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/basic/USBEndpointTester.cpp b/drivers/usb/tests/TESTS/usb_device/basic/USBEndpointTester.cpp index 4b48e94..46a1dd2 100644 --- a/drivers/usb/tests/TESTS/usb_device/basic/USBEndpointTester.cpp +++ b/drivers/usb/tests/TESTS/usb_device/basic/USBEndpointTester.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ -#if USB_DEVICE_TESTS +#if DEVICE_USBDEVICE #include "stdint.h" #include "stdlib.h" diff --git a/drivers/usb/tests/TESTS/usb_device/basic/USBTester.cpp b/drivers/usb/tests/TESTS/usb_device/basic/USBTester.cpp index 743f217..14dcfdb 100644 --- a/drivers/usb/tests/TESTS/usb_device/basic/USBTester.cpp +++ b/drivers/usb/tests/TESTS/usb_device/basic/USBTester.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ -#if USB_DEVICE_TESTS +#if DEVICE_USBDEVICE #include "stdint.h" #include "USBTester.h" diff --git a/drivers/usb/tests/TESTS/usb_device/basic/main.cpp b/drivers/usb/tests/TESTS/usb_device/basic/main.cpp index 2667af2..33a8169 100644 --- a/drivers/usb/tests/TESTS/usb_device/basic/main.cpp +++ b/drivers/usb/tests/TESTS/usb_device/basic/main.cpp @@ -15,10 +15,6 @@ * limitations under the License. */ -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - #include #include #include "mbed.h" @@ -664,5 +660,4 @@ Harness::run(specification); } -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) +#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/hid/CMakeLists.txt b/drivers/usb/tests/TESTS/usb_device/hid/CMakeLists.txt new file mode 100644 index 0000000..c4bcf77 --- /dev/null +++ b/drivers/usb/tests/TESTS/usb_device/hid/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2022 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +if(NOT "DEVICE_USBDEVICE=1" IN_LIST MBED_TARGET_DEFINITIONS) + set(TEST_SKIPPED "USB Device is not supported for this target") +endif() + +mbed_greentea_add_test( + TEST_NAME + mbed-usb-device-hid + TEST_SOURCES + main.cpp + HOST_TESTS_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../host_tests" + TEST_SKIPPED + ${TEST_SKIPPED} + TEST_REQUIRED_LIBS + mbed-usb +) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/hid/main.cpp b/drivers/usb/tests/TESTS/usb_device/hid/main.cpp index 551bac2..e29139f 100644 --- a/drivers/usb/tests/TESTS/usb_device/hid/main.cpp +++ b/drivers/usb/tests/TESTS/usb_device/hid/main.cpp @@ -15,11 +15,7 @@ * limitations under the License. */ -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#if !DEVICE_USBDEVICE #error [NOT_SUPPORTED] USB Device not supported for this target #else @@ -388,5 +384,4 @@ return !Harness::run(specification); } -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) +#endif // !DEVICE_USBDEVICE diff --git a/drivers/usb/tests/TESTS/usb_device/msd/CMakeLists.txt b/drivers/usb/tests/TESTS/usb_device/msd/CMakeLists.txt new file mode 100644 index 0000000..59a434a --- /dev/null +++ b/drivers/usb/tests/TESTS/usb_device/msd/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2022 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +if(NOT "DEVICE_USBDEVICE=1" IN_LIST MBED_TARGET_DEFINITIONS) + set(TEST_SKIPPED "USB Device is not supported for this target") +endif() + +if(MBED_GREENTEA_TEST_BAREMETAL) + set(TEST_SKIPPED "USB MSD test is not compatible with mbed-baremetal") +endif() + +mbed_greentea_add_test( + TEST_NAME + mbed-usb-device-msd + TEST_SOURCES + main.cpp + HOST_TESTS_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../host_tests" + TEST_SKIPPED + ${TEST_SKIPPED} + TEST_REQUIRED_LIBS + mbed-usb-msd + mbed-storage-fat +) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/msd/main.cpp b/drivers/usb/tests/TESTS/usb_device/msd/main.cpp index 9539b2a..19f5f1d 100644 --- a/drivers/usb/tests/TESTS/usb_device/msd/main.cpp +++ b/drivers/usb/tests/TESTS/usb_device/msd/main.cpp @@ -15,10 +15,6 @@ * limitations under the License. */ -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - #if !defined(MBED_CONF_RTOS_PRESENT) #error [NOT_SUPPORTED] USB stack and test cases require RTOS to run. #else @@ -492,5 +488,4 @@ } #endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(MBED_CONF_RTOS_PRESENT) -#endif // !defined(USB_DEVICE_TESTS) +#endif // !defined(MBED_CONF_RTOS_PRESENT) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/serial/CMakeLists.txt b/drivers/usb/tests/TESTS/usb_device/serial/CMakeLists.txt new file mode 100644 index 0000000..1a0f1c5 --- /dev/null +++ b/drivers/usb/tests/TESTS/usb_device/serial/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2022 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +if(NOT "DEVICE_USBDEVICE=1" IN_LIST MBED_TARGET_DEFINITIONS) + set(TEST_SKIPPED "USB Device is not supported for this target") +endif() + +mbed_greentea_add_test( + TEST_NAME + mbed-usb-device-serial + TEST_SOURCES + main.cpp + HOST_TESTS_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../host_tests" + TEST_SKIPPED + ${TEST_SKIPPED} + TEST_REQUIRED_LIBS + mbed-usb +) \ No newline at end of file diff --git a/drivers/usb/tests/TESTS/usb_device/serial/main.cpp b/drivers/usb/tests/TESTS/usb_device/serial/main.cpp index 7f16ef5..011cbbe 100644 --- a/drivers/usb/tests/TESTS/usb_device/serial/main.cpp +++ b/drivers/usb/tests/TESTS/usb_device/serial/main.cpp @@ -15,11 +15,7 @@ * limitations under the License. */ -#if !USB_DEVICE_TESTS -#error [NOT_SUPPORTED] usb device tests not enabled -#else - -#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#if !DEVICE_USBDEVICE #error [NOT_SUPPORTED] USB Device not supported for this target #else @@ -879,5 +875,4 @@ return !Harness::run(specification); } -#endif // !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE -#endif // !defined(USB_DEVICE_TESTS) +#endif // !DEVICE_USBDEVICE \ No newline at end of file diff --git a/tools/requirements-ci-build.txt b/tools/requirements-ci-build.txt index a13393c..35efca1 100644 --- a/tools/requirements-ci-build.txt +++ b/tools/requirements-ci-build.txt @@ -2,4 +2,8 @@ # It installs flashing support through the mbed tools packages and also the mbedhtrun test runner. mbed-host-tests mbed-greentea -mbed-ls \ No newline at end of file +mbed-ls + +# For USB Device host tests +hidapi>=0.7.99 +pyusb>=1.2.0 \ No newline at end of file