diff --git a/.travis.yml b/.travis.yml index 90fd04d..6c73ef1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -284,7 +284,7 @@ # Check that example compiles without rtos - sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' ${EVENTS}/README.md > main.cpp - | - rm -r cmsis/CMSIS_5/CMSIS/RTOS2 cmsis/device/rtos drivers/source/usb connectivity/cellular connectivity/drivers/cellular \ + rm -r cmsis/CMSIS_5/CMSIS/RTOS2 cmsis/device/rtos drivers/usb connectivity/cellular connectivity/drivers/cellular \ connectivity/libraries/ppp connectivity/netsocket connectivity/nanostack connectivity/lwipstack features/frameworks/greentea-client \ features/frameworks/utest features/frameworks/unity BUILD - python tools/make.py -t GCC_ARM -m NUCLEO_F103RB --source=. --build=BUILD/NUCLEO_F103RB/GCC_ARM -j0 diff --git a/drivers/include/drivers/USBAudio.h b/drivers/include/drivers/USBAudio.h deleted file mode 100644 index 58a84cd..0000000 --- a/drivers/include/drivers/USBAudio.h +++ /dev/null @@ -1,389 +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 USBAudio_H -#define USBAudio_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" - -#include "USBDevice.h" -#include "Callback.h" -#include "OperationList.h" -#include "ByteBuffer.h" -#include "rtos/EventFlags.h" - -/** \defgroup drivers-public-api-usb USB - * \ingroup drivers-public-api - */ - -/** - * \defgroup drivers_USBAudio USBAudio class - * \ingroup drivers-public-api-usb - * @{ - */ - - -/** -* USBAudio example -* -* @code -* #include "mbed.h" -* #include "USBAudio.h" -* -* // Audio loopback example use: -* // 1. Select "Mbed Audio" as your sound device -* // 2. Play a song or audio file -* // 3. Record the output using a program such as Audacity -* -* int main() { -* -* USBAudio audio(true, 44100, 2, 44100, 2); -* -* printf("Looping audio\r\n"); -* static uint8_t buf[128]; -* while (true) { -* if (!audio.read(buf, sizeof(buf))) { -* memset(buf, 0, sizeof(buf)); -* } -* audio.write(buf, sizeof(buf)); -* } -* } -* @endcode -*/ -class USBAudio: protected USBDevice { -public: - - enum AudioEvent { - Start, - Transfer, - End - }; - - /** - * Basic constructor - * - * Construct this object optionally connecting. - * - * @note Do not use this constructor in derived classes. - * - * @param connect Call connect on initialization - * @param frequency_rx frequency in Hz (default: 48000) - * @param channel_count_rx channel number (1 or 2) (default: 1) - * @param frequency_tx frequency in Hz (default: 8000) - * @param channel_count_tx channel number (1 or 2) (default: 1) - * @param buffer_ms time audio can be buffered without overflowing in milliseconds - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBAudio(bool connect = true, uint32_t frequency_rx = 48000, uint8_t channel_count_rx = 1, uint32_t frequency_tx = 8000, uint8_t channel_count_tx = 1, uint32_t buffer_ms = 10, uint16_t vendor_id = 0x7bb8, uint16_t product_id = 0x1111, uint16_t product_release = 0x0100); - - /** - * 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 frequency_rx frequency in Hz (default: 48000) - * @param channel_count_rx channel number (1 or 2) (default: 1) - * @param frequency_tx frequency in Hz (default: 8000) - * @param channel_count_tx channel number (1 or 2) (default: 1) - * @param buffer_ms time audio can be buffered without overflowing in milliseconds - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, 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 ~USBAudio(); - - /** - * Connect USBAudio - */ - void connect(); - - /** - * Disconnect USBAudio - * - * This unblocks all calls to read_ready and write_ready. - */ - void disconnect(); - - /** - * Read audio data - * - * @param buf pointer on a buffer which will be filled with audio data - * @param size size to read - * - * @returns true if successful - */ - bool read(uint8_t *buf, uint32_t size); - - /** - * Nonblocking audio data read - * - * Read the available audio data. - * - * @param buf pointer on a buffer which will be filled with audio data - * @param size size to read - * @param actual size actually read - * @note This function is safe to call from USBAudio callbacks. - */ - void read_nb(uint8_t *buf, uint32_t size, uint32_t *actual); - - /** - * Return the number read packets dropped due to overflow - * - * @param clear Reset the overflow count back to 0 - * @return Number of packets dropped due to overflow - */ - uint32_t read_overflows(bool clear = false); - - /** - * Check if the audio read channel is open - * - * @return true if the audio read channel open, false otherwise - */ - bool read_ready(); - - /** - * Wait until the audio read channel is open - */ - void read_wait_ready(); - - /** - * Write audio data - * - * @param buf pointer to audio data to write - * @param size size to write - * - * @returns true if successful - */ - bool write(uint8_t *buf, uint32_t size); - - /** - * Nonblocking audio data write - * - * Write the available audio data. - * - * @param buf pointer to audio data to write - * @param size size to write - * @param actual actual size written - * @note This function is safe to call from USBAudio callbacks. - */ - void write_nb(uint8_t *buf, uint32_t size, uint32_t *actual); - - /** - * Return the number write packets not sent due to underflow - * - * @param clear Reset the underflow count back to 0 - * @return Number of packets that should have been - * sent but weren't due to overflow - */ - uint32_t write_underflows(bool clear = false); - - /** - * Check if the audio write channel is open - * - * @return true if the audio write channel open, false otherwise - */ - bool write_ready(); - - /** - * Wait until the audio write channel is open - */ - void write_wait_ready(); - - /** - * Get current volume between 0.0 and 1.0 - * - * @returns volume - */ - float get_volume(); - - /** Attach a Callback to update the volume - * - * @param cb Callback to attach - * - */ - void attach(mbed::Callback &cb); - - /** attach a Callback to Tx Done - * - * @param cb Callback to attach - * - */ - void attach_tx(mbed::Callback &cb); - - /** attach a Callback to Rx Done - * - * @param cb Callback to attach - * - */ - void attach_rx(mbed::Callback &cb); - -protected: - - 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); - virtual void callback_set_configuration(uint8_t configuration); - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - - virtual const uint8_t *string_iproduct_desc(); - virtual const uint8_t *string_iinterface_desc(); - virtual const uint8_t *configuration_desc(uint8_t index); - -private: - - class AsyncWrite; - class AsyncRead; - - enum ChannelState { - Powerdown, - Closed, - Opened - }; - - void _init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms); - - /* - * Call to rebuild the configuration descriptor - * - * This function should be called on creation or when any - * value that is part of the configuration descriptor - * changes. - * @note This function uses ~200 bytes of stack so - * make sure your stack is big enough for it. - */ - void _build_configuration_desc(); - - void _receive_change(ChannelState new_state); - void _receive_isr(); - void _send_change(ChannelState new_state); - void _send_isr_start(); - void _send_isr_next_sync(); - void _send_isr(); - - // has connect been called - bool _connected; - - // audio volume - float _volume; - - // mute state - uint8_t _mute; - - // Volume Current Value - uint16_t _vol_cur; - - // Volume Minimum Value - uint16_t _vol_min; - - // Volume Maximum Value - uint16_t _vol_max; - - // Volume Resolution - uint16_t _vol_res; - - // callback to update volume - mbed::Callback _update_vol; - - // callback transmit Done - mbed::Callback _tx_done; - - // callback receive Done - mbed::Callback _rx_done; - - // Number of times data was dropped due to an overflow - uint32_t _rx_overflow; - - // Number of times data was not sent due to an underflow - uint32_t _tx_underflow; - - // frequency in Hz - uint32_t _tx_freq; - uint32_t _rx_freq; - - // mono, stereo,... - uint8_t _rx_channel_count; - uint8_t _tx_channel_count; - - bool _tx_idle; - uint16_t _tx_frame_fract; - uint16_t _tx_whole_frames_per_xfer; - uint16_t _tx_fract_frames_per_xfer; - - // size of the maximum packet for the isochronous endpoint - uint16_t _tx_packet_size_max; - uint16_t _rx_packet_size_max; - - // Buffer used for the isochronous transfer - uint8_t *_tx_packet_buf; - uint8_t *_rx_packet_buf; - - // Holding buffer - ByteBuffer _tx_queue; - ByteBuffer _rx_queue; - - // State of the audio channels - ChannelState _tx_state; - ChannelState _rx_state; - - - // sample - a single PCM audio sample - // frame - a group of samples from each channel - // packet - a group of frames sent over USB in one transfer - - // Blocking primitives - OperationList _write_list; - OperationList _read_list; - rtos::EventFlags _flags; - - // endpoint numbers - usb_ep_t _episo_out; // rx endpoint - usb_ep_t _episo_in; // tx endpoint - - // channel config in the configuration descriptor: master, left, right - uint16_t _channel_config_rx; - uint16_t _channel_config_tx; - - // configuration descriptor - uint8_t _config_descriptor[183]; - - // buffer for control requests - uint8_t _control_receive[2]; - -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBCDC.h b/drivers/include/drivers/USBCDC.h deleted file mode 100644 index 777b352..0000000 --- a/drivers/include/drivers/USBCDC.h +++ /dev/null @@ -1,237 +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_H -#define USBCDC_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" - -#include "USBDevice.h" -#include "OperationList.h" - -class AsyncOp; - -/** - * \defgroup drivers_USBCDC USBCDC class - * \ingroup drivers-public-api-usb - * @{ - */ - -#define CDC_MAX_PACKET_SIZE 64 - -class USBCDC: 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(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, 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(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(); - - /** - * Check if this class is ready - * - * @return true if a terminal is connected, false otherwise - */ - bool ready(); - - /** - * Block until the terminal is connected - */ - 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); - - /** - * Send what there is room for - * - * @param buffer data to send - * @param size maximum number of bytes to send - * @param actual a pointer to where to store the number of bytes sent - * @param now true to start data transmission, false to wait - */ - void send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now = true); - - /* - * Read a buffer from a certain endpoint. Warning: blocking - * - * Blocks until at least one byte of data has been read (actual != NULL) or - * until the full size has been read (actual == NULL). - * - * @param buffer buffer where will be stored bytes - * @param size the maximum number of bytes to read - * @param actual A pointer to where to store the number of bytes actually read - * or NULL to read the full size - * @returns true if successful false if interrupted due to a state change - */ - bool receive(uint8_t *buffer, uint32_t size, uint32_t *actual = NULL); - - /** - * 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); - -protected: - /* - * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. - * - * @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 interface descriptor - * - * @returns pointer to the string interface descriptor - */ - virtual const uint8_t *string_iinterface_desc(); - - /* - * Get configuration descriptor - * - * @param index descriptor index - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - - /* - * Called by USBCallback_requestCompleted when CDC line coding is changed - * Warning: Called in ISR - * - * @param baud The baud rate - * @param bits The number of bits in a word (5-8) - * @param parity The parity - * @param stop The number of stop bits (1 or 2) - */ - virtual void line_coding_changed(int baud, int bits, int parity, int stop) {}; - - /* - * Called when there is data that can be read - */ - virtual void data_rx() {} - - /* - * Called when there is space in the TX buffer - */ - virtual void data_tx() {} - -protected: - - class AsyncWrite; - class AsyncRead; - class AsyncWait; - - virtual void callback_reset(); - 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); - virtual void callback_set_configuration(uint8_t configuration); - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - - void _init(); - - void _change_terminal_connected(bool connected); - - void _send_isr_start(); - void _send_isr(); - - void _receive_isr_start(); - void _receive_isr(); - - usb_ep_t _bulk_in; - usb_ep_t _bulk_out; - usb_ep_t _int_in; - - uint8_t _cdc_line_coding[7]; - uint8_t _cdc_new_line_coding[7]; - uint8_t _config_descriptor[75]; - - OperationList _connected_list; - bool _terminal_connected; - - OperationList _tx_list; - bool _tx_in_progress; - uint8_t _tx_buffer[CDC_MAX_PACKET_SIZE]; - uint8_t *_tx_buf; - uint32_t _tx_size; - - OperationList _rx_list; - bool _rx_in_progress; - uint8_t _rx_buffer[CDC_MAX_PACKET_SIZE]; - uint8_t *_rx_buf; - uint32_t _rx_size; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBCDC_ECM.h b/drivers/include/drivers/USBCDC_ECM.h deleted file mode 100644 index 70ddb9a..0000000 --- a/drivers/include/drivers/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 "Mutex.h" -#include "EventFlags.h" -#include "events/EventQueue.h" -#include "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/include/drivers/USBHID.h b/drivers/include/drivers/USBHID.h deleted file mode 100644 index 70b1b3f..0000000 --- a/drivers/include/drivers/USBHID.h +++ /dev/null @@ -1,277 +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 USB_HID_H -#define USB_HID_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice.h" - -#include "USBHID_Types.h" -#include "OperationList.h" - -/** - * \defgroup drivers_USBHID USBHID class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** - * USBHID example - * @code - * #include "mbed.h" - * #include "USBHID.h" - * - * USBHID hid; - * HID_REPORT recv; - * BusOut leds(LED1,LED2,LED3,LED4); - * - * int main(void) { - * while (1) { - * hid.read(&recv); - * leds = recv.data[0]; - * } - * } - * @endcode - */ - -class USBHID: 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 output_report_length Maximum length of a sent report (up to 64 bytes) - * @param input_report_length Maximum length of a received report (up to 64 bytes) - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBHID(bool connect_blocking = true, uint8_t output_report_length = 64, uint8_t input_report_length = 64, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0006, 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 output_report_length Maximum length of a sent report (up to 64 bytes) - * @param input_report_length Maximum length of a received report (up to 64 bytes) - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, 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 ~USBHID(); - - /** - * Check if this class is ready - * - * @return true if the device is in the configured state - */ - bool ready(); - - /** - * Block until this HID device is in the configured state - */ - void wait_ready(); - - /** - * Send a Report. warning: blocking - * - * @param report Report which will be sent (a report is defined by all data and the length) - * @returns true if successful - */ - bool send(const HID_REPORT *report); - - - /** - * Send a Report. warning: non blocking - * - * @param report Report which will be sent (a report is defined by all data and the length) - * @returns true if successful - */ - bool send_nb(const HID_REPORT *report); - - /** - * Read a report: blocking - * - * @param report pointer to the report to fill - * @returns true if successful - */ - bool read(HID_REPORT *report); - - /** - * Read a report: non blocking - * - * @param report pointer to the report to fill - * @returns true if successful - */ - bool read_nb(HID_REPORT *report); - -protected: - uint16_t reportLength; - uint8_t reportDescriptor[27]; - - /* - * Get the Report descriptor - * - * @returns pointer to the report descriptor - */ - virtual const uint8_t *report_desc(); - - /* - * Get the length of the report descriptor - * - * @returns the length of the report descriptor - */ - virtual uint16_t report_desc_length(); - - /* - * Get string product descriptor - * - * @returns pointer to the string product descriptor - */ - virtual const uint8_t *string_iproduct_desc(); - - /* - * Get string interface descriptor - * - * @returns pointer to the string interface descriptor - */ - virtual const uint8_t *string_iinterface_desc(); - - /* - * Get configuration descriptor - * - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - - - /* - * HID Report received by SET_REPORT request. Warning: Called in ISR context - * First byte of data will be the report ID - * - * @param report Data and length received - */ - virtual void HID_callbackSetReport(HID_REPORT *report) {}; - - /** - * 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 - */ - virtual void callback_request(const setup_packet_t *setup); - - /* - * 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); - - /* - * Called when there is a hid report that can be read - */ - virtual void report_rx() {} - - /* - * Called when there is space to send a hid report - */ - virtual void report_tx() {} - -protected: - usb_ep_t _int_in; - usb_ep_t _int_out; - -private: - void _init(uint8_t output_report_length, uint8_t input_report_length); - void _send_isr(); - void _read_isr(); - - class AsyncSend; - class AsyncRead; - class AsyncWait; - - OperationList _connect_list; - OperationList _send_list; - bool _send_idle; - OperationList _read_list; - bool _read_idle; - - uint8_t _configuration_descriptor[41]; - HID_REPORT _input_report; - HID_REPORT _output_report; - uint8_t _output_length; - uint8_t _input_length; - - -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBKeyboard.h b/drivers/include/drivers/USBKeyboard.h deleted file mode 100644 index d9f6d0b..0000000 --- a/drivers/include/drivers/USBKeyboard.h +++ /dev/null @@ -1,220 +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 USBKEYBOARD_H -#define USBKEYBOARD_H - -#include "USBHID.h" -#include "platform/Stream.h" -#include "PlatformMutex.h" - -/* Modifiers, left keys then right keys. */ -enum MODIFIER_KEY { - KEY_CTRL = 0x01, - KEY_SHIFT = 0x02, - KEY_ALT = 0x04, - KEY_LOGO = 0x08, - KEY_RCTRL = 0x10, - KEY_RSHIFT = 0x20, - KEY_RALT = 0x40, - KEY_RLOGO = 0x80, -}; - - -enum MEDIA_KEY { - KEY_NEXT_TRACK, /*!< next Track Button */ - KEY_PREVIOUS_TRACK, /*!< Previous track Button */ - KEY_STOP, /*!< Stop Button */ - KEY_PLAY_PAUSE, /*!< Play/Pause Button */ - KEY_MUTE, /*!< Mute Button */ - KEY_VOLUME_UP, /*!< Volume Up Button */ - KEY_VOLUME_DOWN, /*!< Volume Down Button */ -}; - -enum FUNCTION_KEY { - KEY_F1 = 128, /* F1 key */ - KEY_F2, /* F2 key */ - KEY_F3, /* F3 key */ - KEY_F4, /* F4 key */ - KEY_F5, /* F5 key */ - KEY_F6, /* F6 key */ - KEY_F7, /* F7 key */ - KEY_F8, /* F8 key */ - KEY_F9, /* F9 key */ - KEY_F10, /* F10 key */ - KEY_F11, /* F11 key */ - KEY_F12, /* F12 key */ - - KEY_PRINT_SCREEN, /* Print Screen key */ - KEY_SCROLL_LOCK, /* Scroll lock */ - KEY_CAPS_LOCK, /* caps lock */ - KEY_NUM_LOCK, /* num lock */ - KEY_INSERT, /* Insert key */ - KEY_HOME, /* Home key */ - KEY_PAGE_UP, /* Page Up key */ - KEY_PAGE_DOWN, /* Page Down key */ - - RIGHT_ARROW, /* Right arrow */ - LEFT_ARROW, /* Left arrow */ - DOWN_ARROW, /* Down arrow */ - UP_ARROW, /* Up arrow */ -}; - -/** - * \defgroup drivers_USBKeyboard USBKeyboard class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** - * USBKeyboard example - * @code - * - * #include "mbed.h" - * #include "USBKeyboard.h" - * - * USBKeyboard key; - * - * int main(void) - * { - * while (1) { - * key.printf("Hello World\r\n"); - * ThisThread::sleep_for(1000); - * } - * } - * - * @endcode - * - * @note Synchronization level: Thread safe - */ -class USBKeyboard: public USBHID, public mbed::Stream { -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 - */ - USBKeyboard(bool connect_blocking = true, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, 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 - */ - USBKeyboard(USBPhy *phy, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, uint16_t product_release = 0x0001); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ - virtual ~USBKeyboard(); - - /** - * To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key - * - * @code - * //To send CTRL + s (save) - * keyboard.key_code('s', KEY_CTRL); - * @endcode - * - * @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0) - * @param key character to send - * @returns true if there is no error, false otherwise - */ - bool key_code(uint8_t key, uint8_t modifier = 0); - - /** - * Send a character - * - * @param c character to be sent - * @returns true if there is no error, false otherwise - */ - virtual int _putc(int c); - - /** - * Control media keys - * - * @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN) - * @returns true if there is no error, false otherwise - */ - bool media_control(MEDIA_KEY key); - - /* - * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. - * - * @returns pointer to the report descriptor - */ - virtual const uint8_t *report_desc(); - - /* - * Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys - */ - virtual void report_rx(); - - /** - * Read status of lock keys. Useful to switch-on/off LEDs according to key pressed. Only the first three bits of the result is important: - * - First bit: NUM_LOCK - * - Second bit: CAPS_LOCK - * - Third bit: SCROLL_LOCK - * - * @returns status of lock keys - */ - uint8_t lock_status(); - -protected: - /* - * Get configuration descriptor - * - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - -private: - - //dummy otherwise it doesn't compile (we must define all methods of an abstract class) - virtual int _getc(); - - uint8_t _configuration_descriptor[41]; - uint8_t _lock_status; - PlatformMutex _mutex; - -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBMIDI.h b/drivers/include/drivers/USBMIDI.h deleted file mode 100644 index 3d7582b..0000000 --- a/drivers/include/drivers/USBMIDI.h +++ /dev/null @@ -1,193 +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 USBMIDI_H -#define USBMIDI_H - -/* These headers are included for child class. */ -#include "USBDescriptor.h" -#include "USBDevice_Types.h" - -#include "USBDevice.h" -#include "MIDIMessage.h" -#include "EventFlags.h" -#include "Mutex.h" -#include "Callback.h" - -#define DEFAULT_CONFIGURATION (1) - -/** - * \defgroup drivers_USBMIDI USBMIDI class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** -* USBMIDI example -* -* @code -* #include "mbed.h" -* #include "USBMIDI.h" -* -* USBMIDI midi; -* -* int main() { -* while (1) { -* for(int i=48; i<83; i++) { // send some messages! -* midi.write(MIDIMessage::NoteOn(i)); -* ThisThread::sleep_for(250); -* midi.write(MIDIMessage::NoteOff(i)); -* ThisThread::sleep_for(500); -* } -* } -* } -* @endcode -*/ -class USBMIDI: 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 - */ - USBMIDI(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 - */ - USBMIDI(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 ~USBMIDI(); - - /** - * 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 MIDIMessage - * - * @param m The MIDIMessage to send - * @return true if the message was sent, false otherwise - */ - bool write(MIDIMessage m); - - /** - * Check if a message can be read - * - * @return true if a packet can be read false otherwise - * @note USBMIDI::attach must be called to enable the receiver - */ - bool readable(); - - /** - * Read a message - * - * @param m The MIDIMessage to fill - * @return true if a message was read, false otherwise - */ - bool read(MIDIMessage *m); - - /** - * Attach a callback for when a MIDIEvent is received - * - * @param callback code to call when a packet is received - */ - void attach(mbed::Callback callback); - - -protected: - - 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); - - virtual void callback_set_configuration(uint8_t configuration); - - virtual void callback_set_interface(uint16_t interface, uint8_t alternate); - - virtual const uint8_t *string_iproduct_desc(); - - virtual const uint8_t *string_iinterface_desc(); - - virtual const uint8_t *configuration_desc(uint8_t index); - -private: - static const uint32_t MaxSize = 64; - - uint8_t _bulk_buf[MaxSize]; - uint32_t _bulk_buf_pos; - uint32_t _bulk_buf_size; - - bool _data_ready; - uint8_t _data[MAX_MIDI_MESSAGE_SIZE + 1]; - uint32_t _cur_data; - - rtos::EventFlags _flags; - rtos::Mutex _write_mutex; - - usb_ep_t _bulk_in; - usb_ep_t _bulk_out; - uint8_t _config_descriptor[0x65]; - - mbed::Callback _callback; - - void _init(); - void _in_callback(); - void _out_callback(); - bool _next_message(); -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBMSD.h b/drivers/include/drivers/USBMSD.h deleted file mode 100644 index b6e992a..0000000 --- a/drivers/include/drivers/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 "drivers/internal/PolledQueue.h" -#include "drivers/internal/Task.h" -#include "BlockDevice.h" -#include "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(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, 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; - - 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/include/drivers/USBMouse.h b/drivers/include/drivers/USBMouse.h deleted file mode 100644 index f74e826..0000000 --- a/drivers/include/drivers/USBMouse.h +++ /dev/null @@ -1,243 +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 USBMOUSE_H -#define USBMOUSE_H - -#include "USBHID.h" -#include "PlatformMutex.h" - -#define REPORT_ID_MOUSE 2 - -/* Common usage */ - -enum MOUSE_BUTTON { - MOUSE_LEFT = 1, - MOUSE_RIGHT = 2, - MOUSE_MIDDLE = 4, -}; - -/* X and Y limits */ -/* These values do not directly map to screen pixels */ -/* Zero may be interpreted as meaning 'no movement' */ -#define X_MIN_ABS (1) /*!< Minimum value on x-axis */ -#define Y_MIN_ABS (1) /*!< Minimum value on y-axis */ -#define X_MAX_ABS (0x7fff) /*!< Maximum value on x-axis */ -#define Y_MAX_ABS (0x7fff) /*!< Maximum value on y-axis */ - -#define X_MIN_REL (-127) /*!< The maximum value that we can move to the left on the x-axis */ -#define Y_MIN_REL (-127) /*!< The maximum value that we can move up on the y-axis */ -#define X_MAX_REL (127) /*!< The maximum value that we can move to the right on the x-axis */ -#define Y_MAX_REL (127) /*!< The maximum value that we can move down on the y-axis */ - -enum MOUSE_TYPE { - ABS_MOUSE, - REL_MOUSE, -}; - -/** - * \defgroup drivers_USBMouse USBMouse class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** - * - * USBMouse example - * @code - * #include "mbed.h" - * #include "USBMouse.h" - * - * USBMouse mouse; - * - * int main(void) - * { - * while (1) - * { - * mouse.move(20, 0); - * ThisThread::sleep_for(500); - * } - * } - * - * @endcode - * - * - * @code - * #include "mbed.h" - * #include "USBMouse.h" - * #include - * - * USBMouse mouse(true, ABS_MOUSE); - * - * int main(void) - * { - * uint16_t x_center = (X_MAX_ABS - X_MIN_ABS)/2; - * uint16_t y_center = (Y_MAX_ABS - Y_MIN_ABS)/2; - * uint16_t x_screen = 0; - * uint16_t y_screen = 0; - * - * uint32_t x_origin = x_center; - * uint32_t y_origin = y_center; - * uint32_t radius = 5000; - * uint32_t angle = 0; - * - * while (1) - * { - * x_screen = x_origin + cos((double)angle*3.14/180.0)*radius; - * y_screen = y_origin + sin((double)angle*3.14/180.0)*radius; - * - * mouse.move(x_screen, y_screen); - * angle += 3; - * ThisThread::sleep_for(10); - * } - * } - * - * @endcode - * - * @note Synchronization level: Thread safe - */ -class USBMouse: public USBHID { -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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBMouse(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, 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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) - * @param vendor_id Your vendor_id - * @param product_id Your product_id - * @param product_release Your product_release - */ - USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, uint16_t product_release = 0x0001); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ - virtual ~USBMouse(); - - /** - * Write a state of the mouse - * - * @param x x-axis position - * @param y y-axis position - * @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE) - * @param z wheel state (>0 to scroll down, <0 to scroll up) - * @returns true if there is no error, false otherwise - */ - bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z); - - /** - * Move the cursor to (x, y) - * - * @param x x-axis position - * @param y y-axis position - * @returns true if there is no error, false otherwise - */ - bool move(int16_t x, int16_t y); - - /** - * Press one or several buttons - * - * @param button button state (ex: press(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool press(uint8_t button); - - /** - * Release one or several buttons - * - * @param button button state (ex: release(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool release(uint8_t button); - - /** - * Double click (MOUSE_LEFT) - * - * @returns true if there is no error, false otherwise - */ - bool double_click(); - - /** - * Click - * - * @param button state of the buttons ( ex: clic(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool click(uint8_t button); - - /** - * Scrolling - * - * @param z value of the wheel (>0 to go down, <0 to go up) - * @returns true if there is no error, false otherwise - */ - bool scroll(int8_t z); - - /* - * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. - * - * @returns pointer to the report descriptor - */ - virtual const uint8_t *report_desc(); - -protected: - /* - * Get configuration descriptor - * - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index); - -private: - MOUSE_TYPE _mouse_type; - uint8_t _button; - uint8_t _configuration_descriptor[41]; - PlatformMutex _mutex; - - bool mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z); -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBMouseKeyboard.h b/drivers/include/drivers/USBMouseKeyboard.h deleted file mode 100644 index 54144ba..0000000 --- a/drivers/include/drivers/USBMouseKeyboard.h +++ /dev/null @@ -1,254 +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 USBMOUSEKEYBOARD_H -#define USBMOUSEKEYBOARD_H - -#define REPORT_ID_KEYBOARD 1 -#define REPORT_ID_MOUSE 2 -#define REPORT_ID_VOLUME 3 - -#include "USBMouse.h" -#include "USBKeyboard.h" -#include "platform/Stream.h" -#include "USBHID.h" -#include "PlatformMutex.h" - -/** - * \defgroup drivers_USBMouseKeyboard USBMouseKeyboard class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** - * USBMouseKeyboard example - * @code - * - * #include "mbed.h" - * #include "USBMouseKeyboard.h" - * - * USBMouseKeyboard key_mouse; - * - * int main(void) - * { - * while(1) - * { - * key_mouse.move(20, 0); - * key_mouse.printf("Hello From MBED\r\n"); - * ThisThread::sleep_for(1000); - * } - * } - * @endcode - * - * - * @code - * - * #include "mbed.h" - * #include "USBMouseKeyboard.h" - * - * USBMouseKeyboard key_mouse(ABS_MOUSE); - * - * int main(void) - * { - * while(1) - * { - * key_mouse.move(X_MAX_ABS/2, Y_MAX_ABS/2); - * key_mouse.printf("Hello from MBED\r\n"); - * ThisThread::sleep_for(1000); - * } - * } - * @endcode - * - * @note Synchronization level: Thread safe - */ -class USBMouseKeyboard: public USBHID, public mbed::Stream { -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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) - * @param vendor_id Your vendor_id (default: 0x1234) - * @param product_id Your product_id (default: 0x0001) - * @param product_release Your preoduct_release (default: 0x0001) - * - */ - USBMouseKeyboard(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, 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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) - * @param vendor_id Your vendor_id (default: 0x1234) - * @param product_id Your product_id (default: 0x0001) - * @param product_release Your preoduct_release (default: 0x0001) - * - */ - USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, uint16_t product_release = 0x0001); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ - virtual ~USBMouseKeyboard(); - - /** - * Write a state of the mouse - * - * @param x x-axis position - * @param y y-axis position - * @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE) - * @param z wheel state (>0 to scroll down, <0 to scroll up) - * @returns true if there is no error, false otherwise - */ - bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z); - - - /** - * Move the cursor to (x, y) - * - * @param x x-axis position - * @param y y-axis position - * @returns true if there is no error, false otherwise - */ - bool move(int16_t x, int16_t y); - - /** - * Press one or several buttons - * - * @param button button state (ex: press(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool press(uint8_t button); - - /** - * Release one or several buttons - * - * @param button button state (ex: release(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool release(uint8_t button); - - /** - * Double click (MOUSE_LEFT) - * - * @returns true if there is no error, false otherwise - */ - bool doubleClick(); - - /** - * Click - * - * @param button state of the buttons ( ex: clic(MOUSE_LEFT)) - * @returns true if there is no error, false otherwise - */ - bool click(uint8_t button); - - /** - * Scrolling - * - * @param z value of the wheel (>0 to go down, <0 to go up) - * @returns true if there is no error, false otherwise - */ - bool scroll(int8_t z); - - /** - * To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key - * - * @code - * //To send CTRL + s (save) - * keyboard.keyCode('s', KEY_CTRL); - * @endcode - * - * @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0) - * @param key character to send - * @returns true if there is no error, false otherwise - */ - bool key_code(uint8_t key, uint8_t modifier = 0); - - /** - * Send a character - * - * @param c character to be sent - * @returns true if there is no error, false otherwise - */ - virtual int _putc(int c); - - /** - * Control media keys - * - * @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN) - * @returns true if there is no error, false otherwise - */ - bool media_control(MEDIA_KEY key); - - /** - * Read status of lock keys. Useful to switch-on/off LEDs according to key pressed. Only the first three bits of the result is important: - * - First bit: NUM_LOCK - * - Second bit: CAPS_LOCK - * - Third bit: SCROLL_LOCK - * - * @returns status of lock keys - */ - uint8_t lock_status(); - - /* - * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. - * - * @returns pointer to the report descriptor - */ - virtual const uint8_t *report_desc(); - - /* - * Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys - * - * @returns if handle by subclass, return true - */ - virtual void report_rx(); - - -private: - MOUSE_TYPE _mouse_type; - uint8_t _button; - uint8_t _lock_status; - PlatformMutex _mutex; - - bool _mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z); - - //dummy otherwise it doesn't compile (we must define all methods of an abstract class) - virtual int _getc(); -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/USBSerial.h b/drivers/include/drivers/USBSerial.h deleted file mode 100644 index 8103739..0000000 --- a/drivers/include/drivers/USBSerial.h +++ /dev/null @@ -1,227 +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 USBSERIAL_H -#define USBSERIAL_H - -#include "USBCDC.h" -#include "platform/Stream.h" -#include "Callback.h" - -/** - * \defgroup drivers_USBSerial USBSerial class - * \ingroup drivers-public-api-usb - * @{ - */ - -/** -* USBSerial example -* -* @code -* #include "mbed.h" -* #include "USBSerial.h" -* -* //Virtual serial port over USB -* USBSerial serial; -* -* int main(void) { -* -* while(1) -* { -* serial.printf("I am a virtual serial port\n"); -* ThisThread::sleep_for(1000); -* } -* } -* @endcode -*/ -class USBSerial: public USBCDC, public mbed::Stream { -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 (default: 0x1f00) - * @param product_id Your product_id (default: 0x2012) - * @param product_release Your product_release (default: 0x0001) - * - */ - USBSerial(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, 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 (default: 0x1f00) - * @param product_id Your product_id (default: 0x2012) - * @param product_release Your product_release (default: 0x0001) - * - */ - USBSerial(USBPhy *phy, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001); - - /** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ - virtual ~USBSerial(); - - /** - * Send a character. You can use puts, printf. - * - * @param c character to be sent - * @returns true if there is no error, false otherwise - */ - virtual int _putc(int c); - - /** - * Read a character: blocking - * - * @returns character read - */ - virtual int _getc(); - - /** - * Check the number of bytes available. - * - * @returns the number of bytes available - */ - uint8_t available(); - - /** - * Check if the terminal is connected. - * - * @returns connection status - */ - bool connected(); - - /** Determine if there is a character available to read - * - * @returns - * 1 if there is a character available to read, - * 0 otherwise - */ - int readable() - { - return available() ? 1 : 0; - } - - /** Determine if there is space available to write a character - * - * @returns - * 1 if there is space to write a character, - * 0 otherwise - */ - int writeable() - { - return 1; // always return 1, for write operation is blocking - } - - /** - * Attach a member function to call when a packet is received. - * - * @param tptr pointer to the object to call the member function on - * @param mptr pointer to the member function to be called - */ - template - void attach(T *tptr, void (T::*mptr)(void)) - { - USBCDC::lock(); - - if ((mptr != NULL) && (tptr != NULL)) { - rx = mbed::Callback(tptr, mptr); - } - - USBCDC::unlock(); - } - - /** - * Attach a callback called when a packet is received - * - * @param fptr function pointer - */ - void attach(void (*fptr)(void)) - { - USBCDC::lock(); - - if (fptr != NULL) { - rx = mbed::Callback(fptr); - } - - USBCDC::unlock(); - } - - /** - * Attach a Callback called when a packet is received - * - * @param cb Callback to attach - */ - void attach(mbed::Callback &cb) - { - USBCDC::lock(); - - rx = cb; - - USBCDC::unlock(); - } - - /** - * Attach a callback to call when serial's settings are changed. - * - * @param fptr function pointer - */ - void attach(void (*fptr)(int baud, int bits, int parity, int stop)) - { - USBCDC::lock(); - - _settings_changed_callback = fptr; - - USBCDC::unlock(); - } - -protected: - virtual void data_rx(); - virtual void line_coding_changed(int baud, int bits, int parity, int stop) - { - assert_locked(); - - if (_settings_changed_callback) { - _settings_changed_callback(baud, bits, parity, stop); - } - } - -private: - mbed::Callback rx; - void (*_settings_changed_callback)(int baud, int bits, int parity, int stop); -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/AsyncOp.h b/drivers/include/drivers/internal/AsyncOp.h deleted file mode 100644 index 84789fb..0000000 --- a/drivers/include/drivers/internal/AsyncOp.h +++ /dev/null @@ -1,129 +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 MBED_ASYNC_OP_H -#define MBED_ASYNC_OP_H - -#include "Mutex.h" -#include "Semaphore.h" -#include "Callback.h" - -#include "LinkEntry.h" -#include "OperationListBase.h" - -/** \defgroup mbed-os-internal Internal API */ - -/** \addtogroup drivers-internal-api Drivers - * \ingroup mbed-os-internal - */ - -/** \defgroup drivers-internal-api-usb USB - * \ingroup drivers-internal-api - */ - -/** - * \defgroup drivers_AsyncOp AsyncOp class - * \ingroup drivers-internal-api-usb - * @{ - */ -class AsyncOp: public LinkEntry { -public: - - /** - * Construct a new AsyncOp object - */ - AsyncOp(); - - /** - * Construct a new AsyncOp object - * - * @param callback Completion callback - */ - AsyncOp(mbed::Callback &callback); - - /** - * Cleanup resources used by this AsyncOp - */ - virtual ~AsyncOp(); - - /** - * Wait for this asynchronous operation to complete - * - * If the timeout expires then this asynchronous operation is - * aborted and the timeout flag is set. - * - * @note - the host object's lock MUST NOT be held when this call is made - */ - void wait(rtos::Mutex *host_mutex, rtos::Kernel::Clock::duration_u32 rel_time = rtos::Kernel::wait_for_u32_forever); - - /** - * Abort this asynchronous operation - * - * This function has no effect if the operation is complete. Otherwise - * the aborted flag is set. - * - * @note - the host object's lock MUST be held when this call is made - */ - void abort(); - - /** - * Check if this operation timed out - * - * @return true if this operation timed out, false otherwise - */ - bool timeout(); - - /** - * Check if this operation was aborted - * - * @return true if this operation was aborted, false otherwise - */ - bool aborted(); - -protected: - - /** - * Callback indicating that something changed - * - * @return true if finished false if not - */ - virtual bool process() = 0; - - /** - * Callback indicating that this event finished - */ - virtual void complete(); - -private: - friend class OperationListBase; - - mbed::Callback _callback; - OperationListBase *_list; - rtos::Semaphore *_wait; - bool _aborted; - bool _timeout; - - void _abort(bool timeout); - - static void _host_lock(rtos::Mutex *host_mutex); - - static void _host_unlock(rtos::Mutex *host_mutex); -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/ByteBuffer.h b/drivers/include/drivers/internal/ByteBuffer.h deleted file mode 100644 index 45f2cb1..0000000 --- a/drivers/include/drivers/internal/ByteBuffer.h +++ /dev/null @@ -1,126 +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 BYTE_BUFFER_H -#define BYTE_BUFFER_H - -#include - -/** - * \defgroup drivers_ByteBuffer ByteBuffer class - * \ingroup drivers-internal-api-usb - * @{ - */ -class ByteBuffer { -public: - - /** - * Create a byte buffer of the given size - * - * @param size Number of bytes this buffer can hold - */ - ByteBuffer(uint32_t size = 0); - - /** - * Delete this byte buffer - */ - ~ByteBuffer(); - - /** - * Set the size of the buffer - * - * Buffer contents are reset. - * - * @param size New buffer size - */ - void resize(uint32_t size); - - /** - * Add a single byte to this buffer - * - * There must be enough space in the buffer or the behavior is undefined. - * - * @param data byte to add - */ - void push(uint8_t data); - - /** - * Write a block of data to this ByteBuffer - * - * There must be enough space in the ByteBuffer or the behavior is undefined. - * - * @param data Block of data to write - * @param size Size of data to write - */ - void write(uint8_t *data, uint32_t size); - - /** - * Remove a byte from this buffer - * - * @return data byte - */ - uint8_t pop(); - - /** - * Read a block of data from this ByteBuffer into a buffer pointed by 'data' - * - * There must be enough data in the ByteBuffer or the behavior is undefined. - * - * @param data Block of data to read - * @param size Size of data to read - */ - void read(uint8_t *data, uint32_t size); - - /** - * Return the number bytes in this byte buffer - * - * @return Number of used bytes - */ - uint32_t size(); - - /** - * Return the number of additional bytes this buffer can hold - * - * @return Number of free bytes - */ - uint32_t free(); - - /** - * Check if this byte buffer is full - * - * @return true if full, false otherwise - */ - bool full(); - - /** - * Check if this byte buffer is empty - * - * @return true if empty, false otherwise - */ - bool empty(); - -private: - - uint32_t _head; - uint32_t _tail; - uint32_t _size; - uint8_t *_buf; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/EndpointResolver.h b/drivers/include/drivers/internal/EndpointResolver.h deleted file mode 100644 index 7ea13f4..0000000 --- a/drivers/include/drivers/internal/EndpointResolver.h +++ /dev/null @@ -1,99 +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 ENDPOINT_RESOLVER_H -#define ENDPOINT_RESOLVER_H - -#include "USBPhy.h" - -/** - * \defgroup drivers_EndpointResolver EndpointResolver class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** - * Utility class for resolving endpoints - * - * This class is intended to make the process of - * selecting the correct endpoint from a device endpoint - * table easier. It also provides a verification function - * to check if the device has enough resources for the - * given configuration. - * - */ -class EndpointResolver { -public: - EndpointResolver(const usb_ep_table_t *table); - ~EndpointResolver(); - - /** - * Add control endpoint size - * - * @param size Space reserved for control in and control out - */ - void endpoint_ctrl(uint32_t size); - - /** - * Return a free IN endpoint of the given size - * - * @param type Desired endpoint type - * @param size Space to reserve for this endpoint - * @return Endpoint index or 0 if there are not enough resources - */ - usb_ep_t endpoint_in(usb_ep_type_t type, uint32_t size); - - /** - * Return a free OUT endpoint of the given size - * - * @param type Desired endpoint type - * @param size Space to reserve for this endpoint - * @return Endpoint index or 0 if there are not enough resources - */ - usb_ep_t endpoint_out(usb_ep_type_t type, uint32_t size); - - /** - * Get next free endpoint - */ - usb_ep_t next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size); - - /** - * Check if the endpoint configuration created so far is valid - * - * @return true if all endpoint sizes are available and fit, false otherwise - */ - bool valid(); - - /** - * Reset this class's state to when it was constructed - */ - void reset(); - -private: - - usb_ep_t index_to_endpoint(int index); - int next_index(usb_ep_type_t type, bool in_not_out); - - const usb_ep_table_t *_table; - uint32_t _cost; - uint32_t _used; - bool _valid; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/LinkEntry.h b/drivers/include/drivers/internal/LinkEntry.h deleted file mode 100644 index f3fc947..0000000 --- a/drivers/include/drivers/internal/LinkEntry.h +++ /dev/null @@ -1,42 +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 MBED_LINKED_ENTRY_H -#define MBED_LINKED_ENTRY_H - -#include - -/** - * \defgroup drivers_LinkEntry LinkEntry class - * \ingroup drivers-internal-api-usb - * @{ - */ -class LinkEntry { -public: - LinkEntry(): _next(NULL) - { - - } - -private: - friend class LinkedListBase; - LinkEntry *_next; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/LinkedList.h b/drivers/include/drivers/internal/LinkedList.h deleted file mode 100644 index 2347861..0000000 --- a/drivers/include/drivers/internal/LinkedList.h +++ /dev/null @@ -1,68 +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 MBED_LINKED_LIST_H -#define MBED_LINKED_LIST_H - -#include "LinkEntry.h" -#include "LinkedListBase.h" - -/** - * \defgroup drivers_LinkedList LinkedList class - * \ingroup drivers-internal-api-usb - * @{ - */ -template -class LinkedList: public LinkedListBase { -public: - LinkedList() {} - ~LinkedList() {} - - /** - * Return the element at the head of the list - * - * @return The element at the head of the list or NULL if the list is empty - */ - T *head() - { - return static_cast(LinkedListBase::head()); - } - - /** - * Add an element to the tail of the list - * - * @param entry New element to add - */ - void enqueue(T *entry) - { - LinkedListBase::enqueue(static_cast(entry)); - } - - /** - * Remove the element at the head of the list - * - * @return The element at the head of the list or NULL if the list is empty - */ - T *dequeue() - { - return static_cast(LinkedListBase::dequeue()); - } -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/LinkedListBase.h b/drivers/include/drivers/internal/LinkedListBase.h deleted file mode 100644 index 27d8610..0000000 --- a/drivers/include/drivers/internal/LinkedListBase.h +++ /dev/null @@ -1,68 +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 MBED_LINKED_LIST_BASE_H -#define MBED_LINKED_LIST_BASE_H - -#include "LinkEntry.h" - -/** - * \defgroup drivers_LinkedListBase LinkedListBase class - * \ingroup drivers-internal-api-usb - * @{ - */ -class LinkedListBase { -public: - LinkedListBase(); - ~LinkedListBase(); - - /** - * Return the element at the head of the list - * - * @return The element at the head of the list or NULL if the list is empty - */ - LinkEntry *head(); - - /** - * Add an element to the tail of the list - * - * @param entry New element to add - */ - void enqueue(LinkEntry *entry); - - /** - * Remove the element at the head of the list - * - * @return The element at the head of the list or NULL if the list is empty - */ - LinkEntry *dequeue(); - - /** - * Remove the specified element if it is in the list - * - * @param entry Element to remove from the list - */ - void remove(LinkEntry *entry); - -private: - LinkEntry *_head; - LinkEntry *_tail; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/MIDIMessage.h b/drivers/include/drivers/internal/MIDIMessage.h deleted file mode 100644 index 5e41765..0000000 --- a/drivers/include/drivers/internal/MIDIMessage.h +++ /dev/null @@ -1,484 +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 MIDIMESSAGE_H -#define MIDIMESSAGE_H - -#include - -#define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage - -// MIDI Message Format -// -// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ] -// -// MIDI Data Messages (Channel Specific) -// -// Message msg n m -// --------------------------------------------- -// Note Off 0x8 Key Velocity -// Note On 0x9 Key Velocity -// Polyphonic Aftertouch 0xA Key Pressure -// Control Change 0xB Controller Value -// Program Change 0xC Program - -// Channel Aftertouch 0xD Pressure - -// Pitch Wheel 0xE LSB MSB - -#define CABLE_NUM (0<<4) - - -/** - * \defgroup drivers_MIDIMessage MIDIMessage class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** A MIDI message container */ -class MIDIMessage { -public: - - MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {} - - MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) - { - for (int i = 0; i < 4; i++) { - data[i] = buf[i]; - } - } - - /** - * Copy constructor - */ - MIDIMessage(const MIDIMessage &other) - { - *this = other; - } - - /** - * Assignment operator - */ - MIDIMessage &operator=(const MIDIMessage &other) - { - length = other.length; - for (int i = 0; i < length; i++) { - data[i] = other.data[i]; - } - - return *this; - } - - ~MIDIMessage() - { - delete[] data; - } - - /** - * Set this MIDIMessage to a raw MIDI message - * - * @param buf is a true MIDI message (not USBMidi message) - * @param buf_len size of message - */ - void from_raw(uint8_t *buf, int buf_len) - { - length = buf_len + 1; - if (length > MAX_MIDI_MESSAGE_SIZE) { - // Message is too big - length = 0; - return; - } - - // first byte keeped for retro-compatibility - data[0] = CABLE_NUM | 0x08; - - for (int i = 0; i < buf_len; i++) { - data[i + 1] = buf[i]; - } - } - - // create messages - - /** Create a NoteOff message - * @param key Key ID - * @param velocity Key velocity (0-127, default = 127) - * @param channel Key channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x08; - msg.data[1] = 0x80 | (channel & 0x0F); - msg.data[2] = key & 0x7F; - msg.data[3] = velocity & 0x7F; - msg.length = 4; - return msg; - } - - /** Create a NoteOn message - * @param key Key ID - * @param velocity Key velocity (0-127, default = 127) - * @param channel Key channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x09; - msg.data[1] = 0x90 | (channel & 0x0F); - msg.data[2] = key & 0x7F; - msg.data[3] = velocity & 0x7F; - msg.length = 4; - return msg; - } - - /** Create a PolyPhonic Aftertouch message - * @param key Key ID - * @param pressure Aftertouch pressure (0-127) - * @param channel Key channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x0A; - msg.data[1] = 0xA0 | (channel & 0x0F); - msg.data[2] = key & 0x7F; - msg.data[3] = pressure & 0x7F; - msg.length = 4; - return msg; - } - - /** Create a Control Change message - * @param control Controller ID - * @param value Controller value (0-127) - * @param channel Controller channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage ControlChange(int control, int value, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x0B; - msg.data[1] = 0xB0 | (channel & 0x0F); - msg.data[2] = control & 0x7F; - msg.data[3] = value & 0x7F; - msg.length = 4; - return msg; - } - - /** Create a Program Change message - * @param program Program ID - * @param channel Channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage ProgramChange(int program, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x0C; - msg.data[1] = 0xC0 | (channel & 0x0F); - msg.data[2] = program & 0x7F; - msg.data[3] = 0x00; - msg.length = 4; - return msg; - } - - /** Create a Channel Aftertouch message - * @param pressure Pressure - * @param channel Key channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage ChannelAftertouch(int pressure, int channel = 0) - { - MIDIMessage msg; - msg.data[0] = CABLE_NUM | 0x0D; - msg.data[1] = 0xD0 | (channel & 0x0F); - msg.data[2] = pressure & 0x7F; - msg.data[3] = 0x00; - msg.length = 4; - return msg; - } - - /** Create a Pitch Wheel message - * @param pitch Pitch (-8192 - 8191, default = 0) - * @param channel Channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage PitchWheel(int pitch = 0, int channel = 0) - { - MIDIMessage msg; - int p = pitch + 8192; // 0 - 16383, 8192 is center - msg.data[0] = CABLE_NUM | 0x0E; - msg.data[1] = 0xE0 | (channel & 0x0F); - msg.data[2] = p & 0x7F; - msg.data[3] = (p >> 7) & 0x7F; - msg.length = 4; - return msg; - } - - /** Create an All Notes Off message - * @param channel Channel (0-15, default 0) - * @returns A MIDIMessage - */ - static MIDIMessage AllNotesOff(int channel = 0) - { - return ControlChange(123, 0, channel); - } - - /** Create a SysEx message - * @param data SysEx data (including 0xF0 .. 0xF7) - * @param len SysEx data length - * @returns A MIDIMessage - */ - static MIDIMessage SysEx(uint8_t *data, int len) - { - MIDIMessage msg; - msg.from_raw(data, len); - return msg; - } - - // decode messages - - /** MIDI Message Types */ - enum MIDIMessageType { - ErrorType, - NoteOffType, - NoteOnType, - PolyphonicAftertouchType, - ControlChangeType, - ProgramChangeType, - ChannelAftertouchType, - PitchWheelType, - ResetAllControllersType, - AllNotesOffType, - SysExType - }; - - /** Read the message type - * - * @returns MIDIMessageType - */ - MIDIMessageType type() - { - MIDIMessageType message_type; - uint8_t min_size; - switch ((data[1] >> 4) & 0xF) { - case 0x8: - // message, channel - // key - // velocity - min_size = 3; - message_type = NoteOffType; - break; - case 0x9: - // message, channel - // key - // velocity - min_size = 3; - message_type = NoteOnType; - break; - case 0xA: - // message, channel - // key - // pressure - min_size = 3; - message_type = PolyphonicAftertouchType; - break; - case 0xB: - // message, channel - // controller - min_size = 2; - if ((data[2] & 0x7F) < 120) { // standard controllers - message_type = ControlChangeType; - } else if ((data[2] & 0x7F) == 121) { - message_type = ResetAllControllersType; - } else if ((data[2] & 0x7F) == 123) { - message_type = AllNotesOffType; - } else { - message_type = ErrorType; // unsupported atm - } - break; - case 0xC: - // message, channel - // program - min_size = 2; - message_type = ProgramChangeType; - break; - case 0xD: - // message, channel - // pressure - min_size = 2; - message_type = ChannelAftertouchType; - break; - case 0xE: - // message, channel - // pitch lsb - // pitch msb - min_size = 3; - message_type = PitchWheelType; - break; - case 0xF: - min_size = 2; - message_type = SysExType; - break; - default: - message_type = ErrorType; - break; - } - - - if (length < min_size) { - // too small to be a valid message - message_type = ErrorType; - } - return message_type; - } - - /** - * Read the channel number - * - * @return channel number or -1 on error - */ - - int channel() - { - return (data[1] & 0x0F); - } - - /** - * Read the key ID - * - * @return key ID or -1 on error - */ - int key() - { - MIDIMessageType msg_type = type(); - if ((msg_type != NoteOffType) && - (msg_type != NoteOnType) && - (msg_type != PolyphonicAftertouchType)) { - return -1; - } - - return data[2] & 0x7F; - } - - /** - * Read the velocity - * - * @return velocity or -1 on error - */ - int velocity() - { - MIDIMessageType msg_type = type(); - if ((msg_type != NoteOffType) && - (msg_type != NoteOnType)) { - return -1; - } - - return data[3] & 0x7F; - } - - /** - * Read the controller value - * - * @return controller value or -1 on error - */ - int value() - { - MIDIMessageType msg_type = type(); - if ((msg_type != ControlChangeType) && - (msg_type != ResetAllControllersType) && - (msg_type != AllNotesOffType)) { - return -1; - } - - return data[3] & 0x7F; - } - - /** - * Read the aftertouch pressure - * - * @return aftertouch pressure or -1 on error - */ - int pressure() - { - MIDIMessageType msg_type = type(); - if ((msg_type != PolyphonicAftertouchType) && - (msg_type != ChannelAftertouchType)) { - return -1; - } - - if (type() == PolyphonicAftertouchType) { - return data[3] & 0x7F; - } else { - return data[2] & 0x7F; - } - } - - /** - * Read the controller number - * - * @return controller number or -1 on error - */ - int controller() - { - MIDIMessageType msg_type = type(); - if ((msg_type != ControlChangeType) && - (msg_type != ResetAllControllersType) && - (msg_type != AllNotesOffType)) { - return -1; - } - - return data[2] & 0x7F; - } - - /** - * Read the program number - * - * @return program number or -1 on error - */ - int program() - { - MIDIMessageType msg_type = type(); - if (msg_type != ProgramChangeType) { - return -1; - } - - return data[2] & 0x7F; - } - - /** - * Read the pitch value - * - * @return pitch value or -1 on error - */ - int pitch() - { - MIDIMessageType msg_type = type(); - if (msg_type != PitchWheelType) { - return -1; - } - - int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F); - return p - 8192; // 0 - 16383, 8192 is center - } - - uint8_t *data; - uint16_t length; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/OperationList.h b/drivers/include/drivers/internal/OperationList.h deleted file mode 100644 index 9f5e756..0000000 --- a/drivers/include/drivers/internal/OperationList.h +++ /dev/null @@ -1,94 +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 MBED_OPERATION_LIST_H -#define MBED_OPERATION_LIST_H - -#include "OperationListBase.h" -#include "AsyncOp.h" - -/** - * \defgroup drivers_OperationList OperationList class - * \ingroup drivers-internal-api-usb - * @{ - */ -template -class OperationList: public OperationListBase { -public: - - /** - * Create a new empty operation list - */ - OperationList() - { - - } - - /** - * Destroy this object and abort all operations - */ - ~OperationList() - { - - } - /** - * Add an operation to the list - * - * If the list was empty then call process on this - * operation - * - * @param op Operation to add - */ - void add(T *op) - { - OperationListBase::add(op); - } - - /** - * Remove an operation from the list - * - * If this was the head of the list then process the - * next element in the list. - * - * @param op Operation to remove - */ - void remove(T *op) - { - OperationListBase::remove(op); - } - - /** - * Dequeue the head of the list - * - * Remove the head of the operation list without completing it - * or processing the next element. The caller must call the - * AsnycOp::complete() function of the returned object. - * Additionally process() must be called on this object - * if there are still elements in the list. - * - * @return The asynchronous op at the head of the list - */ - T *dequeue_raw() - { - return static_cast(OperationListBase::dequeue_raw()); - } - -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/OperationListBase.h b/drivers/include/drivers/internal/OperationListBase.h deleted file mode 100644 index e964cc9..0000000 --- a/drivers/include/drivers/internal/OperationListBase.h +++ /dev/null @@ -1,103 +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 MBED_OPERATION_LIST_BASE_H -#define MBED_OPERATION_LIST_BASE_H - -#include "drivers/internal/LinkedListBase.h" - -class AsyncOp; - -/** - * \defgroup drivers_OperationListBase OperationListBase class - * \ingroup drivers-internal-api-usb - * @{ - */ -class OperationListBase { -public: - - /** - * Create a new empty operation list - */ - OperationListBase(); - - /** - * Destroy this object and abort all operations - */ - ~OperationListBase(); - - /** - * Check if the list is empty - * - * @return true if the list is empty false otherwise - */ - bool empty(); - - /** - * Add an operation to the list - * - * If the list was empty then call process on this - * operation - * - * @param op Operation to add - */ - void add(AsyncOp *op); - - /** - * Remove an operation from the list - * - * If this was the head of the list then process the - * next element in the list. - * - * @param op Operation to remove - */ - void remove(AsyncOp *op); - - /** - * Dequeue the head of the list - * - * Remove the head of the operation list without completing it - * or processing the next element. The caller must call the - * AsnycOp::complete() function of the returned object. - * Additionally process() must be called on this object - * if there are still elements in the list. - * - * @return The asynchronous op at the head of the list - */ - AsyncOp *dequeue_raw(); - - /** - * Abort all operations - */ - void remove_all(); - - /** - * Process the operation list - * - * This allow the operation at the head of the list to perform processing - */ - void process(); - -private: - friend class AsyncOp; - - LinkedListBase _list; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/PolledQueue.h b/drivers/include/drivers/internal/PolledQueue.h deleted file mode 100644 index 97c02a8..0000000 --- a/drivers/include/drivers/internal/PolledQueue.h +++ /dev/null @@ -1,75 +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 POLLED_QUEUE_H -#define POLLED_QUEUE_H - -#include "drivers/internal/TaskQueue.h" -#include "platform/Callback.h" -#include "LinkedList.h" -namespace events { -/** - * \defgroup drivers_PolledQueue PolledQueue class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** PolledQueue - * - * This class is an implementation of TaskQueue which is - * processed synchronously by calls to dispatch. - */ -class PolledQueue: public TaskQueue { -public: - - /** Create a PolledQueue - * - * Create an event queue. - * - * @param cb Callback called when dispatch needs to be called - */ - PolledQueue(mbed::Callback cb = nullptr); - - virtual ~PolledQueue(); - - virtual void post(TaskBase *event); - - virtual void cancel(TaskBase *event); - - /** - * Process all the events in this queue - */ - void dispatch(); - - /** - * Attach a callback indicating that this queue needs to be processed - * - * @param cb Callback called when dispatch needs to be called - */ - void attach(mbed::Callback cb); - -protected: - - mbed::Callback _cb; - LinkedList _list; - -}; - -/** @}*/ - -} -#endif diff --git a/drivers/include/drivers/internal/Task.h b/drivers/include/drivers/internal/Task.h deleted file mode 100644 index be2c932..0000000 --- a/drivers/include/drivers/internal/Task.h +++ /dev/null @@ -1,655 +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 MBED_TASK_H -#define MBED_TASK_H - -#include "events/EventQueue.h" -#include "drivers/internal/TaskBase.h" -#include "platform/mbed_assert.h" -#include "platform/Callback.h" - -namespace events { -/** \addtogroup drivers-internal-api-usb - * @{ - */ - - -template -struct AllArgs; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - - AllArgs(B0 b0 = B0()): b0(b0) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(); - s->~Self(); - } - }; - - typedef Operations ops; -}; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - B1 b1; - - AllArgs(B0 b0 = B0(), B1 b1 = B1()): b0(b0), b1(b1) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(s->b1); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(); - s->~Self(); - } - }; - - typedef Operations ops; -}; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - B1 b1; - B2 b2; - - - AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2()): b0(b0), b1(b1), b2(b2) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(s->b1, s->b2); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2); - s->~Self(); - } - }; - - typedef Operations ops; -}; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - B1 b1; - B2 b2; - B3 b3; - - - AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3()): b0(b0), b1(b1), b2(b2), b3(b3) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(s->b1, s->b2, s->b3); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3); - s->~Self(); - } - }; - - typedef Operations ops; -}; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - B1 b1; - B2 b2; - B3 b3; - B4 b4; - - - AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(s->b1, s->b2, s->b3, s->b4); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); - s->~Self(); - } - }; - - typedef Operations ops; -}; - -template -struct AllArgs { - typedef AllArgs Self; - B0 b0; - B1 b1; - B2 b2; - B3 b3; - B4 b4; - B5 b5; - - - AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4(), B5 b5 = B5()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) {} - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - s->b0(s->b1, s->b2, s->b3, s->b4, s->b5); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); - s->~Self(); - } - }; - - template - struct Operations { - static void copy(void *_dest, void *_src) - { - new (_dest) Self(*(Self *)_src); - } - - static void call(void *data) - { - Self *s = static_cast(data); - ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); - s->~Self(); - } - }; - - typedef Operations ops; -}; - - -template -class Task; - -template -class Task: public TaskBase { -public: - - Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) - : TaskBase(q), _args(cb) - { - } - - Task &operator=(mbed::Callback cb) - { - _args.b0 = cb; - return *this; - } - - void call() - { - post(); - } - -protected: - - virtual uint32_t size() - { - return sizeof(_args); - } - - virtual run_callback_t start(void *data, uint32_t max_size) - { - All::ops::copy(data, (void *)&_args); - return &All::ops::call; - } - -private: - typedef AllArgs > All; - All _args; -}; - -template -class Task: public TaskBase { -public: - - Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) - : TaskBase(q), _args(cb) - { - } - - Task &operator=(mbed::Callback cb) - { - _args.b0 = cb; - return *this; - } - - void call(A0 a0) - { - _args.b1 = a0; - post(); - } - -protected: - - virtual uint32_t size() - { - return sizeof(_args); - } - - virtual run_callback_t start(void *data, uint32_t max_size) - { - All::ops::copy(data, (void *)&_args); - return &All::ops::call; - } - -private: - typedef AllArgs, A0> All; - All _args; -}; - -/** Task - * - * Representation of a postable task - */ -template -class Task: public TaskBase { -public: - - /** - * Construct a new task - * - * @param q TaskQueue to post to - * @param cb Callback to run - */ - Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) - : TaskBase(q), _args(cb) - { - } - - /** - * Set the callback of this task - * - * @param cb Callback to run - */ - Task &operator=(mbed::Callback cb) - { - _args.b0 = cb; - return *this; - } - - /** - * Post this task for execution - * - * The number of arguments to call should match - * the type of the callback. For example Task - * expects two integers as arguments to call, while Task - * expects no arguments. - * - * @param a0 First callback parameter - * @param a1 Second callback parameter - */ - void call(A0 a0, A1 a1) - { - _args.b1 = a0; - _args.b2 = a1; - post(); - } - -protected: - - virtual uint32_t size() - { - return sizeof(_args); - } - - virtual run_callback_t start(void *data, uint32_t max_size) - { - All::ops::copy(data, (void *)&_args); - return &All::ops::call; - } - -private: - typedef AllArgs, A0, A1> All; - All _args; -}; - -/** @}*/ - -} - -#endif diff --git a/drivers/include/drivers/internal/TaskBase.h b/drivers/include/drivers/internal/TaskBase.h deleted file mode 100644 index e8a38b4..0000000 --- a/drivers/include/drivers/internal/TaskBase.h +++ /dev/null @@ -1,168 +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 TASK_BASE_H -#define TASK_BASE_H - -#include "platform/Callback.h" -#include "platform/mbed_assert.h" -#include "LinkEntry.h" - -namespace rtos { -class Semaphore; -} - -namespace events { - -class TaskQueue; - -/** - * \defgroup drivers_TaskBase TaskBase class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** TaskBase - * - * Representation of a caller allocated task - */ -class TaskBase : public LinkEntry { -public: - - typedef void (*run_callback_t)(void *data); - - /** - * Construct a new TaskBase object - * - * @param q Queue for posting to - */ - TaskBase(TaskQueue *q); - - /** - * Destroy this TaskBase - */ - virtual ~TaskBase(); - - /** - * Set the queue of this task - * - * @param q TaskQueue to post to - */ - void set(TaskQueue *q); - - /** - * Cancel the execution of this task - * - * Once cancelled the task can be posted again. Previous - * calls to post may still run. If you need to ensure the - * callback has finished the function wait() can be used. - * - * @note This function is interrupt safe - */ - void cancel(); - - /** - * Return true if this task is ready to be posted - * - * Check if this task is on a queue waiting to be run. - * - * @return true if it is safe to call post - */ - bool ready(); - - /** - * Wait for this task to finish execution - * - * When this function returns then this task is in the finished state. - */ - void wait(); - - /** - * Check if the callback has run to completion or been fully canceled - * - * When an task is finished the queue is completely done with it and the - * callback is either fully complete or has been canceled and will not run. - * - * @return true if this task has been flushed from the queue, false otherwise - */ - bool finished(); - -protected: - - /** - * Size of buffer required for TaskBase::start - * - * @return requested buffer size - */ - virtual uint32_t size() = 0; - - /** - * Copy any callback data and return a callback to run - * - * @param data Buffer to copy data to. Do not copy more than TaskBase::size() data. - * @param size Maximum size to copy - */ - virtual run_callback_t start(void *data, uint32_t size) = 0; - - /** - * Inform this task that execution has finished. - * - */ - virtual void finish(); - - /** - * Post this task to the set TaskQueue for execution - */ - void post(); - -private: - - TaskQueue *_queue; - bool _posted; - uint16_t _start_count; - rtos::Semaphore *_flush_sem; - - friend class TaskQueue; - - /* - * Must be called in a critical section - * - * This function should not be called directly. Instead - * TaskQueue::task_start should be used instead. - */ - run_callback_t _start(void *buffer, uint32_t size); - - /* - * Must be called in a critical section - * - * This function should not be called directly. Instead - * TaskQueue::task_finish should be used instead. - * - */ - void _finish(); - - /* - * Unblock wait if this task is finished - */ - void _wake_check(); -}; - -/** @}*/ - -} - -#endif diff --git a/drivers/include/drivers/internal/TaskQueue.h b/drivers/include/drivers/internal/TaskQueue.h deleted file mode 100644 index 530a920..0000000 --- a/drivers/include/drivers/internal/TaskQueue.h +++ /dev/null @@ -1,142 +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 TASK_QUEUE_H -#define TASK_QUEUE_H - -#include "drivers/internal/TaskBase.h" -#include "platform/Callback.h" -#include "mbed_critical.h" - -#define MBED_MAX_TASK_SIZE 32 - -namespace events { - -/** - * \defgroup drivers_TaskQueue TaskQueue class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** TaskQueue - * - * Flexible task queue for dispatching tasks - */ -class TaskQueue { -public: - - /** Create a TaskQueue - * - * Create an event queue. - */ - TaskQueue() - { - - } - - /** Destroy a TaskQueue - */ - virtual ~TaskQueue() - { - - } - - /** - * Add this event to the queue for execution - * - * If the event is already in the queue then it is canceled and - * added to the end of the queue. - * - * @param event Pointer to the event - */ - virtual void post(TaskBase *event) = 0; - - /** Cancel an in-flight event - * - * Cancels the given event so the event's memory can be reused. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active, the cancel - * function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. It does - * guarantee that the event queue is no longer using event data so - * the event can be freed or reused. - * - * @param event Pointer to the event - */ - virtual void cancel(TaskBase *event) = 0; - -protected: - - /** - * Get the size required to run this task - * - * Get the minimum size required for TaskQueue::task_start - * - * @param task The task to check size on - * @return required size - * @note This call must be made in a critical section - */ - static uint32_t task_size(TaskBase *task) - { - - return task->size(); - } - - /** - * Start processing this event by copying out its data - * - * Inform this event both that callback execution has started - * and that the event is free to be posted again. - * - * @param task The task to start processing - * @param dest The buffer to copy the callback arguments to - * @param size maximum size to copy - * @return Pointer to function run - * - * @note event_start must not be called on a canceled event as the - * memory may have been freed already - * @note Every call to event_start must be paired with event_finish - * @note This call must be made in a critical section - */ - static TaskBase::run_callback_t task_start(TaskBase *task, uint8_t *dest, uint32_t size) - { - - return task->_start(dest, size); - } - - /** - * Finish processing this event - * - * Inform this event that the callback has run to completion. - * - * @param task The task to finish processing - * - * @note Every call to event_finish must be preceded by a call to event_start - * @note This call must be made in a critical section - */ - static void task_finish(TaskBase *task) - { - task->_finish(); - } -}; - -/** @}*/ - -} -#endif diff --git a/drivers/include/drivers/internal/USBAudio_Types.h b/drivers/include/drivers/internal/USBAudio_Types.h deleted file mode 100644 index f13a867..0000000 --- a/drivers/include/drivers/internal/USBAudio_Types.h +++ /dev/null @@ -1,96 +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 USBAUDIO_TYPES_H -#define USBAUDIO_TYPES_H - - -#define DEFAULT_CONFIGURATION (1) - -// Audio Request Codes -#define REQUEST_SET_CUR 0x01 -#define REQUEST_GET_CUR 0x81 -#define REQUEST_SET_MIN 0x02 -#define REQUEST_GET_MIN 0x82 -#define REQUEST_SET_MAX 0x03 -#define REQUEST_GET_MAX 0x83 -#define REQUEST_SET_RES 0x04 -#define REQUEST_GET_RES 0x84 - -#define MUTE_CONTROL 0x01 -#define VOLUME_CONTROL 0x02 - - -// Audio Descriptor Sizes -#define CONTROL_INTERFACE_DESCRIPTOR_LENGTH 0x09 -#define STREAMING_INTERFACE_DESCRIPTOR_LENGTH 0x07 -#define INPUT_TERMINAL_DESCRIPTOR_LENGTH 0x0C -#define OUTPUT_TERMINAL_DESCRIPTOR_LENGTH 0x09 -#define FEATURE_UNIT_DESCRIPTOR_LENGTH 0x09 -#define STREAMING_ENDPOINT_DESCRIPTOR_LENGTH 0x07 - -// Audio Format Type Descriptor Sizes -#define FORMAT_TYPE_I_DESCRIPTOR_LENGTH 0x0b - -#define AUDIO_CLASS 0x01 -#define SUBCLASS_AUDIOCONTROL 0x01 -#define SUBCLASS_AUDIOSTREAMING 0x02 - -// Audio Descriptor Types -#define INTERFACE_DESCRIPTOR_TYPE 0x24 -#define ENDPOINT_DESCRIPTOR_TYPE 0x25 - -// Audio Control Interface Descriptor Subtypes -#define CONTROL_HEADER 0x01 -#define CONTROL_INPUT_TERMINAL 0x02 -#define CONTROL_OUTPUT_TERMINAL 0x03 -#define CONTROL_FEATURE_UNIT 0x06 - -// USB Terminal Types -#define TERMINAL_USB_STREAMING 0x0101 - -// Predefined Audio Channel Configuration Bits -// Mono -#define CHANNEL_M 0x0000 -#define CHANNEL_L 0x0001 /* Left Front */ -#define CHANNEL_R 0x0002 /* Right Front */ - -// Feature Unit Control Bits -#define CONTROL_MUTE 0x0001 -#define CONTROL_VOLUME 0x0002 - -// Input Terminal Types -#define TERMINAL_MICROPHONE 0x0201 - -// Output Terminal Types -#define TERMINAL_SPEAKER 0x0301 -#define TERMINAL_HEADPHONES 0x0302 - -// Audio Streaming Interface Descriptor Subtypes -#define STREAMING_GENERAL 0x01 -#define STREAMING_FORMAT_TYPE 0x02 - -// Audio Data Format Type I Codes -#define FORMAT_PCM 0x0001 - -// Audio Format Types -#define FORMAT_TYPE_I 0x01 - -// Audio Endpoint Descriptor Subtypes -#define ENDPOINT_GENERAL 0x01 - -#endif diff --git a/drivers/include/drivers/internal/USBDescriptor.h b/drivers/include/drivers/internal/USBDescriptor.h deleted file mode 100644 index 5add2fe..0000000 --- a/drivers/include/drivers/internal/USBDescriptor.h +++ /dev/null @@ -1,75 +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 USBDESCRIPTOR_H -#define USBDESCRIPTOR_H - -/* Standard descriptor types */ -#define DEVICE_DESCRIPTOR (1) -#define CONFIGURATION_DESCRIPTOR (2) -#define STRING_DESCRIPTOR (3) -#define INTERFACE_DESCRIPTOR (4) -#define ENDPOINT_DESCRIPTOR (5) -#define QUALIFIER_DESCRIPTOR (6) - -/* Standard descriptor lengths */ -#define DEVICE_DESCRIPTOR_LENGTH (0x12) -#define CONFIGURATION_DESCRIPTOR_LENGTH (0x09) -#define INTERFACE_DESCRIPTOR_LENGTH (0x09) -#define ENDPOINT_DESCRIPTOR_LENGTH (0x07) - - -/*string offset*/ -#define STRING_OFFSET_LANGID (0) -#define STRING_OFFSET_IMANUFACTURER (1) -#define STRING_OFFSET_IPRODUCT (2) -#define STRING_OFFSET_ISERIAL (3) -#define STRING_OFFSET_ICONFIGURATION (4) -#define STRING_OFFSET_IINTERFACE (5) - -/* USB Specification Release Number */ -#define USB_VERSION_2_0 (0x0200) - -/* Least/Most significant byte of short integer */ -#define LSB(n) ((n)&0xff) -#define MSB(n) (((n)&0xff00)>>8) - -/* bmAttributes in configuration descriptor */ -/* C_RESERVED must always be set */ -#define C_RESERVED (1U<<7) -#define C_SELF_POWERED (1U<<6) -#define C_REMOTE_WAKEUP (1U<<5) - -/* bMaxPower in configuration descriptor */ -#define C_POWER(mA) ((mA)/2) - -/* bmAttributes in endpoint descriptor */ -#define E_CONTROL (0x00) -#define E_ISOCHRONOUS (0x01) -#define E_BULK (0x02) -#define E_INTERRUPT (0x03) - -/* For isochronous endpoints only: */ -#define E_NO_SYNCHRONIZATION (0x00) -#define E_ASYNCHRONOUS (0x04) -#define E_ADAPTIVE (0x08) -#define E_SYNCHRONOUS (0x0C) -#define E_DATA (0x00) -#define E_FEEDBACK (0x10) -#define E_IMPLICIT_FEEDBACK (0x20) - -#endif diff --git a/drivers/include/drivers/internal/USBDevice.h b/drivers/include/drivers/internal/USBDevice.h deleted file mode 100644 index f03f7f8..0000000 --- a/drivers/include/drivers/internal/USBDevice.h +++ /dev/null @@ -1,623 +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 USBDEVICE_H -#define USBDEVICE_H - -#include -#include "USBDevice_Types.h" -#include "USBPhy.h" -#include "mbed_critical.h" -#include "Callback.h" - -/** - * \defgroup drivers_USBDevice USBDevice class - * \ingroup drivers-internal-api-usb - * @{ - */ - -/** - * Core USB Device driver - * - * USB driver which wraps and provides synchronization for a USBPhy object. - */ -class USBDevice: public USBPhyEvents { -public: - typedef void (USBDevice::*ep_cb_t)(); - - enum RequestResult { - Receive = 0, - Send = 1, - Success = 2, - Failure = 3, - PassThrough = 4, - }; - - enum DeviceState { - Attached, - Powered, - Default, - Address, - Configured - }; - - struct setup_packet_t { - struct { - uint8_t dataTransferDirection; - uint8_t Type; - uint8_t Recipient; - } bmRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; - }; - - /** - * Instantiate a new USBDevice with the given parameters - * - * @param phy The USBPhy providing physical USB access - * @param vendor_id The USB vendor ID - * @param product_id The USB product ID - * @param product_release The device release number - */ - USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); - - /** - * Cleanup this USBDevice - * - * This USBDevice must be uninitialized when the destructor is - * called or the behavior is undefined. - */ - virtual ~USBDevice(); - - /** - * Initialize this instance - * - * This function must be called before calling - * any other functions of this class, unless specifically - */ - void init(); - - /** - * Power down this instance - * - * Disable interrupts and stop sending events. - * This method can be used for temporary power-saving; This call can allow - * USB to be temporarily disabled to permit power saving. - * However, it is up to the user to make sure all the - * transfers have concluded (for example when USB power is lost). - * USBDevice::connect can be used to resume USB operation. - */ - void deinit(); - - /** - * Check if the device is configured - * - * @returns true if configured, false otherwise - */ - bool configured(); - - /** - * Connect a device - * This method can also be used to resume USB operation when USB power is - * detected after it was suspended via USBDevice::deinit. - */ - void connect(); - - /** - * Disconnect a device - */ - void disconnect(); - - /** - * Enable the start of frame interrupt - * - * Call USBDevice::callback_sof on every frame. - */ - void sof_enable(); - - /** - * Disable the start of frame interrupt - * - * Stop calling USBDevice::callback_sof. - */ - void sof_disable(); - - /** - * Add an endpoint - * - * @param endpoint Endpoint to enable - * @param max_packet Maximum size of a packet which can be sent or received on this endpoint - * @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO - * @param callback Method pointer to be called when a packet is transferred - * @returns true if successful, false otherwise - */ - bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, mbed::Callback callback = nullptr); - - /** - * Add an endpoint - * - * @param endpoint Endpoint to enable - * @param max_packet Maximum size of a packet which can be sent or received on this endpoint - * @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO - * @param callback Method pointer to be called when a packet is transferred - * @returns true if successful, false otherwise - */ - template - bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, void (T::*callback)()) - { - return endpoint_add(endpoint, max_packet, type, mbed::callback(this, static_cast(callback))); - } - - /** - * Remove an endpoint - * - * @param endpoint Endpoint to disable - * @note This endpoint must already have been setup with endpoint_add - */ - void endpoint_remove(usb_ep_t endpoint); - - /** - * Remove all non-zero endpoints - */ - void endpoint_remove_all(); - - /** - * Stall an endpoint - * - * If there is an ongoing transfer on this endpoint then it will - * be aborted. - * - * @param endpoint Endpoint to stall - * @note You cannot stall endpoint 0 with this function - * @note This endpoint must already have been setup with endpoint_add - */ - void endpoint_stall(usb_ep_t endpoint); - - /** - * Un-stall an endpoint - * - * Un-stalling an endpoint resets data toggle back to DATA0. - * Additionally, if there is an ongoing transfer on this endpoint - * it will be aborted. - * - * @param endpoint Endpoint to un-stall - * @note This endpoint must already have been setup with endpoint_add - */ - void endpoint_unstall(usb_ep_t endpoint); - - /** - * Get the current maximum size for this endpoint - * - * Return the currently configured maximum packet size, wMaxPacketSize, - * for this endpoint. - * @note This endpoint must already have been setup with endpoint_add - */ - uint32_t endpoint_max_packet_size(usb_ep_t endpoint); - - /** - * Abort the current transfer on this endpoint - * - * @param endpoint endpoint with transfer to abort - * @note This endpoint must already have been setup with endpoint_add - */ - void endpoint_abort(usb_ep_t endpoint); - - /** - * start a read on the given endpoint - * - * Start a read on the given endpoint. The data buffer must remain - * unchanged until the transfer either completes or is aborted. - * - * @param endpoint endpoint to read data from - * @param buffer buffer to fill with read data - * @param size The size of data to read. This must be greater than or equal - * to the max packet size for this endpoint - * @return true if the read was completed, otherwise false - * @note This endpoint must already have been setup with endpoint_add - */ - bool read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size); - - /** - * Get the status of a read - * - * @param endpoint endpoint to get the status of - * @return number of bytes read by this endpoint - */ - uint32_t read_finish(usb_ep_t endpoint); - - /** - * Write a data to the given endpoint - * - * Write data to an endpoint. The data sent must remain unchanged until - * the transfer either completes or is aborted. - * - * @param endpoint endpoint to write data to - * @param buffer data to write - * @param size the size of data to send. This must be less than or equal to the - * max packet size of this endpoint - * @note This endpoint must already have been setup with endpoint_add - */ - bool write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size); - - /** - * Get the status of a write - * - * @param endpoint endpoint to get the status of - * @return number of bytes sent by this endpoint - */ - uint32_t write_finish(usb_ep_t endpoint); - - /* - * Get device descriptor. - * - * @returns pointer to the device descriptor - */ - virtual const uint8_t *device_desc(); - - /* - * Get configuration descriptor - * - * @param index descriptor index - * @returns pointer to the configuration descriptor - */ - virtual const uint8_t *configuration_desc(uint8_t index) = 0; - - /* - * Get string lang id descriptor - * - * @return pointer to the string lang id descriptor - */ - virtual const uint8_t *string_langid_desc(); - - /* - * Get string manufacturer descriptor - * - * @returns pointer to the string manufacturer descriptor - */ - virtual const uint8_t *string_imanufacturer_desc(); - - /* - * Get string product descriptor - * - * @returns pointer to the string product descriptor - */ - virtual const uint8_t *string_iproduct_desc(); - - /* - * Get string serial descriptor - * - * @returns pointer to the string serial descriptor - */ - virtual const uint8_t *string_iserial_desc(); - - /* - * Get string configuration descriptor - * - * @returns pointer to the string configuration descriptor - */ - virtual const uint8_t *string_iconfiguration_desc(); - - /* - * Get string interface descriptor - * - * @returns pointer to the string interface descriptor - */ - virtual const uint8_t *string_iinterface_desc(); - - /* - * Get the length of the report descriptor - * - * @returns length of the report descriptor - */ - virtual uint16_t report_desc_dength() - { - return 0; - }; - -protected: - - /** - * Called by USBDevice layer on power state change. - * - * @param powered true if device is powered, false otherwise - * - * Warning: Called in ISR context - */ - virtual void callback_power(bool powered) - { - - } - - /** - * Called by USBDevice layer on each new USB frame. - * - * Callbacks are enabled and disabled by calling sof_enable - * and sof_disable. - * - * @param frame_number The current frame number - * - * Warning: Called in ISR context - */ - virtual void callback_sof(int frame_number) - { - - } - - /** - * 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() - { - - } - - /** - * 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) = 0; - - /** - * Called by USBDevice on Endpoint0 request. - * - * This is used to handle extensions to standard requests - * and class specific requests. The function complete_request - * must be always be called in response to this callback. - * - * Warning: Called in ISR context - */ - virtual void callback_request(const setup_packet_t *setup) = 0; - - /** - * Called to complete the setup stage of a callback request - * - * Possible options that can be passed as a result are: - * - Receive - Start the data OUT phase of this control transfer - * - Send - Start the data IN phase of this control transfer - * - Success - Operation was a success so start the status phase - * - Failure - Operation failed or is unsupported so send a stall - * - PassThrough - Pass on the request for standard processing - * - * @param result The result of the setup phase. - * @param data Buffer to send or receive if the result is Send or Receive - * @param size Size to transfer if the result is Send or Receive - */ - void complete_request(RequestResult result, uint8_t *data = NULL, uint32_t size = 0); - - /** - * Called by USBDevice on data stage completion - * - * The function complete_request_xfer_done must be always be called - * in response to this callback. - * - * @param setup Setup packet of the current request - * @param aborted false if the operation was aborted, true otherwise - * - * Warning: Called in ISR context - */ - virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted) = 0; - - /** - * Called to complete the data stage of a callback request - * - * @param success true if the operation was successful, false otherwise - */ - void complete_request_xfer_done(bool success); - - /* - * Called by USBDevice layer in response to set_configuration. - * - * Upon reception of this command endpoints of the previous configuration - * 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_configuration(uint8_t configuration) = 0; - - /** - * Called to complete a set configuration command - * - * @param success true if the configuration was set, false otherwise - */ - void complete_set_configuration(bool success); - - /* - * 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) = 0; - - /** - * Called to complete a set interface command - * - * @param success true if the interface was set, false otherwise - */ - void complete_set_interface(bool success); - - /** - * Find a descriptor type inside the configuration descriptor - * - * @param descriptor_type Type of descriptor to find - * @param index Configuration descriptor index ( 0 if only one configuration present ) - * @return A descriptor of the given type or NULL if none were found - */ - uint8_t *find_descriptor(uint8_t descriptor_type, uint8_t index = 0); - - /** - * Get the endpoint table of this device - * - * @return Endpoint table of the USBPhy attached to this USBDevice - */ - const usb_ep_table_t *endpoint_table(); - - /** - * Callback called to indicate the USB processing needs to be done - */ - virtual void start_process(); - - /** - * Acquire exclusive access to this instance USBDevice - */ - virtual void lock(); - - /** - * Release exclusive access to this instance USBDevice - */ - virtual void unlock(); - - /** - * Assert that the current thread of execution holds the lock - * - */ - virtual void assert_locked(); - - uint16_t vendor_id; - uint16_t product_id; - uint16_t product_release; - uint8_t device_descriptor[18]; - -private: - // USBPhyEvents - virtual void power(bool powered); - virtual void suspend(bool suspended); - virtual void sof(int frame_number); - virtual void reset(); - virtual void ep0_setup(); - virtual void ep0_out(); - virtual void ep0_in(); - virtual void out(usb_ep_t endpoint); - virtual void in(usb_ep_t endpoint); - - bool _request_get_descriptor(); - bool _control_out(); - bool _control_in(); - bool _request_set_address(); - bool _request_set_configuration(); - bool _request_set_feature(); - bool _request_clear_feature(); - bool _request_get_status(); - bool _request_setup(); - void _control_setup(); - void _control_abort(); - void _control_abort_start(); - void _control_setup_continue(); - void _decode_setup_packet(uint8_t *data, setup_packet_t *packet); - bool _request_get_configuration(); - bool _request_get_interface(); - bool _request_set_interface(); - void _change_state(DeviceState state); - void _run_later(void (USBDevice::*function)()); - - void _complete_request(); - void _complete_request_xfer_done(); - void _complete_set_configuration(); - void _complete_set_interface(); - - struct endpoint_info_t { - mbed::Callback callback; - uint16_t max_packet_size; - uint16_t transfer_size; - uint8_t flags; - uint8_t pending; - }; - - struct usb_device_t { - volatile DeviceState state; - uint8_t configuration; - bool suspended; - }; - - enum ControlState { - Setup, - DataOut, - DataIn, - Status - }; - - enum UserCallback { - None, - Request, - RequestXferDone, - SetConfiguration, - SetInterface - }; - - struct complete_request_t { - RequestResult result; - uint8_t *data; - uint32_t size; - }; - - union complete_args_t { - complete_request_t request; - bool status; - }; - - struct control_transfer_t { - setup_packet_t setup; - uint8_t *ptr; - uint32_t remaining; - uint8_t direction; - bool zlp; - bool notify; - ControlState stage; - UserCallback user_callback; - complete_args_t args; - }; - - endpoint_info_t _endpoint_info[32 - 2]; - - USBPhy *_phy; - bool _initialized; - bool _connected; - bool _endpoint_add_remove_allowed; - control_transfer_t _transfer; - usb_device_t _device; - uint32_t _max_packet_size_ep0; - void (USBDevice::*_post_process)(); - - bool _setup_ready; - bool _abort_control; - - uint16_t _current_interface; - uint8_t _current_alternate; - uint32_t _locked; -}; - -/** @}*/ - -#endif diff --git a/drivers/include/drivers/internal/USBDevice_Types.h b/drivers/include/drivers/internal/USBDevice_Types.h deleted file mode 100644 index 488f61a..0000000 --- a/drivers/include/drivers/internal/USBDevice_Types.h +++ /dev/null @@ -1,53 +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 USBDEVICE_TYPES_H -#define USBDEVICE_TYPES_H - -/* Standard requests */ -#define GET_STATUS (0) -#define CLEAR_FEATURE (1) -#define SET_FEATURE (3) -#define SET_ADDRESS (5) -#define GET_DESCRIPTOR (6) -#define SET_DESCRIPTOR (7) -#define GET_CONFIGURATION (8) -#define SET_CONFIGURATION (9) -#define GET_INTERFACE (10) -#define SET_INTERFACE (11) - -/* bmRequestType.dataTransferDirection */ -#define HOST_TO_DEVICE (0) -#define DEVICE_TO_HOST (1) - -/* bmRequestType.Type*/ -#define STANDARD_TYPE (0) -#define CLASS_TYPE (1) -#define VENDOR_TYPE (2) -#define RESERVED_TYPE (3) - -/* bmRequestType.Recipient */ -#define DEVICE_RECIPIENT (0) -#define INTERFACE_RECIPIENT (1) -#define ENDPOINT_RECIPIENT (2) -#define OTHER_RECIPIENT (3) - -/* Descriptors */ -#define DESCRIPTOR_TYPE(wValue) (wValue >> 8) -#define DESCRIPTOR_INDEX(wValue) (wValue & 0xff) - -#endif diff --git a/drivers/include/drivers/internal/USBHID_Types.h b/drivers/include/drivers/internal/USBHID_Types.h deleted file mode 100644 index 9a8d9c8..0000000 --- a/drivers/include/drivers/internal/USBHID_Types.h +++ /dev/null @@ -1,93 +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 USBCLASS_HID_TYPES -#define USBCLASS_HID_TYPES - -#include - -/* */ -#define HID_VERSION_1_11 (0x0111) - -/* HID Class */ -#define HID_CLASS (3) -#define HID_SUBCLASS_NONE (0) -#define HID_SUBCLASS_BOOT (1) -#define HID_PROTOCOL_NONE (0) -#define HID_PROTOCOL_KEYBOARD (1) -#define HID_PROTOCOL_MOUSE (2) - -/* Descriptors */ -#define HID_DESCRIPTOR (33) -#define HID_DESCRIPTOR_LENGTH (0x09) -#define REPORT_DESCRIPTOR (34) - -/* Class requests */ -#define GET_REPORT (0x1) -#define GET_IDLE (0x2) -#define SET_REPORT (0x9) -#define SET_IDLE (0xa) - -/* HID Class Report Descriptor */ -/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ -/* of data as per HID Class standard */ - -/* Main items */ -#define INPUT(size) (0x80 | size) -#define OUTPUT(size) (0x90 | size) -#define FEATURE(size) (0xb0 | size) -#define COLLECTION(size) (0xa0 | size) -#define END_COLLECTION(size) (0xc0 | size) - -/* Global items */ -#define USAGE_PAGE(size) (0x04 | size) -#define LOGICAL_MINIMUM(size) (0x14 | size) -#define LOGICAL_MAXIMUM(size) (0x24 | size) -#define PHYSICAL_MINIMUM(size) (0x34 | size) -#define PHYSICAL_MAXIMUM(size) (0x44 | size) -#define UNIT_EXPONENT(size) (0x54 | size) -#define UNIT(size) (0x64 | size) -#define REPORT_SIZE(size) (0x74 | size) -#define REPORT_ID(size) (0x84 | size) -#define REPORT_COUNT(size) (0x94 | size) -#define PUSH(size) (0xa4 | size) -#define POP(size) (0xb4 | size) - -/* Local items */ -#define USAGE(size) (0x08 | size) -#define USAGE_MINIMUM(size) (0x18 | size) -#define USAGE_MAXIMUM(size) (0x28 | size) -#define DESIGNATOR_INDEX(size) (0x38 | size) -#define DESIGNATOR_MINIMUM(size) (0x48 | size) -#define DESIGNATOR_MAXIMUM(size) (0x58 | size) -#define STRING_INDEX(size) (0x78 | size) -#define STRING_MINIMUM(size) (0x88 | size) -#define STRING_MAXIMUM(size) (0x98 | size) -#define DELIMITER(size) (0xa8 | size) - -/* HID Report */ -/* Where report IDs are used the first byte of 'data' will be the */ -/* report ID and 'length' will include this report ID byte. */ - -#define MAX_HID_REPORT_SIZE (64) - -typedef struct { - uint32_t length; - uint8_t data[MAX_HID_REPORT_SIZE]; -} HID_REPORT; - -#endif diff --git a/drivers/source/usb/AsyncOp.cpp b/drivers/source/usb/AsyncOp.cpp deleted file mode 100644 index 85ac553..0000000 --- a/drivers/source/usb/AsyncOp.cpp +++ /dev/null @@ -1,146 +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 "AsyncOp.h" -#include "mbed_critical.h" -#include "mbed_assert.h" - -using namespace rtos; - -AsyncOp::AsyncOp(): - _list(NULL), _wait(NULL), _aborted(false), _timeout(false) -{ - -} - -AsyncOp::AsyncOp(mbed::Callback &callback): - _list(NULL), _wait(NULL), _aborted(false), _timeout(false) -{ - _callback = callback; -} - -AsyncOp::~AsyncOp() -{ - MBED_ASSERT(_list == NULL); -} - -void AsyncOp::wait(rtos::Mutex *host_mutex, rtos::Kernel::Clock::duration_u32 rel_time) -{ - // Optimization so semaphore is only created if necessary - core_util_critical_section_enter(); - bool done = _list == NULL; - core_util_critical_section_exit(); - if (done) { - return; - } - - // Construct semaphore to wait on - Semaphore sem(0); - - core_util_critical_section_enter(); - done = _list == NULL; - // Wait is only allowed to be called from one thread - MBED_ASSERT(_wait == NULL); - _wait = &sem; - core_util_critical_section_exit(); - - if (done) { - // Operation was signaled before semaphore was set - return; - } - - if (sem.try_acquire_for(rel_time)) { - // Operation completion signaled semaphore - return; - } - - _host_lock(host_mutex); - _abort(true); - _host_unlock(host_mutex); -} - -void AsyncOp::abort() -{ - // Host lock must be held - - _abort(false); -} - -void AsyncOp::complete() -{ - core_util_critical_section_enter(); - - mbed::Callback cb = _callback; - _callback = nullptr; - _list = nullptr; - if (_wait != nullptr) { - _wait->release(); - } - - core_util_critical_section_exit(); - - if (cb) { - cb(); - } -} - -bool AsyncOp::timeout() -{ - core_util_critical_section_enter(); - - bool ret = _timeout; - - core_util_critical_section_exit(); - return ret; -} - -void AsyncOp::_abort(bool timeout) -{ - // host lock must be held - - core_util_critical_section_enter(); - OperationListBase *list = _list; - if (list) { - _callback = nullptr; - _aborted = true; - _wait = nullptr; - _timeout = timeout; - _list = nullptr; - } - core_util_critical_section_exit(); - if (list) { - list->remove(this); - } -} - -void AsyncOp::_host_lock(rtos::Mutex *host_mutex) -{ - if (host_mutex) { - host_mutex->lock(); - } else { - core_util_critical_section_enter(); - } -} - -void AsyncOp::_host_unlock(rtos::Mutex *host_mutex) -{ - if (host_mutex) { - host_mutex->unlock(); - } else { - core_util_critical_section_exit(); - } -} diff --git a/drivers/source/usb/ByteBuffer.cpp b/drivers/source/usb/ByteBuffer.cpp deleted file mode 100644 index 87ee3db..0000000 --- a/drivers/source/usb/ByteBuffer.cpp +++ /dev/null @@ -1,151 +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 "ByteBuffer.h" -#include "mbed_assert.h" -#include - -ByteBuffer::ByteBuffer(uint32_t size): _head(0), _tail(0), _size(0), _buf(NULL) -{ - resize(_size); -} - -ByteBuffer::~ByteBuffer() -{ - delete[] _buf; - _buf = 0; -} - -void ByteBuffer::resize(uint32_t size) -{ - delete[] _buf; - _head = 0; - _tail = 0; - _size = size + 1; - _buf = new uint8_t[_size](); -} - -void ByteBuffer::push(uint8_t data) -{ - _buf[_tail] = data; - _tail++; - if (_tail >= _size) { - _tail -= _size; - } - // Overflow not allowed - MBED_ASSERT(_head != _tail); -} - -void ByteBuffer::write(uint8_t *data, uint32_t size) -{ - MBED_ASSERT(size <= free()); - - if (size == 0) { - return; - } - - uint32_t new_tail = _tail + size; - if (new_tail >= _size) { - new_tail -= _size; - } - - // Perform first memcpy - uint32_t until_end = _size - _tail; - uint32_t copy_size = until_end < size ? until_end : size; - memcpy(_buf + _tail, data, copy_size); - data += copy_size; - size -= copy_size; - - // Perform second memcpy - if (size > 0) { - memcpy(_buf, data, size); - } - - // Update tail - _tail = new_tail; -} - -uint8_t ByteBuffer::pop() -{ - // Underflow not allowed - MBED_ASSERT(_head != _tail); - uint8_t val = _buf[_head]; - _head++; - if (_head >= _size) { - _head -= _size; - } - return val; -} - -void ByteBuffer::read(uint8_t *data, uint32_t size) -{ - MBED_ASSERT(size <= ByteBuffer::size()); - - if (size == 0) { - return; - } - - uint32_t new_head = _head + size; - if (new_head >= _size) { - new_head -= _size; - } - - // Perform first memcpy - uint32_t until_end = _size - _head; - uint32_t copy_size = until_end < size ? until_end : size; - memcpy(data, _buf + _head, copy_size); - data += copy_size; - size -= copy_size; - - // Perform second memcpy - if (size > 0) { - memcpy(data, _buf, size); - } - - // Update head - _head = new_head; -} - -uint32_t ByteBuffer::size() -{ - uint32_t size; - if (_tail < _head) { - size = _size + _tail - _head; - } else { - size = _tail - _head; - } - return size; -} - -uint32_t ByteBuffer::free() -{ - return _size - size() - 1; -} - -bool ByteBuffer::full() -{ - uint32_t next = _tail + 1; - if (next >= _size) { - next -= _size; - } - return next == _head; -} - -bool ByteBuffer::empty() -{ - return _head == _tail; -} diff --git a/drivers/source/usb/EndpointResolver.cpp b/drivers/source/usb/EndpointResolver.cpp deleted file mode 100644 index 90f549b..0000000 --- a/drivers/source/usb/EndpointResolver.cpp +++ /dev/null @@ -1,132 +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 "EndpointResolver.h" - -static uint32_t logical_to_index(uint32_t logical, bool in_not_out) -{ - return (logical << 1) | (in_not_out ? 1 : 0); -} - -static uint32_t index_to_logical(uint32_t index) -{ - return index >> 1; -} - - -EndpointResolver::EndpointResolver(const usb_ep_table_t *table) : _table(table), _cost(0), _used(0), _valid(true) -{ - // Do nothing -} - -EndpointResolver::~EndpointResolver() -{ - // Do nothing -} - -void EndpointResolver::endpoint_ctrl(uint32_t size) -{ - endpoint_in(USB_EP_TYPE_CTRL, size); - endpoint_out(USB_EP_TYPE_CTRL, size); -} - -usb_ep_t EndpointResolver::next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size) -{ - int index = next_index(type, in_not_out); - if (index < 0) { - _valid = false; - return 0; - } - - const usb_ep_entry_t &entry = _table->table[index_to_logical(index)]; - _cost += entry.base_cost + entry.byte_cost * size; - _used |= 1 << index; - - return index_to_endpoint(index); - -} -usb_ep_t EndpointResolver::endpoint_in(usb_ep_type_t type, uint32_t size) -{ - return next_free_endpoint(true, type, size); -} - -usb_ep_t EndpointResolver::endpoint_out(usb_ep_type_t type, uint32_t size) -{ - return next_free_endpoint(false, type, size); -} - -bool EndpointResolver::valid() -{ - return _valid && (_cost <= _table->resources); -} - -void EndpointResolver::reset() -{ - _cost = 0; - _used = 0; - _valid = true; -} - -usb_ep_t EndpointResolver::index_to_endpoint(int index) -{ - return index_to_logical(index) | ((index & 1) ? 0x80 : 0); -} - -int EndpointResolver::next_index(usb_ep_type_t type, bool in_not_out) -{ - for (int logical = 0; logical < (int)(sizeof(_table->table) / sizeof(_table->table[0])); logical++) { - uint32_t index = logical_to_index(logical, in_not_out); - uint32_t other = logical_to_index(logical, !in_not_out); - const usb_ep_entry_t &entry = _table->table[logical]; - - usb_ep_attr_t dir = entry.attributes & USB_EP_ATTR_DIR_MASK; - bool in_allowed = dir != USB_EP_ATTR_DIR_OUT; - bool out_allowed = dir != USB_EP_ATTR_DIR_IN; - bool shared = dir == USB_EP_ATTR_DIR_IN_OR_OUT; - - if (!(entry.attributes & (1 << type))) { - // This type is not supported - continue; - } - - if (in_not_out && !in_allowed) { - // In endpoint not supported - continue; - } - - if (!in_not_out && !out_allowed) { - // Out endpoint not supported - continue; - } - - if (_used & (1 << index)) { - // This endpoint is in use - continue; - } - - if (shared && (_used & (1 << other))) { - // This endpoint can only be one direction at a time and is in - // use by the other direction - continue; - } - - return index; - } - - // Not found - return -1; -} diff --git a/drivers/source/usb/LinkedListBase.cpp b/drivers/source/usb/LinkedListBase.cpp deleted file mode 100644 index 597b395..0000000 --- a/drivers/source/usb/LinkedListBase.cpp +++ /dev/null @@ -1,85 +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 "LinkedList.h" -#include "LinkEntry.h" -#include "mbed_assert.h" - -LinkedListBase::LinkedListBase(): _head(0), _tail(0) -{ - -} - -LinkedListBase::~LinkedListBase() -{ - -} - -LinkEntry *LinkedListBase::head() -{ - return _head; -} - -void LinkedListBase::enqueue(LinkEntry *entry) -{ - entry->_next = NULL; - if (_tail == NULL) { - _head = entry; - } else { - _tail->_next = entry; - } - _tail = entry; -} - -LinkEntry *LinkedListBase::dequeue() -{ - if (_head == NULL) { - return NULL; - } - if (_head->_next == NULL) { - _tail = NULL; - } - LinkEntry *entry = _head; - _head = _head->_next; - entry->_next = NULL; - return entry; -} - -void LinkedListBase::remove(LinkEntry *entry) -{ - LinkEntry *prev = NULL; - LinkEntry *cur = _head; - while (cur != entry) { - if (cur == NULL) { - // Element is not in the list - return; - } - prev = cur; - cur = cur->_next; - } - - if (prev != NULL) { - prev->_next = entry->_next; - } - if (entry == _head) { - _head = entry->_next; - } - if (entry == _tail) { - _tail = prev; - } - entry->_next = NULL; -} diff --git a/drivers/source/usb/OperationListBase.cpp b/drivers/source/usb/OperationListBase.cpp deleted file mode 100644 index 0cbcac6..0000000 --- a/drivers/source/usb/OperationListBase.cpp +++ /dev/null @@ -1,88 +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 "OperationListBase.h" -#include "AsyncOp.h" -#include "mbed_assert.h" - -OperationListBase::OperationListBase() -{ - -} - -OperationListBase::~OperationListBase() -{ - remove_all(); -} - -bool OperationListBase::empty() -{ - return _list.head() == NULL; -} - -void OperationListBase::add(AsyncOp *op) -{ - bool was_empty = _list.head() == NULL; - op->_list = this; - _list.enqueue(op); - if (was_empty) { - process(); - } -} - -void OperationListBase::process() -{ - while (true) { - AsyncOp *op = static_cast(_list.head()); - if (op == NULL) { - // List empty, nothing left to do - break; - } - if (!op->process()) { - // Processing is in progress - break; - } - _list.dequeue(); - op->complete(); - } -} - -void OperationListBase::remove(AsyncOp *op) -{ - bool head = _list.head() == op; - _list.remove(op); - if (head) { - process(); - } -} - -AsyncOp *OperationListBase::dequeue_raw() -{ - return static_cast(_list.dequeue()); -} - -void OperationListBase::remove_all() -{ - while (true) { - AsyncOp *op = static_cast(_list.head()); - if (op == NULL) { - // List empty, nothing left to do - break; - } - op->complete(); - } -} diff --git a/drivers/source/usb/PolledQueue.cpp b/drivers/source/usb/PolledQueue.cpp deleted file mode 100644 index 0f6e5e7..0000000 --- a/drivers/source/usb/PolledQueue.cpp +++ /dev/null @@ -1,93 +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 "drivers/internal/PolledQueue.h" - -#include "events/mbed_events.h" -#include "platform/Callback.h" - - -PolledQueue::PolledQueue(mbed::Callback cb): _cb(cb) -{ - -} - -PolledQueue::~PolledQueue() -{ - -} - -void PolledQueue::dispatch() -{ - core_util_critical_section_enter(); - uint64_t buf[MBED_MAX_TASK_SIZE / sizeof(uint64_t)]; - - while (true) { - - // Atomically dequeue the task and copy the callback - TaskBase *task = _list.dequeue(); - if (!task) { - break; - } - MBED_ASSERT(sizeof(buf) >= task_size(task)); - TaskBase::run_callback_t callback = task_start(task, (uint8_t *)buf, sizeof(buf)); - - // Run the callback outside the critical section - core_util_critical_section_exit(); - callback((uint8_t *)buf); - core_util_critical_section_enter(); - - // Finish - task_finish(task); - task = NULL; - - } - - core_util_critical_section_exit(); -} - -void PolledQueue::attach(mbed::Callback cb) -{ - core_util_critical_section_enter(); - - _cb = cb; - - core_util_critical_section_exit(); -} - -void PolledQueue::post(TaskBase *task) -{ - core_util_critical_section_enter(); - - bool empty = _list.head() == NULL; - _list.remove(task); - _list.enqueue(task); - if (empty && _cb) { - _cb(); - } - - core_util_critical_section_exit(); -} - -void PolledQueue::cancel(TaskBase *task) -{ - core_util_critical_section_enter(); - - _list.remove(task); - - core_util_critical_section_exit(); -} diff --git a/drivers/source/usb/TaskBase.cpp b/drivers/source/usb/TaskBase.cpp deleted file mode 100644 index feabb7a..0000000 --- a/drivers/source/usb/TaskBase.cpp +++ /dev/null @@ -1,152 +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 "drivers/internal/TaskBase.h" -#include "drivers/internal/TaskQueue.h" -#include "events/mbed_events.h" -#include "rtos/Semaphore.h" -#include "platform/mbed_critical.h" - -TaskBase::TaskBase(TaskQueue *q) - : _queue(q), _posted(false), _start_count(0), _flush_sem(NULL) -{ - -} - -TaskBase::~TaskBase() -{ - cancel(); - wait(); -} - -void TaskBase::set(TaskQueue *q) -{ - core_util_critical_section_enter(); - - // Cannot set the queue when it has been posted but has not been finished - MBED_ASSERT(!_posted); - _queue = q; - - core_util_critical_section_exit(); -} - -void TaskBase::cancel() -{ - core_util_critical_section_enter(); - - if (_posted) { - _queue->cancel(this); - _posted = false; - _wake_check(); - } - - core_util_critical_section_exit(); -} - -void TaskBase::wait() -{ - // Fast path check for finished - core_util_critical_section_enter(); - if (finished()) { - core_util_critical_section_exit(); - return; - } - core_util_critical_section_exit(); - - rtos::Semaphore sem; - - // If the event is in-flight then wait for it to complete - core_util_critical_section_enter(); - if (finished()) { - // This element has been flushed from the queue - core_util_critical_section_exit(); - return; - } - _flush_sem = &sem; - core_util_critical_section_exit(); - - sem.acquire(); -} - -bool TaskBase::ready() -{ - core_util_critical_section_enter(); - - bool is_ready = !_posted; - - core_util_critical_section_exit(); - return is_ready; -} - -bool TaskBase::finished() -{ - core_util_critical_section_enter(); - - bool is_finished = !_posted && (_start_count == 0); - - core_util_critical_section_exit(); - return is_finished; -} - -void TaskBase::finish() -{ - // Nothing to do -} - -void TaskBase::post() -{ - core_util_critical_section_enter(); - - MBED_ASSERT(_queue); - if (_queue) { - MBED_ASSERT(!_posted); - _queue->post(this); - _posted = true; - } - - core_util_critical_section_exit(); -} - -TaskBase::run_callback_t TaskBase::_start(void *buffer, uint32_t size) -{ - // Each call to _start must result in a call to _finish - MBED_ASSERT(_start_count < 0xFFFF); - _start_count++; - _posted = false; - - return start(buffer, size); -} - -void TaskBase::_finish() -{ - // Each call to _finish must be preceded by a call to _start - MBED_ASSERT(_start_count > 0); - _start_count--; - _wake_check(); - finish(); -} - -void TaskBase::_wake_check() -{ - if (!finished()) { - return; - } - if (_flush_sem) { - _flush_sem->release(); - _flush_sem = NULL; - } -} diff --git a/drivers/source/usb/USBAudio.cpp b/drivers/source/usb/USBAudio.cpp deleted file mode 100644 index dd29b21..0000000 --- a/drivers/source/usb/USBAudio.cpp +++ /dev/null @@ -1,1001 +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 "USBAudio.h" -#include "USBAudio_Types.h" -#include "EndpointResolver.h" -#include "usb_phy_api.h" - -#define SAMPLE_SIZE 2 -#define XFER_FREQUENCY_HZ 1000 -#define WRITE_READY_UNBLOCK (1 << 0) -#define READ_READY_UNBLOCK (1 << 1) - -class USBAudio::AsyncWrite: public AsyncOp { -public: - AsyncWrite(USBAudio *audio, uint8_t *buf, uint32_t size): - audio(audio), tx_buf(buf), tx_size(size), result(false) - { - - } - - virtual ~AsyncWrite() - { - - } - - virtual bool process() - { - if (audio->_tx_state != Opened) { - result = false; - return true; - } - - uint32_t actual_size = 0; - audio->write_nb(tx_buf, tx_size, &actual_size); - tx_size -= actual_size; - tx_buf += actual_size; - if (tx_size == 0) { - result = true; - return true; - } - - return false; - } - - USBAudio *audio; - uint8_t *tx_buf; - uint32_t tx_size; - bool result; -}; - -class USBAudio::AsyncRead: public AsyncOp { -public: - AsyncRead(USBAudio *audio, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all) - : audio(audio), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false) - { - - } - - virtual ~AsyncRead() - { - - } - - virtual bool process() - { - if (audio->_rx_state != Opened) { - result = false; - return true; - } - - uint32_t actual_size = 0; - audio->read_nb(rx_buf, rx_size, &actual_size); - rx_buf += actual_size; - *rx_actual += actual_size; - rx_size -= actual_size; - if ((!all && *rx_actual > 0) || (rx_size == 0)) { - // Wake thread if request is done - result = true; - return true; - } - - return false; - } - - USBAudio *audio; - uint8_t *rx_buf; - uint32_t rx_size; - uint32_t *rx_actual; - bool all; - bool result; -}; - -static void stub_volume() -{ - -} - -static void stub_handler(USBAudio::AudioEvent event) -{ - (void)event; -} - -USBAudio::USBAudio(bool connect, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBDevice(get_usb_phy(), vendor_id, product_id, product_release) -{ - _init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms); - - // connect or init device - if (connect) { - USBDevice::connect(); - } else { - USBDevice::init(); - } -} - -USBAudio::USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBDevice(phy, vendor_id, product_id, product_release) -{ - _init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms); -} - -void USBAudio::_init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms) -{ - _connected = false; - - _volume = 0; - _mute = 0; - _vol_cur = 0x0080; - _vol_min = 0x0000; - _vol_max = 0x0100; - _vol_res = 0x0004; - - _update_vol = mbed::callback(stub_volume); - _tx_done = mbed::callback(stub_handler); - _rx_done = mbed::callback(stub_handler); - - _rx_overflow = 0; - _tx_underflow = 0; - - _tx_freq = frequency_tx; - _rx_freq = frequency_rx; - - _tx_channel_count = channel_count_tx; - _rx_channel_count = channel_count_rx; - - _tx_idle = true; - _tx_frame_fract = 0; - _tx_whole_frames_per_xfer = _tx_freq / XFER_FREQUENCY_HZ; - _tx_fract_frames_per_xfer = _tx_freq % XFER_FREQUENCY_HZ; - - uint32_t max_frames = _tx_whole_frames_per_xfer + (_tx_fract_frames_per_xfer ? 1 : 0); - _tx_packet_size_max = max_frames * SAMPLE_SIZE * _tx_channel_count; - _rx_packet_size_max = (_rx_freq + 1000 - 1) / 1000 * _rx_channel_count * 2; - - _tx_packet_buf = new uint8_t[_tx_packet_size_max](); - _rx_packet_buf = new uint8_t[_rx_packet_size_max](); - - _tx_queue.resize(buffer_ms * _tx_channel_count * SAMPLE_SIZE * _tx_freq / XFER_FREQUENCY_HZ); - _rx_queue.resize(buffer_ms * _rx_channel_count * SAMPLE_SIZE * _rx_freq / XFER_FREQUENCY_HZ); - - _tx_state = Closed; - _rx_state = Closed; - - EndpointResolver resolver(endpoint_table()); - resolver.endpoint_ctrl(64); - _episo_out = resolver.endpoint_out(USB_EP_TYPE_ISO, _tx_packet_size_max); - _episo_in = resolver.endpoint_in(USB_EP_TYPE_ISO, _rx_packet_size_max); - MBED_ASSERT(resolver.valid()); - - _channel_config_rx = (_rx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R; - _channel_config_tx = (_tx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R; - - _build_configuration_desc(); -} - -USBAudio::~USBAudio() -{ - disconnect(); - deinit(); - - delete[] _tx_packet_buf; - delete[] _rx_packet_buf; -} - -void USBAudio::connect() -{ - lock(); - - if (!_connected) { - USBDevice::connect(); - _connected = true; - _receive_change(Closed); - _send_change(Closed); - } - - unlock(); -} - -void USBAudio::disconnect() -{ - lock(); - - if (_connected) { - _connected = false; - USBDevice::disconnect(); - _receive_change(Powerdown); - _send_change(Powerdown); - } - - unlock(); -} - -bool USBAudio::read(uint8_t *buf, uint32_t size) -{ - lock(); - - uint32_t actual; - AsyncRead read(this, buf, size, &actual, true); - _read_list.add(&read); - - unlock(); - - read.wait(NULL); - return read.result; -} - -void USBAudio::read_nb(uint8_t *buf, uint32_t size, uint32_t *actual) -{ - - lock(); - - uint32_t available = _rx_queue.size(); - uint32_t copy_size = available > size ? size : available; - _rx_queue.read(buf, copy_size); - *actual = copy_size; - - unlock(); -} - -uint32_t USBAudio::read_overflows(bool clear) -{ - lock(); - - uint32_t overflows = _rx_overflow; - if (clear) { - _rx_overflow = 0; - } - - unlock(); - return overflows; -} - - -bool USBAudio::read_ready() -{ - lock(); - - bool ready = _rx_state == Opened; - - unlock(); - - return ready; -} - -void USBAudio::read_wait_ready() -{ - _flags.wait_any(READ_READY_UNBLOCK, osWaitForever, false); -} - -bool USBAudio::write(uint8_t *buf, uint32_t size) -{ - lock(); - - AsyncWrite write(this, buf, size); - _write_list.add(&write); - - unlock(); - - write.wait(NULL); - return write.result; -} - -void USBAudio::write_nb(uint8_t *buf, uint32_t size, uint32_t *actual) -{ - lock(); - - uint32_t available = _tx_queue.free(); - uint32_t copy_size = available > size ? size : available; - _tx_queue.write(buf, copy_size); - *actual = copy_size; - _send_isr_start(); - - unlock(); -} - -uint32_t USBAudio::write_underflows(bool clear) -{ - lock(); - - uint32_t underflows = _tx_underflow; - if (clear) { - _tx_underflow = 0; - } - - unlock(); - return underflows; -} - -bool USBAudio::write_ready() -{ - lock(); - - bool ready = _tx_state == Opened; - - unlock(); - - return ready; -} - -void USBAudio::write_wait_ready() -{ - _flags.wait_any(WRITE_READY_UNBLOCK, osWaitForever, false); -} - - -float USBAudio::get_volume() -{ - lock(); - - float ret = _mute ? 0.0f : _volume; - - unlock(); - return ret; -} - -void USBAudio::attach(mbed::Callback &cb) -{ - lock(); - - _update_vol = cb; - if (!_update_vol) { - _update_vol = stub_volume; - } - - unlock(); -} - -void USBAudio::attach_tx(mbed::Callback &cb) -{ - lock(); - - _tx_done = cb; - if (!_tx_done) { - _tx_done = mbed::callback(stub_handler); - } - - unlock(); -} - -void USBAudio::attach_rx(mbed::Callback &cb) -{ - lock(); - - _rx_done = cb; - if (!_rx_done) { - _rx_done = mbed::callback(stub_handler); - } - - unlock(); -} - -void USBAudio::callback_state_change(DeviceState new_state) -{ - assert_locked(); - - if (_connected && (new_state != Configured)) { - _receive_change(Closed); - _send_change(Closed); - } -} - -void USBAudio::callback_request(const setup_packet_t *setup) -{ - assert_locked(); - - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - // Process class-specific requests - if (setup->bmRequestType.Type == CLASS_TYPE) { - - // Feature Unit: Interface = 0, ID = 2 - if (setup->wIndex == 0x0200) { - - // Master Channel - if ((setup->wValue & 0xff) == 0) { - - switch (setup->wValue >> 8) { - case MUTE_CONTROL: - switch (setup->bRequest) { - case REQUEST_GET_CUR: - size = 1; - data = &_mute; - result = Send; - break; - - case REQUEST_SET_CUR: - size = 1; - data = _control_receive; - result = Receive; - break; - default: - break; - } - break; - case VOLUME_CONTROL: - switch (setup->bRequest) { - case REQUEST_GET_CUR: - size = 2; - data = (uint8_t *)&_vol_cur; - result = Send; - break; - case REQUEST_GET_MIN: - size = 2; - data = (uint8_t *)&_vol_min; - result = Send; - break; - case REQUEST_GET_MAX: - size = 2; - data = (uint8_t *)&_vol_max; - result = Send; - break; - case REQUEST_GET_RES: - size = 2; - data = (uint8_t *)&_vol_res; - result = Send; - break; - - case REQUEST_SET_CUR: - size = 2; - data = _control_receive; - result = Receive; - break; - case REQUEST_SET_MIN: - size = 2; - data = _control_receive; - result = Receive; - break; - case REQUEST_SET_MAX: - size = 2; - data = _control_receive; - result = Receive; - break; - case REQUEST_SET_RES: - size = 2; - data = _control_receive; - result = Receive; - break; - } - break; - default: - break; - } - } - } - } - complete_request(result, data, size); -} - -void USBAudio::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - assert_locked(); - - if (aborted) { - complete_request_xfer_done(false); - return; - } - - if (setup->bmRequestType.dataTransferDirection == DEVICE_TO_HOST) { - complete_request_xfer_done(true); - return; - } - - if ((setup->wLength == 1) || (setup->wLength == 2)) { - uint16_t data = (_control_receive[0] << 0) | (_control_receive[1] << 8); - data &= ((setup->wLength == 1) ? 0xFF : 0xFFFF); - switch (setup->wValue >> 8) { - case MUTE_CONTROL: - switch (setup->bRequest) { - case REQUEST_SET_CUR: - _mute = data & 0xff; - _update_vol.call(); - break; - default: - break; - } - break; - case VOLUME_CONTROL: - switch (setup->bRequest) { - case REQUEST_SET_CUR: - _vol_cur = data; - _volume = (float)_vol_cur / (float)_vol_max; - _update_vol.call(); - break; - default: - break; - } - break; - default: - break; - } - complete_request_xfer_done(true); - return; - } - - complete_request_xfer_done(false); -} - -void USBAudio::callback_set_configuration(uint8_t configuration) -{ - assert_locked(); - - bool ret = false; - if (configuration == DEFAULT_CONFIGURATION) { - endpoint_remove_all(); - - // Configure isochronous endpoint - endpoint_add(_episo_out, _rx_packet_size_max, USB_EP_TYPE_ISO, static_cast(&USBAudio::_receive_isr)); - endpoint_add(_episo_in, _tx_packet_size_max, USB_EP_TYPE_ISO, static_cast(&USBAudio::_send_isr)); - - // activate readings on this endpoint - read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max); - ret = true; - } - complete_set_configuration(ret); -} - -void USBAudio::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - assert_locked(); - - bool ret = false; - if (interface == 0 && alternate == 0) { - ret = true; - } - if (interface == 1 && (alternate == 0 || alternate == 1)) { - _receive_change(alternate == 1 ? Opened : Closed); - ret = true; - } - if (interface == 2 && (alternate == 0 || alternate == 1)) { - _send_change(alternate == 1 ? Opened : Closed); - ret = true; - } - complete_set_interface(ret); -} - -const uint8_t *USBAudio::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - return _config_descriptor; -} - -const uint8_t *USBAudio::string_iinterface_desc() -{ - static const uint8_t stringIinterfaceDescriptor[] = { - 0x0c, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio - }; - return stringIinterfaceDescriptor; -} - -const uint8_t *USBAudio::string_iproduct_desc() -{ - static const uint8_t stringIproductDescriptor[] = { - 0x16, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio - }; - return stringIproductDescriptor; -} - -#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ - + (5 * INTERFACE_DESCRIPTOR_LENGTH) \ - + (1 * CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1) \ - + (2 * INPUT_TERMINAL_DESCRIPTOR_LENGTH) \ - + (1 * FEATURE_UNIT_DESCRIPTOR_LENGTH) \ - + (2 * OUTPUT_TERMINAL_DESCRIPTOR_LENGTH) \ - + (2 * STREAMING_INTERFACE_DESCRIPTOR_LENGTH) \ - + (2 * FORMAT_TYPE_I_DESCRIPTOR_LENGTH) \ - + (2 * (ENDPOINT_DESCRIPTOR_LENGTH + 2)) \ - + (2 * STREAMING_ENDPOINT_DESCRIPTOR_LENGTH) ) - -#define TOTAL_CONTROL_INTF_LENGTH (CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1 + \ - 2*INPUT_TERMINAL_DESCRIPTOR_LENGTH + \ - FEATURE_UNIT_DESCRIPTOR_LENGTH + \ - 2*OUTPUT_TERMINAL_DESCRIPTOR_LENGTH) - -void USBAudio::_build_configuration_desc() -{ - uint8_t config_descriptor_temp[] = { - // Configuration 1 - CONFIGURATION_DESCRIPTOR_LENGTH, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) - MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) - 0x03, // bNumInterfaces - DEFAULT_CONFIGURATION, // bConfigurationValue - 0x00, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - - // Interface 0, Alternate Setting 0, Audio Control - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x00, // bNumEndpoints - AUDIO_CLASS, // bInterfaceClass - SUBCLASS_AUDIOCONTROL, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface - - - // Audio Control Interface - CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1,// bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_HEADER, // bDescriptorSubtype - LSB(0x0100), // bcdADC (LSB) - MSB(0x0100), // bcdADC (MSB) - LSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength - MSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength - 0x02, // bInCollection - 0x01, // baInterfaceNr - 0x02, // baInterfaceNr - - // Audio Input Terminal (Speaker) - INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_INPUT_TERMINAL, // bDescriptorSubtype - 0x01, // bTerminalID - LSB(TERMINAL_USB_STREAMING), // wTerminalType - MSB(TERMINAL_USB_STREAMING), // wTerminalType - 0x00, // bAssocTerminal - _rx_channel_count, // bNrChannels - (uint8_t)(LSB(_channel_config_rx)), // wChannelConfig - (uint8_t)(MSB(_channel_config_rx)), // wChannelConfig - 0x00, // iChannelNames - 0x00, // iTerminal - - // Audio Feature Unit (Speaker) - FEATURE_UNIT_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_FEATURE_UNIT, // bDescriptorSubtype - 0x02, // bUnitID - 0x01, // bSourceID - 0x01, // bControlSize - CONTROL_MUTE | - CONTROL_VOLUME, // bmaControls(0) - 0x00, // bmaControls(1) - 0x00, // iTerminal - - // Audio Output Terminal (Speaker) - OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype - 0x03, // bTerminalID - LSB(TERMINAL_SPEAKER), // wTerminalType - MSB(TERMINAL_SPEAKER), // wTerminalType - 0x00, // bAssocTerminal - 0x02, // bSourceID - 0x00, // iTerminal - - - // Audio Input Terminal (Microphone) - INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_INPUT_TERMINAL, // bDescriptorSubtype - 0x04, // bTerminalID - LSB(TERMINAL_MICROPHONE), // wTerminalType - MSB(TERMINAL_MICROPHONE), // wTerminalType - 0x00, // bAssocTerminal - _tx_channel_count, // bNrChannels - (uint8_t)(LSB(_channel_config_tx)), // wChannelConfig - (uint8_t)(MSB(_channel_config_tx)), // wChannelConfig - 0x00, // iChannelNames - 0x00, // iTerminal - - // Audio Output Terminal (Microphone) - OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype - 0x05, // bTerminalID - LSB(TERMINAL_USB_STREAMING), // wTerminalType - MSB(TERMINAL_USB_STREAMING), // wTerminalType - 0x00, // bAssocTerminal - 0x04, // bSourceID - 0x00, // iTerminal - - - - - - - // Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x01, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x00, // bNumEndpoints - AUDIO_CLASS, // bInterfaceClass - SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface - - // Interface 1, Alternate Setting 1, Audio Streaming - Operational - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x01, // bInterfaceNumber - 0x01, // bAlternateSetting - 0x01, // bNumEndpoints - AUDIO_CLASS, // bInterfaceClass - SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface - - // Audio Streaming Interface - STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - STREAMING_GENERAL, // bDescriptorSubtype - 0x01, // bTerminalLink - 0x00, // bDelay - LSB(FORMAT_PCM), // wFormatTag - MSB(FORMAT_PCM), // wFormatTag - - // Audio Type I Format - FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - STREAMING_FORMAT_TYPE, // bDescriptorSubtype - FORMAT_TYPE_I, // bFormatType - _rx_channel_count, // bNrChannels - 0x02, // bSubFrameSize - 16, // bBitResolution - 0x01, // bSamFreqType - (uint8_t)(LSB(_rx_freq)), // tSamFreq - (uint8_t)((_rx_freq >> 8) & 0xff), // tSamFreq - (uint8_t)((_rx_freq >> 16) & 0xff), // tSamFreq - - // Endpoint - Standard Descriptor - ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _episo_out, // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_rx_packet_size_max)), // wMaxPacketSize - (uint8_t)(MSB(_rx_packet_size_max)), // wMaxPacketSize - 0x01, // bInterval - 0x00, // bRefresh - 0x00, // bSynchAddress - - // Endpoint - Audio Streaming - STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType - ENDPOINT_GENERAL, // bDescriptor - 0x00, // bmAttributes - 0x00, // bLockDelayUnits - LSB(0x0000), // wLockDelay - MSB(0x0000), // wLockDelay - - - // Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x02, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x00, // bNumEndpoints - AUDIO_CLASS, // bInterfaceClass - SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface - - // Interface 1, Alternate Setting 1, Audio Streaming - Operational - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x02, // bInterfaceNumber - 0x01, // bAlternateSetting - 0x01, // bNumEndpoints - AUDIO_CLASS, // bInterfaceClass - SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface - - // Audio Streaming Interface - STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - SUBCLASS_AUDIOCONTROL, // bDescriptorSubtype - 0x05, // bTerminalLink (output terminal microphone) - 0x01, // bDelay - 0x01, // wFormatTag - 0x00, // wFormatTag - - // Audio Type I Format - FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType - SUBCLASS_AUDIOSTREAMING, // bDescriptorSubtype - FORMAT_TYPE_I, // bFormatType - _tx_channel_count, // bNrChannels - 0x02, // bSubFrameSize - 0x10, // bBitResolution - 0x01, // bSamFreqType - (uint8_t)(LSB(_tx_freq)), // tSamFreq - (uint8_t)((_tx_freq >> 8) & 0xff), // tSamFreq - (uint8_t)((_tx_freq >> 16) & 0xff), // tSamFreq - - // Endpoint - Standard Descriptor - ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _episo_in, // bEndpointAddress - E_ISOCHRONOUS, // bmAttributes - (uint8_t)(LSB(_tx_packet_size_max)), // wMaxPacketSize - (uint8_t)(MSB(_tx_packet_size_max)), // wMaxPacketSize - 0x01, // bInterval - 0x00, // bRefresh - 0x00, // bSynchAddress - - // Endpoint - Audio Streaming - STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType - ENDPOINT_GENERAL, // bDescriptor - 0x00, // bmAttributes - 0x00, // bLockDelayUnits - LSB(0x0000), // wLockDelay - MSB(0x0000), // wLockDelay - }; - - MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); - memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor)); -} - -void USBAudio::_receive_change(ChannelState new_state) -{ - assert_locked(); - - ChannelState prev_state = _rx_state; - _rx_state = new_state; - if (prev_state == new_state) { - // no change - return; - } - - if (prev_state == Opened) { - // Leaving the opened state - _read_list.process(); - _rx_done.call(End); - } - if (new_state == Opened) { - // Entering the opened state - _read_list.process(); - _rx_done.call(Start); - } - if (new_state == Closed) { - // Only block if the channel is closed - _flags.clear(READ_READY_UNBLOCK); - } else { - _flags.set(READ_READY_UNBLOCK); - } -} - -void USBAudio::_receive_isr() -{ - assert_locked(); - - uint32_t size = read_finish(_episo_out); - - if (size > _rx_queue.free()) { - _rx_overflow++; - } else { - - // Copy data over - _rx_queue.write(_rx_packet_buf, size); - - // Signal that there is more data available - _read_list.process(); - if (_rx_done) { - _rx_done.call(Transfer); - } - } - - read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max); -} - -void USBAudio::_send_change(ChannelState new_state) -{ - assert_locked(); - - ChannelState prev_state = _tx_state; - _tx_state = new_state; - if (prev_state == new_state) { - // no change - return; - } - - if (prev_state == Opened) { - // Leaving the opened state - _write_list.process(); - _tx_done.call(End); - } - if (new_state == Opened) { - // Entering the opened state - _write_list.process(); - _tx_done.call(Start); - } - if (new_state == Closed) { - // Only block if the channel is closed - _flags.clear(WRITE_READY_UNBLOCK); - } else { - _flags.set(WRITE_READY_UNBLOCK); - } -} -void USBAudio::_send_isr_start() -{ - assert_locked(); - - if (!_tx_idle) { - return; - } - - _send_isr_next_sync(); -} - -void USBAudio::_send_isr_next_sync() -{ - // Compute size to send - uint32_t fames = _tx_whole_frames_per_xfer; - if (_tx_frame_fract >= XFER_FREQUENCY_HZ) { - _tx_frame_fract -= XFER_FREQUENCY_HZ; - fames += 1; - } - uint32_t send_size = fames * _tx_channel_count * 2; - - // Check if this is the initial TX packet - if (_tx_idle && !_tx_queue.full()) { - // Don't start until the TX buffer is full - return; - } - - // Check if this stream was closed - if (_tx_state != Opened) { - _tx_idle = true; - return; - } - - // Check for enough data to send - if (_tx_queue.size() < send_size) { - _tx_underflow++; - _tx_idle = true; - return; - } - - // Copy data over - _tx_queue.read(_tx_packet_buf, send_size); - - // Start the write - write_start(_episo_in, _tx_packet_buf, send_size); - _tx_idle = false; - _tx_frame_fract += _tx_fract_frames_per_xfer; -} - -void USBAudio::_send_isr() -{ - assert_locked(); - - write_finish(_episo_in); - - _send_isr_next_sync(); - - // Signal that there is space for more data - _write_list.process(); - if (_tx_done) { - _tx_done.call(Transfer); - } -} diff --git a/drivers/source/usb/USBCDC.cpp b/drivers/source/usb/USBCDC.cpp deleted file mode 100644 index 9cf1ed0..0000000 --- a/drivers/source/usb/USBCDC.cpp +++ /dev/null @@ -1,644 +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 "USBCDC.h" -#include "EndpointResolver.h" -#include "AsyncOp.h" -#include "usb_phy_api.h" - -static const uint8_t cdc_line_coding_default[7] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08}; - -#define DEFAULT_CONFIGURATION (1) - -#define CDC_SET_LINE_CODING 0x20 -#define CDC_GET_LINE_CODING 0x21 -#define CDC_SET_CONTROL_LINE_STATE 0x22 - -// Control Line State bits -#define CLS_DTR (1 << 0) -#define CLS_RTS (1 << 1) - -class USBCDC::AsyncWrite: public AsyncOp { -public: - AsyncWrite(USBCDC *serial, uint8_t *buf, uint32_t size): - serial(serial), tx_buf(buf), tx_size(size), result(false) - { - - } - - virtual ~AsyncWrite() - { - - } - - virtual bool process() - { - if (!serial->_terminal_connected) { - result = false; - return true; - } - - uint32_t actual_size = 0; - serial->send_nb(tx_buf, tx_size, &actual_size, true); - tx_size -= actual_size; - tx_buf += actual_size; - if (tx_size == 0) { - result = true; - return true; - } - - // Start transfer if it hasn't been - serial->_send_isr_start(); - return false; - } - - USBCDC *serial; - uint8_t *tx_buf; - uint32_t tx_size; - bool result; -}; - -class USBCDC::AsyncRead: public AsyncOp { -public: - AsyncRead(USBCDC *serial, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all) - : serial(serial), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false) - { - - } - - virtual ~AsyncRead() - { - - } - - virtual bool process() - { - if (!serial->_terminal_connected) { - result = false; - return true; - } - - uint32_t actual_size = 0; - serial->receive_nb(rx_buf, rx_size, &actual_size); - rx_buf += actual_size; - *rx_actual += actual_size; - rx_size -= actual_size; - if ((!all && *rx_actual > 0) || (rx_size == 0)) { - // Wake thread if request is done - result = true; - return true; - } - - serial->_receive_isr_start(); - return false; - } - - USBCDC *serial; - uint8_t *rx_buf; - uint32_t rx_size; - uint32_t *rx_actual; - bool all; - bool result; -}; - -class USBCDC::AsyncWait: public AsyncOp { -public: - AsyncWait(USBCDC *serial) - : serial(serial) - { - - } - - virtual ~AsyncWait() - { - - } - - virtual bool process() - { - if (serial->_terminal_connected) { - return true; - } - - return false; - } - - USBCDC *serial; -}; - -USBCDC::USBCDC(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) - -{ - _init(); - if (connect_blocking) { - connect(); - wait_ready(); - } else { - init(); - } -} - -USBCDC::USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(phy, vendor_id, product_id, product_release) -{ - _init(); -} - -USBCDC::~USBCDC() -{ - deinit(); -} - -void USBCDC::_init() -{ - memcpy(_cdc_line_coding, cdc_line_coding_default, sizeof(_cdc_line_coding)); - - EndpointResolver resolver(endpoint_table()); - resolver.endpoint_ctrl(CDC_MAX_PACKET_SIZE); - _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); - _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); - _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, CDC_MAX_PACKET_SIZE); - MBED_ASSERT(resolver.valid()); - - _terminal_connected = false; - - _tx_in_progress = false; - _tx_buf = _tx_buffer; - _tx_size = 0; - - _rx_in_progress = false; - _rx_buf = _rx_buffer; - _rx_size = 0; -} - -void USBCDC::callback_reset() -{ - assert_locked(); - /* Called in ISR context */ - - _change_terminal_connected(false); -}; - -void USBCDC::callback_state_change(DeviceState new_state) -{ - assert_locked(); - /* Called in ISR context */ - - if (new_state != Configured) { - _change_terminal_connected(false); - } -} - -void USBCDC::callback_request(const setup_packet_t *setup) -{ - assert_locked(); - /* Called in ISR context */ - - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - /* Only process class-specific requests */ - if (setup->bmRequestType.Type == CLASS_TYPE) { - switch (setup->bRequest) { - case CDC_GET_LINE_CODING: - result = Send; - data = _cdc_line_coding; - size = 7; - break; - case CDC_SET_LINE_CODING: - result = Receive; - data = _cdc_new_line_coding; - size = 7; - break; - case CDC_SET_CONTROL_LINE_STATE: - if (setup->wValue & CLS_DTR) { - _change_terminal_connected(true); - } else { - _change_terminal_connected(false); - } - result = Success; - break; - default: - result = Failure; - break; - } - } - complete_request(result, data, size); -} - - -void USBCDC::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - assert_locked(); - /* Called in ISR context */ - - if (aborted) { - complete_request_xfer_done(false); - return; - } - - bool success = false; - - /* Process class-specific requests */ - if (setup->bmRequestType.Type == CLASS_TYPE) { - if ((setup->bRequest == CDC_SET_LINE_CODING) && (setup->wLength == 7)) { - if (memcmp(_cdc_line_coding, _cdc_new_line_coding, 7)) { - memcpy(_cdc_line_coding, _cdc_new_line_coding, 7); - - const uint8_t *buf = _cdc_line_coding; - int baud = buf[0] + (buf[1] << 8) - + (buf[2] << 16) + (buf[3] << 24); - int stop = buf[4]; - int bits = buf[6]; - int parity = buf[5]; - - line_coding_changed(baud, bits, parity, stop); - } - success = true; - } - if (setup->bRequest == CDC_GET_LINE_CODING) { - success = true; - } - } - - complete_request_xfer_done(success); -} - -void USBCDC::callback_set_configuration(uint8_t configuration) -{ - assert_locked(); - /* Called in ISR context */ - - bool ret = false; - if (configuration == DEFAULT_CONFIGURATION) { - // Configure endpoints > 0 - endpoint_add(_int_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_INT); - endpoint_add(_bulk_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_send_isr); - endpoint_add(_bulk_out, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_receive_isr); - - read_start(_bulk_out, _rx_buf, sizeof(_rx_buffer)); - _rx_in_progress = true; - - ret = true; - } - - complete_set_configuration(ret); -} - -void USBCDC::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - assert_locked(); - complete_set_interface(true); -} - -void USBCDC::_change_terminal_connected(bool connected) -{ - assert_locked(); - - _terminal_connected = connected; - if (!_terminal_connected) { - // Abort TX - if (_tx_in_progress) { - endpoint_abort(_bulk_in); - _tx_in_progress = false; - } - _tx_buf = _tx_buffer; - _tx_size = 0; - _tx_list.process(); - MBED_ASSERT(_tx_list.empty()); - - // Abort RX - if (_rx_in_progress) { - endpoint_abort(_bulk_in); - _rx_in_progress = false; - } - _rx_buf = _rx_buffer; - _rx_size = 0; - _rx_list.process(); - MBED_ASSERT(_rx_list.empty()); - - } - _connected_list.process(); -} - -bool USBCDC::ready() -{ - lock(); - - bool ready = _terminal_connected; - - unlock(); - return ready; -} - -void USBCDC::wait_ready() -{ - lock(); - - AsyncWait wait_op(this); - _connected_list.add(&wait_op); - - unlock(); - - wait_op.wait(NULL); -} - -bool USBCDC::send(uint8_t *buffer, uint32_t size) -{ - lock(); - - AsyncWrite write_op(this, buffer, size); - _tx_list.add(&write_op); - - unlock(); - - write_op.wait(NULL); - return write_op.result; -} - -void USBCDC::send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now) -{ - lock(); - - *actual = 0; - if (_terminal_connected && !_tx_in_progress) { - uint32_t free = sizeof(_tx_buffer) - _tx_size; - uint32_t write_size = free > size ? size : free; - if (size > 0) { - memcpy(_tx_buf, buffer, write_size); - } - _tx_size += write_size; - *actual = write_size; - if (now) { - _send_isr_start(); - } - } - - unlock(); -} - -void USBCDC::_send_isr_start() -{ - assert_locked(); - - if (!_tx_in_progress && _tx_size) { - if (USBDevice::write_start(_bulk_in, _tx_buffer, _tx_size)) { - _tx_in_progress = true; - } - } -} - -/* -* Called by when CDC data is sent -* Warning: Called in ISR -*/ -void USBCDC::_send_isr() -{ - assert_locked(); - - write_finish(_bulk_in); - _tx_buf = _tx_buffer; - _tx_size = 0; - _tx_in_progress = false; - - _tx_list.process(); - if (!_tx_in_progress) { - data_tx(); - } -} - -bool USBCDC::receive(uint8_t *buffer, uint32_t size, uint32_t *size_read) -{ - lock(); - - bool read_all = size_read == NULL; - uint32_t size_read_dummy; - uint32_t *size_read_ptr = read_all ? &size_read_dummy : size_read; - *size_read_ptr = 0; - AsyncRead read_op(this, buffer, size, size_read_ptr, read_all); - _rx_list.add(&read_op); - - unlock(); - - read_op.wait(NULL); - return read_op.result; -} - -void USBCDC::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *size_read) -{ - - *size_read = 0; - if (_terminal_connected && !_rx_in_progress) { - // Copy data over - uint32_t copy_size = _rx_size > size ? size : _rx_size; - memcpy(buffer, _rx_buf, copy_size); - *size_read = copy_size; - _rx_buf += copy_size; - _rx_size -= copy_size; - if (_rx_size == 0) { - _receive_isr_start(); - } - } -} - -void USBCDC::_receive_isr_start() -{ - if ((_rx_size == 0) && !_rx_in_progress) { - // Refill the buffer - _rx_in_progress = true; - read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer)); - } -} - -/* -* Called by when CDC data is received -* Warning: Called in ISR -*/ -void USBCDC::_receive_isr() -{ - assert_locked(); - - MBED_ASSERT(_rx_size == 0); - _rx_buf = _rx_buffer; - _rx_size = read_finish(_bulk_out); - _rx_in_progress = false; - _rx_list.process(); - if (!_rx_in_progress) { - data_rx(); - } - -} - -const uint8_t *USBCDC::device_desc() -{ - uint8_t ep0_size = endpoint_max_packet_size(0x00); - uint8_t device_descriptor_temp[] = { - 18, // bLength - 1, // bDescriptorType - 0x10, 0x01, // bcdUSB - 2, // bDeviceClass - 0, // bDeviceSubClass - 0, // 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 - 0x00, 0x01, // bcdDevice - 1, // iManufacturer - 2, // iProduct - 3, // iSerialNumber - 1 // 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::string_iinterface_desc() -{ - static const uint8_t stringIinterfaceDescriptor[] = { - 0x08, - STRING_DESCRIPTOR, - 'C', 0, 'D', 0, 'C', 0, - }; - return stringIinterfaceDescriptor; -} - -const uint8_t *USBCDC::string_iproduct_desc() -{ - static const uint8_t stringIproductDescriptor[] = { - 0x16, - STRING_DESCRIPTOR, - 'C', 0, 'D', 0, 'C', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 - }; - return stringIproductDescriptor; -} - - -#define CONFIG1_DESC_SIZE (9+8+9+5+5+4+5+7+9+7+7) - -const uint8_t *USBCDC::configuration_desc(uint8_t index) -{ - uint8_t config_descriptor_temp[] = { - // configuration descriptor - 9, // bLength - 2, // bDescriptorType - LSB(CONFIG1_DESC_SIZE), // wTotalLength - MSB(CONFIG1_DESC_SIZE), - 2, // bNumInterfaces - 1, // bConfigurationValue - 0, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - - // IAD to associate the two CDC interfaces - 0x08, // bLength - 0x0b, // bDescriptorType - 0x00, // bFirstInterface - 0x02, // bInterfaceCount - 0x02, // bFunctionClass - 0x02, // bFunctionSubClass - 0, // bFunctionProtocol - 0, // iFunction - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - 9, // bLength - 4, // bDescriptorType - 0, // bInterfaceNumber - 0, // bAlternateSetting - 1, // bNumEndpoints - 0x02, // bInterfaceClass - 0x02, // bInterfaceSubClass - 0x01, // bInterfaceProtocol - 0, // iInterface - - // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 - 5, // bFunctionLength - 0x24, // bDescriptorType - 0x00, // bDescriptorSubtype - 0x10, 0x01, // bcdCDC - - // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 - 5, // bFunctionLength - 0x24, // bDescriptorType - 0x01, // bDescriptorSubtype - 0x03, // bmCapabilities - 1, // bDataInterface - - // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 - 4, // bFunctionLength - 0x24, // bDescriptorType - 0x02, // bDescriptorSubtype - 0x06, // bmCapabilities - - // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 - 5, // bFunctionLength - 0x24, // bDescriptorType - 0x06, // bDescriptorSubtype - 0, // bMasterInterface - 1, // bSlaveInterface0 - - // 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) - LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) - MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) - 16, // bInterval - - // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 - 9, // bLength - 4, // bDescriptorType - 1, // bInterfaceNumber - 0, // 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) - LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) - MSB(CDC_MAX_PACKET_SIZE), // 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) - LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) - MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) - 0 // bInterval - }; - - if (index == 0) { - MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); - memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor)); - return _config_descriptor; - } else { - return NULL; - } -} diff --git a/drivers/source/usb/USBCDC_ECM.cpp b/drivers/source/usb/USBCDC_ECM.cpp deleted file mode 100644 index 45e97ad..0000000 --- a/drivers/source/usb/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/source/usb/USBDevice.cpp b/drivers/source/usb/USBDevice.cpp deleted file mode 100644 index 6d3cfc8..0000000 --- a/drivers/source/usb/USBDevice.cpp +++ /dev/null @@ -1,1773 +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 "USBDevice.h" -#include "USBDescriptor.h" -#include "usb_phy_api.h" -#include "mbed_assert.h" -#include "platform/mbed_error.h" - -//#define DEBUG - -/* Device status */ -#define DEVICE_STATUS_SELF_POWERED (1U<<0) -#define DEVICE_STATUS_REMOTE_WAKEUP (1U<<1) - -/* Endpoint status */ -#define ENDPOINT_STATUS_HALT (1U<<0) - -/* Standard feature selectors */ -#define DEVICE_REMOTE_WAKEUP (1) -#define ENDPOINT_HALT (0) - -/* Endpoint macros */ -#define EP_INDEXABLE(endpoint) (EP_VALID(endpoint) && !EP_CONTROL(endpoint)) -#define EP_TO_INDEX(endpoint) ((((endpoint & 0xf) << 1) | (endpoint & 0x80 ? 1 : 0)) - 2) -#define INDEX_TO_EP(index) ((usb_ep_t)((((index) >> 1) | (index & 1 ? 0x80 : 0)) + 1)) -#define EP_VALID(endpoint) (((endpoint) & ~0x8F) == 0) -#define EP_CONTROL(endpoint) (((endpoint) & 0xF) == 0) -#define EP_RX(endpoint) ((endpoint) & 0x80) - -/* Other defines */ -#define ENDPOINT_ENABLED (1 << 0) -#define ENDPOINT_STALLED (1 << 1) - -/* The maximum wMaxPacketSize for endpoint 0 */ -#if defined(MAX_PACKET_SIZE_EP0) -#undef MAX_PACKET_SIZE_EP0 -#endif -#define MAX_PACKET_SIZE_EP0 64 - -#define USB_MIN(a, b) ((a) > (b) ? (b) : (a)) - - -bool USBDevice::_request_get_descriptor() -{ - assert_locked(); - - bool success = false; -#ifdef DEBUG - printf("get descr: type: %d\r\n", DESCRIPTOR_TYPE(_transfer.setup.wValue)); -#endif - switch (DESCRIPTOR_TYPE(_transfer.setup.wValue)) { - case DEVICE_DESCRIPTOR: { - if (device_desc() != NULL) { - if ((device_desc()[0] == DEVICE_DESCRIPTOR_LENGTH) \ - && (device_desc()[1] == DEVICE_DESCRIPTOR)) { -#ifdef DEBUG - printf("device descr\r\n"); -#endif - _transfer.remaining = DEVICE_DESCRIPTOR_LENGTH; - _transfer.ptr = (uint8_t *)device_desc(); - _transfer.direction = Send; - success = true; - } - } - break; - } - case CONFIGURATION_DESCRIPTOR: { - const uint8_t idx = DESCRIPTOR_INDEX(_transfer.setup.wValue); - if (configuration_desc(idx) != NULL) { - if ((configuration_desc(idx)[0] == CONFIGURATION_DESCRIPTOR_LENGTH) \ - && (configuration_desc(idx)[1] == CONFIGURATION_DESCRIPTOR)) { -#ifdef DEBUG - printf("conf descr request\r\n"); -#endif - /* Get wTotalLength */ - _transfer.remaining = configuration_desc(idx)[2] \ - | (configuration_desc(idx)[3] << 8); - - _transfer.ptr = (uint8_t *)configuration_desc(idx); - _transfer.direction = Send; - success = true; - } - } - break; - } - case STRING_DESCRIPTOR: { -#ifdef DEBUG - printf("str descriptor\r\n"); -#endif - switch (DESCRIPTOR_INDEX(_transfer.setup.wValue)) { - case STRING_OFFSET_LANGID: -#ifdef DEBUG - printf("1\r\n"); -#endif - _transfer.remaining = string_langid_desc()[0]; - _transfer.ptr = (uint8_t *)string_langid_desc(); - _transfer.direction = Send; - success = true; - break; - case STRING_OFFSET_IMANUFACTURER: -#ifdef DEBUG - printf("2\r\n"); -#endif - _transfer.remaining = string_imanufacturer_desc()[0]; - _transfer.ptr = (uint8_t *)string_imanufacturer_desc(); - _transfer.direction = Send; - success = true; - break; - case STRING_OFFSET_IPRODUCT: -#ifdef DEBUG - printf("3\r\n"); -#endif - _transfer.remaining = string_iproduct_desc()[0]; - _transfer.ptr = (uint8_t *)string_iproduct_desc(); - _transfer.direction = Send; - success = true; - break; - case STRING_OFFSET_ISERIAL: -#ifdef DEBUG - printf("4\r\n"); -#endif - _transfer.remaining = string_iserial_desc()[0]; - _transfer.ptr = (uint8_t *)string_iserial_desc(); - _transfer.direction = Send; - success = true; - break; - case STRING_OFFSET_ICONFIGURATION: -#ifdef DEBUG - printf("5\r\n"); -#endif - _transfer.remaining = string_iconfiguration_desc()[0]; - _transfer.ptr = (uint8_t *)string_iconfiguration_desc(); - _transfer.direction = Send; - success = true; - break; - case STRING_OFFSET_IINTERFACE: -#ifdef DEBUG - printf("6\r\n"); -#endif - _transfer.remaining = string_iinterface_desc()[0]; - _transfer.ptr = (uint8_t *)string_iinterface_desc(); - _transfer.direction = Send; - success = true; - break; - } - break; - } - case INTERFACE_DESCRIPTOR: { -#ifdef DEBUG - printf("interface descr\r\n"); -#endif - break; - } - case ENDPOINT_DESCRIPTOR: { -#ifdef DEBUG - printf("endpoint descr\r\n"); -#endif - /* TODO: Support is optional, not implemented here */ - break; - } - default: { -#ifdef DEBUG - printf("ERROR\r\n"); -#endif - break; - } - } - - return success; -} - -void USBDevice::_decode_setup_packet(uint8_t *data, setup_packet_t *packet) -{ - // No lock needed - stateless function - - /* Fill in the elements of a setup_packet_t structure from raw data */ - packet->bmRequestType.dataTransferDirection = (data[0] & 0x80) >> 7; - packet->bmRequestType.Type = (data[0] & 0x60) >> 5; - packet->bmRequestType.Recipient = data[0] & 0x1f; - packet->bRequest = data[1]; - packet->wValue = (data[2] | (uint16_t)data[3] << 8); - packet->wIndex = (data[4] | (uint16_t)data[5] << 8); - packet->wLength = (data[6] | (uint16_t)data[7] << 8); -} - - -bool USBDevice::_control_out() -{ - assert_locked(); - - /* Control transfer data OUT stage */ - uint32_t packetSize; - - /* Check we should be transferring data OUT */ - if (_transfer.direction != Receive) { - /* for other platforms, count on the HAL to handle this case */ - return false; - } - - /* Read from endpoint */ - packetSize = _phy->ep0_read_result(); - - /* Check if transfer size is valid */ - if (packetSize > _transfer.remaining) { - /* Too big */ - return false; - } - - /* Update transfer */ - _transfer.ptr += packetSize; - _transfer.remaining -= packetSize; - - /* Check if transfer has completed */ - if (_transfer.remaining == 0) { - /* Transfer completed */ - _transfer.user_callback = RequestXferDone; - if (_transfer.notify) { - /* Notify class layer. */ - _transfer.notify = false; - callback_request_xfer_done(&_transfer.setup, false); - } else { - complete_request_xfer_done(true); - } - } else { - _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); - } - - return true; -} - -bool USBDevice::_control_in() -{ - assert_locked(); - - /* Control transfer data IN stage */ - uint32_t packetSize; - - - /* Check we should be transferring data IN */ - if (_transfer.direction != Send) { - return false; - } - - if (_transfer.remaining == 0) { - if (!_transfer.zlp) { - /* Status as already been sent so ignore this IN. */ - return true; - } - /* ZLP will be sent below */ - _transfer.zlp = false; - } - - packetSize = _transfer.remaining; - - if (packetSize > _max_packet_size_ep0) { - packetSize = _max_packet_size_ep0; - } - - /* Write to endpoint */ - _phy->ep0_write(_transfer.ptr, packetSize); - - /* Update transfer */ - _transfer.ptr += packetSize; - _transfer.remaining -= packetSize; - - /* Send status if all the data has been sent - * NOTE - Start the status stage immediately - * after writing the last packet. Do not wait - * for the next IN event, as this can be dropped - * if the ACK by the host is corrupted. - * - * For more info on this see section - * 8.5.3.2 of the USB2.0 specification. - */ - if ((_transfer.remaining == 0) && !_transfer.zlp) { - /* Transfer completed */ - _transfer.user_callback = RequestXferDone; - if (_transfer.notify) { - /* Notify class layer. */ - _transfer.notify = false; - callback_request_xfer_done(&_transfer.setup, false); - } else { - complete_request_xfer_done(true); - } - - - /* Completed */ - return true; - } - - - return true; -} - -void USBDevice::complete_request_xfer_done(bool success) -{ - lock(); - - MBED_ASSERT(_transfer.user_callback == RequestXferDone); - _transfer.args.status = success; - _run_later(&USBDevice::_complete_request_xfer_done); - - unlock(); -} - -void USBDevice::_complete_request_xfer_done() -{ - assert_locked(); - - bool success = _transfer.args.status; - - _transfer.user_callback = None; - if (_abort_control) { - _control_abort(); - return; - } - - if (!success) { - _phy->ep0_stall(); - return; - } - - /* Status stage */ - if (_transfer.stage == DataOut) { - _transfer.stage = Status; - _phy->ep0_write(NULL, 0); - } else if (_transfer.stage == DataIn) { - _transfer.stage = Status; - _phy->ep0_read(NULL, 0); - } -} - -bool USBDevice::_request_set_address() -{ - assert_locked(); - - /* Set the device address */ - _phy->set_address(_transfer.setup.wValue); - - if (_transfer.setup.wValue == 0) { - _change_state(Default); - } else { - _change_state(Address); - } - - return true; -} - -bool USBDevice::_request_set_configuration() -{ - assert_locked(); - - _device.configuration = _transfer.setup.wValue; - /* Set the device configuration */ - if (_device.configuration == 0) { - /* Not configured */ - _phy->unconfigure(); - _change_state(Address); - } else { - _endpoint_add_remove_allowed = true; - _transfer.user_callback = SetConfiguration; - callback_set_configuration(_device.configuration); - } - - return true; -} - -void USBDevice::complete_set_configuration(bool success) -{ - lock(); - - MBED_ASSERT(_transfer.user_callback == SetConfiguration); - _transfer.args.status = success; - _run_later(&USBDevice::_complete_set_configuration); - - unlock(); -} - -void USBDevice::_complete_set_configuration() -{ - assert_locked(); - - bool success = _transfer.args.status; - if ((_abort_control || !success) && !configured()) { - // The set configuration request was aborted or failed so - // reset any endpoints which may have been added. - memset(_endpoint_info, 0, sizeof(_endpoint_info)); - _device.configuration = 0; - _endpoint_add_remove_allowed = false; - } - - - _transfer.user_callback = None; - if (_abort_control) { - _control_abort(); - return; - } - - if (success) { - /* Valid configuration */ - _phy->configure(); - _change_state(Configured); - _control_setup_continue(); - } else { - _phy->ep0_stall(); - return; - } -} - -bool USBDevice::_request_get_configuration() -{ - assert_locked(); - - /* Send the device configuration */ - _transfer.ptr = &_device.configuration; - _transfer.remaining = sizeof(_device.configuration); - _transfer.direction = Send; - return true; -} - -bool USBDevice::_request_get_interface() -{ - assert_locked(); - - /* Return the selected alternate setting for an interface */ - - if (_device.state != Configured) { - return false; - } - - /* Send the alternate setting */ - _transfer.setup.wIndex = _current_interface; - _transfer.ptr = &_current_alternate; - _transfer.remaining = sizeof(_current_alternate); - _transfer.direction = Send; - return true; -} - -bool USBDevice::_request_set_interface() -{ - assert_locked(); - - _transfer.user_callback = SetInterface; - callback_set_interface(_transfer.setup.wIndex, _transfer.setup.wValue); - return true; -} - -void USBDevice::complete_set_interface(bool success) -{ - lock(); - - MBED_ASSERT(_transfer.user_callback == SetInterface); - _transfer.args.status = success; - _run_later(&USBDevice::_complete_set_interface); - - unlock(); -} - -void USBDevice::_complete_set_interface() -{ - assert_locked(); - - bool success = _transfer.args.status; - - _transfer.user_callback = None; - if (_abort_control) { - _control_abort(); - return; - } - - if (success) { - _current_interface = _transfer.setup.wIndex; - _current_alternate = _transfer.setup.wValue; - _control_setup_continue(); - } else { - _phy->ep0_stall(); - return; - } -} - -bool USBDevice::_request_set_feature() -{ - assert_locked(); - bool success = false; - - if (_device.state != Configured) { - /* Endpoint or interface must be zero */ - if (_transfer.setup.wIndex != 0) { - return false; - } - } - - switch (_transfer.setup.bmRequestType.Recipient) { - case DEVICE_RECIPIENT: - /* TODO: Remote wakeup feature not supported */ - break; - case ENDPOINT_RECIPIENT: - if (!EP_INDEXABLE(_transfer.setup.wIndex)) { - break; - } else if (_transfer.setup.wValue == ENDPOINT_HALT) { - endpoint_stall(_transfer.setup.wIndex); - success = true; - } - break; - default: - break; - } - - return success; -} - -bool USBDevice::_request_clear_feature() -{ - assert_locked(); - - bool success = false; - - if (_device.state != Configured) { - /* Endpoint or interface must be zero */ - if (_transfer.setup.wIndex != 0) { - return false; - } - } - - switch (_transfer.setup.bmRequestType.Recipient) { - case DEVICE_RECIPIENT: - /* TODO: Remote wakeup feature not supported */ - break; - case ENDPOINT_RECIPIENT: - if (!EP_INDEXABLE(_transfer.setup.wIndex)) { - break; - } else if (_transfer.setup.wValue == ENDPOINT_HALT) { - endpoint_unstall(_transfer.setup.wIndex); - success = true; - } - break; - default: - break; - } - - return success; -} - -bool USBDevice::_request_get_status() -{ - assert_locked(); - - static uint16_t status; - bool success = false; - - if (_device.state != Configured) { - /* Endpoint or interface must be zero */ - if (_transfer.setup.wIndex != 0) { - return false; - } - } - - switch (_transfer.setup.bmRequestType.Recipient) { - case DEVICE_RECIPIENT: - /* TODO: Currently only supports self powered devices */ - status = DEVICE_STATUS_SELF_POWERED; - success = true; - break; - case INTERFACE_RECIPIENT: - status = 0; - success = true; - break; - case ENDPOINT_RECIPIENT: - if (!EP_VALID(_transfer.setup.wIndex)) { - break; - } else if (EP_CONTROL(_transfer.setup.wIndex)) { - /* Control endpoint can't be halted */ - status = 0; - } else if (_endpoint_info[EP_TO_INDEX(_transfer.setup.wIndex & 0xFF)].flags & ENDPOINT_STALLED) { - status = ENDPOINT_STATUS_HALT; - } else { - status = 0; - } - success = true; - break; - default: - break; - } - - if (success) { - /* Send the status */ - _transfer.ptr = (uint8_t *)&status; /* Assumes little endian */ - _transfer.remaining = sizeof(status); - _transfer.direction = Send; - } - - return success; -} - -bool USBDevice::_request_setup() -{ - assert_locked(); - - bool success = false; - - /* Process standard requests */ - if (_transfer.setup.bmRequestType.Type == STANDARD_TYPE) { - switch (_transfer.setup.bRequest) { - case GET_STATUS: - success = _request_get_status(); - break; - case CLEAR_FEATURE: - success = _request_clear_feature(); - break; - case SET_FEATURE: - success = _request_set_feature(); - break; - case SET_ADDRESS: - success = _request_set_address(); - break; - case GET_DESCRIPTOR: - success = _request_get_descriptor(); - break; - case SET_DESCRIPTOR: - /* TODO: Support is optional, not implemented here */ - success = false; - break; - case GET_CONFIGURATION: - success = _request_get_configuration(); - break; - case SET_CONFIGURATION: - success = _request_set_configuration(); - break; - case GET_INTERFACE: - success = _request_get_interface(); - break; - case SET_INTERFACE: - success = _request_set_interface(); - break; - default: - break; - } - } - - return success; -} - -void USBDevice::_control_setup() -{ - assert_locked(); - - /* Control transfer setup stage */ - uint8_t buffer[MAX_PACKET_SIZE_EP0]; - - _phy->ep0_setup_read_result(buffer, _max_packet_size_ep0); - - /* Initialise control transfer state */ - _decode_setup_packet(buffer, &_transfer.setup); - _transfer.ptr = NULL; - _transfer.remaining = 0; - _transfer.direction = 0; - _transfer.zlp = false; - _transfer.notify = false; - _transfer.stage = Setup; - _transfer.user_callback = Request; - -#ifdef DEBUG - printf("dataTransferDirection: %d\r\nType: %d\r\nRecipient: %d\r\nbRequest: %d\r\nwValue: %d\r\nwIndex: %d\r\nwLength: %d\r\n", _transfer.setup.bmRequestType.dataTransferDirection, - _transfer.setup.bmRequestType.Type, - _transfer.setup.bmRequestType.Recipient, - _transfer.setup.bRequest, - _transfer.setup.wValue, - _transfer.setup.wIndex, - _transfer.setup.wLength); -#endif - - /* Class / vendor specific */ - callback_request(&_transfer.setup); -} - -void USBDevice::complete_request(RequestResult direction, uint8_t *data, uint32_t size) -{ - lock(); - - MBED_ASSERT(_transfer.user_callback == Request); - _transfer.args.request.result = direction; - _transfer.args.request.data = data; - _transfer.args.request.size = size; - _run_later(&USBDevice::_complete_request); - - unlock(); -} - -void USBDevice::_complete_request() -{ - assert_locked(); - - RequestResult direction = _transfer.args.request.result; - uint8_t *data = _transfer.args.request.data; - uint32_t size = _transfer.args.request.size; - - _transfer.user_callback = None; - if (_abort_control) { - if ((direction == Receive) || (direction == Send)) { - _transfer.user_callback = RequestXferDone; - callback_request_xfer_done(&_transfer.setup, true); - } else { - _control_abort(); - } - return; - } - - if (direction == PassThrough) { - /* Standard requests */ - if (!_request_setup()) { - _phy->ep0_stall(); - return; - } - - /* user_callback may be set by _request_setup() */ - if (_transfer.user_callback == None) { - _control_setup_continue(); - } - } else if (direction == Failure) { - _phy->ep0_stall(); - return; - } else { - _transfer.notify = true; - _transfer.remaining = size; - _transfer.ptr = data; - _transfer.direction = direction; - _control_setup_continue(); - } -} - -void USBDevice::_control_abort_start() -{ - assert_locked(); - - _setup_ready = false; - if (_transfer.user_callback == None) { - _control_abort(); - } else { - _abort_control = true; - } -} - -void USBDevice::_control_setup_continue() -{ - assert_locked(); - - /* Check transfer size and direction */ - if (_transfer.setup.wLength > 0) { - if (_transfer.setup.bmRequestType.dataTransferDirection \ - == Send) { - /* IN data stage is required */ - if (_transfer.direction != Send) { - _phy->ep0_stall(); - return; - } - - /* Transfer must be less than or equal to the size */ - /* requested by the host */ - if (_transfer.remaining > _transfer.setup.wLength) { - _transfer.remaining = _transfer.setup.wLength; - } - } else { - - /* OUT data stage is required */ - if (_transfer.direction != Receive) { - _phy->ep0_stall(); - return; - } - - /* Transfer must be equal to the size requested by the host */ - if (_transfer.remaining != _transfer.setup.wLength) { - _phy->ep0_stall(); - return; - } - } - } else { - /* No data stage; transfer size must be zero */ - if (_transfer.remaining != 0) { - _phy->ep0_stall(); - return; - } - } - - /* Data or status stage if applicable */ - if (_transfer.setup.wLength > 0) { - if (_transfer.setup.bmRequestType.dataTransferDirection \ - == Send) { - /* Check if we'll need to send a zero length packet at */ - /* the end of this transfer */ - if (_transfer.setup.wLength > _transfer.remaining) { - /* Device wishes to transfer less than host requested */ - if ((_transfer.remaining % _max_packet_size_ep0) == 0) { - /* Transfer is a multiple of EP0 max packet size */ - _transfer.zlp = true; - } - } - - /* IN stage */ - _transfer.stage = DataIn; - _control_in(); - } else { - /* OUT stage */ - _transfer.stage = DataOut; - _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); - } - } else { - /* Status stage */ - _transfer.stage = Status; - _phy->ep0_write(NULL, 0); - } -} - -void USBDevice::_control_abort() -{ - assert_locked(); - - _abort_control = false; - _transfer.stage = Status; -} - -void USBDevice::reset() -{ - assert_locked(); - - _change_state(Default); - _device.suspended = false; - _control_abort_start(); - - /* Call class / vendor specific busReset function */ - callback_reset(); -} - -void USBDevice::ep0_setup() -{ - assert_locked(); - - if (_device.state < Default) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_NOT_READY - ), - "Device state is \"Powered\" or \"Detached\"" - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - _setup_ready = true; - - /* Endpoint 0 setup event */ - if (_transfer.user_callback == None) { - _control_setup(); - } else { - /* A new setup packet has arrived so abort the - current control transfer */ - _abort_control = true; - } - -} - -void USBDevice::ep0_out() -{ - assert_locked(); - - if (_device.state < Default) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_NOT_READY - ), - "Device state is \"Powered\" or \"Detached\"" - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (_transfer.user_callback != None) { - /* EP0 OUT should not receive data if the stack is waiting - on a user callback for the buffer to fill or status */ -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_NOT_READY - ), - "The stack is waiting on a user callback for the buffer to fill or status." - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (_transfer.stage == Status) { - // No action needed on status stage - return; - } - - /* Endpoint 0 OUT data event */ - if (!_control_out()) { - /* Protocol stall; this will stall both endpoints */ - _phy->ep0_stall(); - return; - } -} - -void USBDevice::ep0_in() -{ - assert_locked(); - - if (_device.state < Default) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_NOT_READY - ), - "Device state is \"Powered\" or \"Detached\"" - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - -#ifdef DEBUG - printf("ep0_in\r\n"); -#endif - if (_transfer.stage == Status) { - // No action needed on status stage - return; - } - - /* Endpoint 0 IN data event */ - if (!_control_in()) { - /* Protocol stall; this will stall both endpoints */ - _phy->ep0_stall(); - return; - } -} - -void USBDevice::out(usb_ep_t endpoint) -{ - assert_locked(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - - MBED_ASSERT(info->pending >= 1); - info->pending -= 1; - if (info->callback) { - info->callback(); - } -} - -void USBDevice::in(usb_ep_t endpoint) -{ - assert_locked(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - - MBED_ASSERT(info->pending >= 1); - info->pending -= 1; - if (info->callback) { - info->callback(); - } -} - -void USBDevice::init() -{ - lock(); - - if (!_initialized) { - this->_phy->init(this); - _max_packet_size_ep0 = this->_phy->ep0_set_max_packet(MAX_PACKET_SIZE_EP0); - _initialized = true; - } - - unlock(); -} - -void USBDevice::deinit() -{ - lock(); - - if (_initialized) { - disconnect(); - this->_phy->deinit(); - _initialized = false; - } - - unlock(); -} - -bool USBDevice::configured() -{ - lock(); - - /* Returns true if device is in the Configured state */ - bool ret = (_device.state == Configured); - - unlock(); - return ret; -} - -void USBDevice::connect() -{ - lock(); - - /* Ensure device has been initialized */ - init(); - - /* Connect device */ - if (!_connected) { - _phy->connect(); - _connected = true; - } - - unlock(); -} - -void USBDevice::disconnect() -{ - lock(); - - /* Disconnect device */ - if (_connected) { - _phy->disconnect(); - _connected = false; - } - - /* Set initial device state */ - if (_device.state > Powered) { - _change_state(Powered); - } - //TODO - remove these? - _device.configuration = 0; - _device.suspended = false; - - unlock(); -} - -void USBDevice::sof_enable() -{ - lock(); - - _phy->sof_enable(); - - unlock(); -} - -void USBDevice::sof_disable() -{ - lock(); - - _phy->sof_disable(); - - unlock(); -} - -bool USBDevice::endpoint_add(usb_ep_t endpoint, uint32_t max_packet_size, usb_ep_type_t type, mbed::Callback callback) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return false; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (!_endpoint_add_remove_allowed) { - unlock(); - return false; - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - MBED_ASSERT(!(info->flags & ENDPOINT_ENABLED)); - MBED_ASSERT(max_packet_size <= 1024); - - bool ret = _phy->endpoint_add(endpoint, max_packet_size, type); - if (ret) { - info->callback = callback; - info->flags |= ENDPOINT_ENABLED; - info->pending = 0; - info->max_packet_size = max_packet_size; - } - - unlock(); - return ret; -} - -void USBDevice::endpoint_remove(usb_ep_t endpoint) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (!_endpoint_add_remove_allowed) { - unlock(); - return; - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - MBED_ASSERT(info->flags & ENDPOINT_ENABLED); - - if (info->pending) { - _phy->endpoint_abort(endpoint); - } - - info->callback = nullptr; - info->flags = 0; - info->pending = 0; - info->max_packet_size = 0; - - _phy->endpoint_remove(endpoint); - - unlock(); -} - -void USBDevice::endpoint_remove_all() -{ - lock(); - - for (uint32_t i = 0; i < sizeof(_endpoint_info) / sizeof(_endpoint_info[0]); i++) { - endpoint_info_t *info = _endpoint_info + i; - if (info->flags & ENDPOINT_ENABLED) { - endpoint_remove(INDEX_TO_EP(i)); - } - } - - unlock(); -} - -void USBDevice::endpoint_stall(usb_ep_t endpoint) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Invalid endpoint is being used - MBED_ASSERT(!configured()); - unlock(); - return; - } - - info->flags |= ENDPOINT_STALLED; - _phy->endpoint_stall(endpoint); - - if (info->pending) { - endpoint_abort(endpoint); - } - - unlock(); -} - -void USBDevice::endpoint_unstall(usb_ep_t endpoint) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Invalid endpoint is being used - MBED_ASSERT(!configured()); - unlock(); - return; - } - - if (info->pending) { - endpoint_abort(endpoint); - } - - info->flags &= ~ENDPOINT_STALLED; - _phy->endpoint_unstall(endpoint); - - unlock(); -} - -uint8_t *USBDevice::find_descriptor(uint8_t descriptorType, uint8_t index) -{ - /* Find a descriptor within the list of descriptors */ - /* following a configuration descriptor. */ - uint16_t wTotalLength; - uint8_t *ptr; - - if (configuration_desc(index) == NULL) { - return NULL; - } - - /* Check this is a configuration descriptor */ - if ((configuration_desc(index)[0] != CONFIGURATION_DESCRIPTOR_LENGTH) \ - || (configuration_desc(index)[1] != CONFIGURATION_DESCRIPTOR)) { - return NULL; - } - - wTotalLength = configuration_desc(index)[2] | (configuration_desc(index)[3] << 8); - - /* Check there are some more descriptors to follow */ - if (wTotalLength <= (CONFIGURATION_DESCRIPTOR_LENGTH + 2)) - /* +2 is for bLength and bDescriptorType of next descriptor */ - { - return NULL; - } - - /* Start at first descriptor after the configuration descriptor */ - ptr = &(((uint8_t *)configuration_desc(index))[CONFIGURATION_DESCRIPTOR_LENGTH]); - - do { - if (ptr[1] /* bDescriptorType */ == descriptorType) { - /* Found */ - return ptr; - } - - /* Skip to next descriptor */ - ptr += ptr[0]; /* bLength */ - } while (ptr < (configuration_desc(index) + wTotalLength)); - - /* Reached end of the descriptors - not found */ - return NULL; -} - -const usb_ep_table_t *USBDevice::endpoint_table() -{ - return _phy->endpoint_table(); -} - -void USBDevice::power(bool powered) -{ - assert_locked(); - - if (!powered && _device.state > Attached) { - _change_state(Attached); - } -} - -void USBDevice::suspend(bool suspended) -{ -} - -void USBDevice::sof(int frame_number) -{ - callback_sof(frame_number); -} - - -USBDevice::USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) -{ - this->vendor_id = vendor_id; - this->product_id = product_id; - this->product_release = product_release; - - memset(_endpoint_info, 0, sizeof(_endpoint_info)); - memset(&_transfer, 0, sizeof(_transfer)); - _transfer.user_callback = None; - - _setup_ready = false; - _abort_control = false; - - _phy = phy; - _initialized = false; - _connected = false; - _endpoint_add_remove_allowed = false; - _current_interface = 0; - _current_alternate = 0; - _locked = 0; - _post_process = NULL; - _max_packet_size_ep0 = 0; - - /* Set initial device state */ - _device.state = Powered; - _device.configuration = 0; - _device.suspended = false; -} - -USBDevice::~USBDevice() -{ - MBED_ASSERT(!_initialized); - deinit(); -} - -uint32_t USBDevice::endpoint_max_packet_size(usb_ep_t endpoint) -{ - lock(); - - uint32_t size = 0; - if (EP_CONTROL(endpoint)) { - size = _max_packet_size_ep0; - } else { - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - size = info->max_packet_size; - } - - unlock(); - return size; -} - -void USBDevice::endpoint_abort(usb_ep_t endpoint) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Assert that only valid endpoints are used when in the configured state - MBED_ASSERT(!configured()); - unlock(); - return; - } - - if (info->pending) { - _phy->endpoint_abort(endpoint); - info->pending = 0; - } - - unlock(); -} - -bool USBDevice::read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t max_size) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return false; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Assert that only valid endpoints are used when in the configured state - MBED_ASSERT(!configured()); - unlock(); - return false; - } - - if (max_size < info->max_packet_size) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_SIZE - ), - "The size of the data to read is less than the max packet size for this endpoint." - ); -#else - unlock(); - return false; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (info->pending) { - // Only allow 1 packet - unlock(); - return false; - } - - bool ret = _phy->endpoint_read(endpoint, buffer, info->max_packet_size); - if (ret) { - info->pending += 1; - } - - unlock(); - - return ret; -} - -uint32_t USBDevice::read_finish(usb_ep_t endpoint) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return 0; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Assert that only valid endpoints are used when in the configured state - MBED_ASSERT(!configured()); - unlock(); - return 0; - } - - uint32_t size = 0; - size = _phy->endpoint_read_result(endpoint); - unlock(); - return size; -} - -bool USBDevice::write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size) -{ - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable" - ); -#else - unlock(); - return false; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Assert that only valid endpoints are used when in the configured state - MBED_ASSERT(!configured()); - unlock(); - return false; - } - - if (size > info->max_packet_size) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_SIZE - ), - "Size being written is too large." - ); -#else - unlock(); - return false; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - if (info->pending) { - // Only allow 1 packet - unlock(); - return false; - } - - /* Send report */ - bool ret = _phy->endpoint_write(endpoint, buffer, size); - if (ret) { - info->transfer_size = size; - info->pending += 1; - } else { - info->transfer_size = 0; - } - - unlock(); - return ret; -} - -uint32_t USBDevice::write_finish(usb_ep_t endpoint) -{ - uint32_t ret = 0; - - lock(); - - if (!EP_INDEXABLE(endpoint)) { -#if MBED_TRAP_ERRORS_ENABLED - MBED_ERROR( - MBED_MAKE_ERROR( - MBED_MODULE_DRIVER_USB, - MBED_ERROR_CODE_INVALID_INDEX - ), - "The endpoint is not indexable." - ); -#else - unlock(); - return 0; -#endif // MBED_TRAP_ERRORS_ENABLED - } - - endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; - if (!(info->flags & ENDPOINT_ENABLED)) { - // Assert that only valid endpoints are used when in the configured state - MBED_ASSERT(!configured()); - unlock(); - return 0; - } - - ret = info->transfer_size; - - unlock(); - return ret; -} - -const uint8_t *USBDevice::device_desc() -{ - uint8_t device_descriptor_temp[] = { - DEVICE_DESCRIPTOR_LENGTH, /* bLength */ - DEVICE_DESCRIPTOR, /* bDescriptorType */ - LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */ - MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */ - 0x00, /* bDeviceClass */ - 0x00, /* bDeviceSubClass */ - 0x00, /* bDeviceprotocol */ - (uint8_t)_max_packet_size_ep0, /* bMaxPacketSize0 */ - (uint8_t)(LSB(vendor_id)), /* idVendor (LSB) */ - (uint8_t)(MSB(vendor_id)), /* idVendor (MSB) */ - (uint8_t)(LSB(product_id)), /* idProduct (LSB) */ - (uint8_t)(MSB(product_id)), /* idProduct (MSB) */ - (uint8_t)(LSB(product_release)), /* bcdDevice (LSB) */ - (uint8_t)(MSB(product_release)), /* bcdDevice (MSB) */ - 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 *USBDevice::string_langid_desc() -{ - static const uint8_t string_langid_descriptor[] = { - 0x04, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - 0x09, 0x04, /*bString Lang ID - 0x0409 - English*/ - }; - return string_langid_descriptor; -} - -const uint8_t *USBDevice::string_imanufacturer_desc() -{ - static const uint8_t string_imanufacturer_descriptor[] = { - 0x12, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - 'm', 0, 'b', 0, 'e', 0, 'd', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0, /*bString iManufacturer - mbed.org*/ - }; - return string_imanufacturer_descriptor; -} - -const uint8_t *USBDevice::string_iserial_desc() -{ - static const uint8_t string_iserial_descriptor[] = { - 0x16, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - '0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, /*bString iSerial - 0123456789*/ - }; - return string_iserial_descriptor; -} - -const uint8_t *USBDevice::string_iconfiguration_desc() -{ - static const uint8_t string_iconfiguration_descriptor[] = { - 0x06, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - '0', 0, '1', 0, /*bString iConfiguration - 01*/ - }; - return string_iconfiguration_descriptor; -} - -const uint8_t *USBDevice::string_iinterface_desc() -{ - static const uint8_t string_iinterface_descriptor[] = { - 0x08, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - 'U', 0, 'S', 0, 'B', 0, /*bString iInterface - USB*/ - }; - return string_iinterface_descriptor; -} - -const uint8_t *USBDevice::string_iproduct_desc() -{ - static const uint8_t string_iproduct_descriptor[] = { - 0x16, /*bLength*/ - STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ - 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 /*bString iProduct - USB DEVICE*/ - }; - return string_iproduct_descriptor; -} - -void USBDevice::start_process() -{ - lock(); - - _phy->process(); - - unlock(); -} - -void USBDevice::lock() -{ - core_util_critical_section_enter(); - _locked++; - MBED_ASSERT(_locked > 0); -} - -void USBDevice::unlock() -{ - if (_locked == 1) { - // Perform post processing before fully unlocking - while (_post_process != NULL) { - void (USBDevice::*call)() = _post_process; - _post_process = NULL; - (this->*call)(); - } - } - - MBED_ASSERT(_locked > 0); - _locked--; - core_util_critical_section_exit(); -} - -void USBDevice::assert_locked() -{ - MBED_ASSERT(_locked > 0); -} - -void USBDevice::_change_state(DeviceState new_state) -{ - assert_locked(); - - DeviceState old_state = _device.state; - _device.state = new_state; - - if (old_state == new_state) { - return; - } - - bool leaving_configured_state = (old_state >= Configured) && (new_state < Configured); - bool leaving_default_state = (old_state >= Default) && (new_state < Default); - - if (leaving_configured_state) { - memset(_endpoint_info, 0, sizeof(_endpoint_info)); - _device.configuration = 0; - _endpoint_add_remove_allowed = false; - } - - if (leaving_default_state) { - /* Abort any pending control transfers */ - _control_abort_start(); - } - - callback_state_change(new_state); -} - -void USBDevice::_run_later(void (USBDevice::*function)()) -{ - _post_process = function; -} diff --git a/drivers/source/usb/USBHID.cpp b/drivers/source/usb/USBHID.cpp deleted file mode 100644 index cb2cb67..0000000 --- a/drivers/source/usb/USBHID.cpp +++ /dev/null @@ -1,512 +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 "USBHID.h" -#include "EndpointResolver.h" -#include "usb_phy_api.h" - -class USBHID::AsyncSend: public AsyncOp { -public: - AsyncSend(USBHID *hid, const HID_REPORT *report): hid(hid), report(report), result(false) - { - - } - - virtual ~AsyncSend() - { - - } - - virtual bool process() - { - if (!hid->configured()) { - result = false; - return true; - } - - if (hid->send_nb(report)) { - result = true; - return true; - } - - return false; - } - - USBHID *hid; - const HID_REPORT *report; - bool result; -}; - -class USBHID::AsyncRead: public AsyncOp { -public: - AsyncRead(USBHID *hid, HID_REPORT *report): hid(hid), report(report), result(false) - { - - } - - virtual ~AsyncRead() - { - - } - - virtual bool process() - { - if (!hid->configured()) { - result = false; - return true; - } - - if (hid->read_nb(report)) { - result = true; - return true; - } - - return false; - } - - USBHID *hid; - HID_REPORT *report; - bool result; -}; - -class USBHID::AsyncWait: public AsyncOp { -public: - AsyncWait(USBHID *hid): hid(hid) - { - - } - - virtual ~AsyncWait() - { - - } - - virtual bool process() - { - if (hid->configured()) { - return true; - } - - return false; - } - - USBHID *hid; -}; - -USBHID::USBHID(bool connect_blocking, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(get_usb_phy(), vendor_id, product_id, product_release) -{ - _init(output_report_length, input_report_length); - if (connect_blocking) { - connect(); - wait_ready(); - } else { - init(); - } - -} - -USBHID::USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(phy, vendor_id, product_id, product_release) -{ - _init(output_report_length, input_report_length); -} - -USBHID::~USBHID() -{ - deinit(); -} - -void USBHID::_init(uint8_t output_report_length, uint8_t input_report_length) -{ - EndpointResolver resolver(endpoint_table()); - - resolver.endpoint_ctrl(64); - _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE); - _int_out = resolver.endpoint_out(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE); - MBED_ASSERT(resolver.valid()); - - _send_idle = true; - _read_idle = true; - _output_length = output_report_length; - _input_length = input_report_length; - reportLength = 0; - _input_report.length = 0; - _output_report.length = 0; -} - -bool USBHID::ready() -{ - return configured(); -} - -void USBHID::wait_ready() -{ - lock(); - - AsyncWait wait_op(this); - _connect_list.add(&wait_op); - - unlock(); - - wait_op.wait(NULL); -} - - -bool USBHID::send(const HID_REPORT *report) -{ - lock(); - - AsyncSend send_op(this, report); - _send_list.add(&send_op); - - unlock(); - - send_op.wait(NULL); - return send_op.result; -} - -bool USBHID::send_nb(const HID_REPORT *report) -{ - lock(); - - if (!configured()) { - unlock(); - return false; - } - - bool success = false; - if (_send_idle) { - memcpy(&_input_report, report, sizeof(_input_report)); - write_start(_int_in, _input_report.data, _input_report.length); - _send_idle = false; - success = true; - } - - unlock(); - return success; -} - -bool USBHID::read(HID_REPORT *report) -{ - lock(); - - AsyncRead read_op(this, report); - _read_list.add(&read_op); - - unlock(); - - read_op.wait(NULL); - return read_op.result; -} - - -bool USBHID::read_nb(HID_REPORT *report) -{ - lock(); - - if (!configured()) { - unlock(); - return false; - } - - bool success = false; - if (_read_idle) { - memcpy(report, &_output_report, sizeof(_output_report)); - read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE); - _read_idle = false; - success = true; - } - - unlock(); - return success; -} - -void USBHID::_send_isr() -{ - assert_locked(); - - write_finish(_int_in); - _send_idle = true; - - _send_list.process(); - if (_send_idle) { - report_tx(); - } - -} - -void USBHID::_read_isr() -{ - assert_locked(); - - _output_report.length = read_finish(_int_out); - _read_idle = true; - - _read_list.process(); - if (_read_idle) { - report_rx(); - } -} - -uint16_t USBHID::report_desc_length() -{ - report_desc(); - return reportLength; -} - - -void USBHID::callback_state_change(DeviceState new_state) -{ - if (new_state != Configured) { - if (!_send_idle) { - endpoint_abort(_int_in); - _send_idle = true; - } - if (!_read_idle) { - endpoint_abort(_int_out); - _read_idle = true; - } - } - _send_list.process(); - _read_list.process(); - _connect_list.process(); -} - -// -// Route callbacks from lower layers to class(es) -// - - -// Called in ISR context -// Called by USBDevice on Endpoint0 request -// This is used to handle extensions to standard requests -// and class specific requests -// Return true if class handles this request -void USBHID::callback_request(const setup_packet_t *setup) -{ - uint8_t *hidDescriptor; - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - // Process additional standard requests - - if ((setup->bmRequestType.Type == STANDARD_TYPE)) { - switch (setup->bRequest) { - case GET_DESCRIPTOR: - switch (DESCRIPTOR_TYPE(setup->wValue)) { - case REPORT_DESCRIPTOR: - if ((report_desc() != NULL) \ - && (report_desc_length() != 0)) { - size = report_desc_length(); - data = (uint8_t *)report_desc(); - result = Send; - } - break; - case HID_DESCRIPTOR: - // Find the HID descriptor, after the configuration descriptor - hidDescriptor = find_descriptor(HID_DESCRIPTOR); - if (hidDescriptor != NULL) { - size = HID_DESCRIPTOR_LENGTH; - data = hidDescriptor; - result = Send; - } - break; - - default: - break; - } - break; - default: - break; - } - } - - // Process class-specific requests - - if (setup->bmRequestType.Type == CLASS_TYPE) { - switch (setup->bRequest) { - case SET_REPORT: - // First byte will be used for report ID - _output_report.data[0] = setup->wValue & 0xff; - _output_report.length = setup->wLength + 1; - - size = sizeof(_output_report.data) - 1; - data = &_output_report.data[1]; - result = Send; - break; - default: - break; - } - } - - complete_request(result, data, size); -} - -void USBHID::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - (void)aborted; - complete_request_xfer_done(true); -} - - -#define DEFAULT_CONFIGURATION (1) - - -// Called in ISR context -// Set configuration. Return false if the -// configuration is not supported -void USBHID::callback_set_configuration(uint8_t configuration) -{ - if (configuration == DEFAULT_CONFIGURATION) { - complete_set_configuration(false); - } - - // Configure endpoints > 0 - endpoint_add(_int_in, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_send_isr); - endpoint_add(_int_out, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_read_isr); - - // We activate the endpoint to be able to recceive data - read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE); - _read_idle = false; - - - complete_set_configuration(true); -} - -void USBHID::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - assert_locked(); - complete_set_interface(true); -} - - -const uint8_t *USBHID::string_iinterface_desc() -{ - static const uint8_t stringIinterfaceDescriptor[] = { - 0x08, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'H', 0, 'I', 0, 'D', 0, //bString iInterface - HID - }; - return stringIinterfaceDescriptor; -} - -const uint8_t *USBHID::string_iproduct_desc() -{ - static const uint8_t stringIproductDescriptor[] = { - 0x16, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'H', 0, 'I', 0, 'D', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 //bString iProduct - HID device - }; - return stringIproductDescriptor; -} - - - -const uint8_t *USBHID::report_desc() -{ - uint8_t reportDescriptorTemp[] = { - USAGE_PAGE(2), LSB(0xFFAB), MSB(0xFFAB), - USAGE(2), LSB(0x0200), MSB(0x0200), - COLLECTION(1), 0x01, // Collection (Application) - - REPORT_SIZE(1), 0x08, // 8 bits - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0xFF, - - REPORT_COUNT(1), _input_length, - USAGE(1), 0x01, - INPUT(1), 0x02, // Data, Var, Abs - - REPORT_COUNT(1), _output_length, - USAGE(1), 0x02, - OUTPUT(1), 0x02, // Data, Var, Abs - - END_COLLECTION(0), - }; - reportLength = sizeof(reportDescriptor); - MBED_ASSERT(sizeof(reportDescriptorTemp) == sizeof(reportDescriptor)); - memcpy(reportDescriptor, reportDescriptorTemp, sizeof(reportDescriptor)); - return reportDescriptor; -} - -#define DEFAULT_CONFIGURATION (1) -#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ - + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ - + (1 * HID_DESCRIPTOR_LENGTH) \ - + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) - -const uint8_t *USBHID::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - - uint8_t configurationDescriptorTemp[] = { - CONFIGURATION_DESCRIPTOR_LENGTH, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) - MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) - 0x01, // bNumInterfaces - DEFAULT_CONFIGURATION, // bConfigurationValue - 0x00, // iConfiguration - C_RESERVED | C_SELF_POWERED, // bmAttributes - C_POWER(0), // bMaxPower - - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - HID_CLASS, // bInterfaceClass - HID_SUBCLASS_NONE, // bInterfaceSubClass - HID_PROTOCOL_NONE, // bInterfaceProtocol - 0x00, // iInterface - - HID_DESCRIPTOR_LENGTH, // bLength - HID_DESCRIPTOR, // bDescriptorType - LSB(HID_VERSION_1_11), // bcdHID (LSB) - MSB(HID_VERSION_1_11), // bcdHID (MSB) - 0x00, // bCountryCode - 0x01, // bNumDescriptors - REPORT_DESCRIPTOR, // bDescriptorType - (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) - (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - }; - MBED_ASSERT(sizeof(configurationDescriptorTemp) == sizeof(_configuration_descriptor)); - memcpy(_configuration_descriptor, configurationDescriptorTemp, sizeof(_configuration_descriptor)); - return _configuration_descriptor; -} diff --git a/drivers/source/usb/USBKeyboard.cpp b/drivers/source/usb/USBKeyboard.cpp deleted file mode 100644 index 5598b36..0000000 --- a/drivers/source/usb/USBKeyboard.cpp +++ /dev/null @@ -1,605 +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 "stdint.h" - -#include "USBKeyboard.h" -#include "usb_phy_api.h" - -#define REPORT_ID_KEYBOARD 1 -#define REPORT_ID_VOLUME 3 - - -typedef struct { - unsigned char usage; - unsigned char modifier; -} KEYMAP; - -#ifdef US_KEYBOARD -/* US keyboard (as HID standard) */ -#define KEYMAP_SIZE (152) -const KEYMAP keymap[KEYMAP_SIZE] = { - {0, 0}, /* NUL */ - {0, 0}, /* SOH */ - {0, 0}, /* STX */ - {0, 0}, /* ETX */ - {0, 0}, /* EOT */ - {0, 0}, /* ENQ */ - {0, 0}, /* ACK */ - {0, 0}, /* BEL */ - {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ - {0x2b, 0}, /* TAB */ /* Keyboard Tab */ - {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ - {0, 0}, /* VT */ - {0, 0}, /* FF */ - {0, 0}, /* CR */ - {0, 0}, /* SO */ - {0, 0}, /* SI */ - {0, 0}, /* DEL */ - {0, 0}, /* DC1 */ - {0, 0}, /* DC2 */ - {0, 0}, /* DC3 */ - {0, 0}, /* DC4 */ - {0, 0}, /* NAK */ - {0, 0}, /* SYN */ - {0, 0}, /* ETB */ - {0, 0}, /* CAN */ - {0, 0}, /* EM */ - {0, 0}, /* SUB */ - {0, 0}, /* ESC */ - {0, 0}, /* FS */ - {0, 0}, /* GS */ - {0, 0}, /* RS */ - {0, 0}, /* US */ - {0x2c, 0}, /* */ - {0x1e, KEY_SHIFT}, /* ! */ - {0x34, KEY_SHIFT}, /* " */ - {0x20, KEY_SHIFT}, /* # */ - {0x21, KEY_SHIFT}, /* $ */ - {0x22, KEY_SHIFT}, /* % */ - {0x24, KEY_SHIFT}, /* & */ - {0x34, 0}, /* ' */ - {0x26, KEY_SHIFT}, /* ( */ - {0x27, KEY_SHIFT}, /* ) */ - {0x25, KEY_SHIFT}, /* * */ - {0x2e, KEY_SHIFT}, /* + */ - {0x36, 0}, /* , */ - {0x2d, 0}, /* - */ - {0x37, 0}, /* . */ - {0x38, 0}, /* / */ - {0x27, 0}, /* 0 */ - {0x1e, 0}, /* 1 */ - {0x1f, 0}, /* 2 */ - {0x20, 0}, /* 3 */ - {0x21, 0}, /* 4 */ - {0x22, 0}, /* 5 */ - {0x23, 0}, /* 6 */ - {0x24, 0}, /* 7 */ - {0x25, 0}, /* 8 */ - {0x26, 0}, /* 9 */ - {0x33, KEY_SHIFT}, /* : */ - {0x33, 0}, /* ; */ - {0x36, KEY_SHIFT}, /* < */ - {0x2e, 0}, /* = */ - {0x37, KEY_SHIFT}, /* > */ - {0x38, KEY_SHIFT}, /* ? */ - {0x1f, KEY_SHIFT}, /* @ */ - {0x04, KEY_SHIFT}, /* A */ - {0x05, KEY_SHIFT}, /* B */ - {0x06, KEY_SHIFT}, /* C */ - {0x07, KEY_SHIFT}, /* D */ - {0x08, KEY_SHIFT}, /* E */ - {0x09, KEY_SHIFT}, /* F */ - {0x0a, KEY_SHIFT}, /* G */ - {0x0b, KEY_SHIFT}, /* H */ - {0x0c, KEY_SHIFT}, /* I */ - {0x0d, KEY_SHIFT}, /* J */ - {0x0e, KEY_SHIFT}, /* K */ - {0x0f, KEY_SHIFT}, /* L */ - {0x10, KEY_SHIFT}, /* M */ - {0x11, KEY_SHIFT}, /* N */ - {0x12, KEY_SHIFT}, /* O */ - {0x13, KEY_SHIFT}, /* P */ - {0x14, KEY_SHIFT}, /* Q */ - {0x15, KEY_SHIFT}, /* R */ - {0x16, KEY_SHIFT}, /* S */ - {0x17, KEY_SHIFT}, /* T */ - {0x18, KEY_SHIFT}, /* U */ - {0x19, KEY_SHIFT}, /* V */ - {0x1a, KEY_SHIFT}, /* W */ - {0x1b, KEY_SHIFT}, /* X */ - {0x1c, KEY_SHIFT}, /* Y */ - {0x1d, KEY_SHIFT}, /* Z */ - {0x2f, 0}, /* [ */ - {0x31, 0}, /* \ */ - {0x30, 0}, /* ] */ - {0x23, KEY_SHIFT}, /* ^ */ - {0x2d, KEY_SHIFT}, /* _ */ - {0x35, 0}, /* ` */ - {0x04, 0}, /* a */ - {0x05, 0}, /* b */ - {0x06, 0}, /* c */ - {0x07, 0}, /* d */ - {0x08, 0}, /* e */ - {0x09, 0}, /* f */ - {0x0a, 0}, /* g */ - {0x0b, 0}, /* h */ - {0x0c, 0}, /* i */ - {0x0d, 0}, /* j */ - {0x0e, 0}, /* k */ - {0x0f, 0}, /* l */ - {0x10, 0}, /* m */ - {0x11, 0}, /* n */ - {0x12, 0}, /* o */ - {0x13, 0}, /* p */ - {0x14, 0}, /* q */ - {0x15, 0}, /* r */ - {0x16, 0}, /* s */ - {0x17, 0}, /* t */ - {0x18, 0}, /* u */ - {0x19, 0}, /* v */ - {0x1a, 0}, /* w */ - {0x1b, 0}, /* x */ - {0x1c, 0}, /* y */ - {0x1d, 0}, /* z */ - {0x2f, KEY_SHIFT}, /* { */ - {0x31, KEY_SHIFT}, /* | */ - {0x30, KEY_SHIFT}, /* } */ - {0x35, KEY_SHIFT}, /* ~ */ - {0, 0}, /* DEL */ - - {0x3a, 0}, /* F1 */ - {0x3b, 0}, /* F2 */ - {0x3c, 0}, /* F3 */ - {0x3d, 0}, /* F4 */ - {0x3e, 0}, /* F5 */ - {0x3f, 0}, /* F6 */ - {0x40, 0}, /* F7 */ - {0x41, 0}, /* F8 */ - {0x42, 0}, /* F9 */ - {0x43, 0}, /* F10 */ - {0x44, 0}, /* F11 */ - {0x45, 0}, /* F12 */ - - {0x46, 0}, /* PRINT_SCREEN */ - {0x47, 0}, /* SCROLL_LOCK */ - {0x39, 0}, /* CAPS_LOCK */ - {0x53, 0}, /* NUM_LOCK */ - {0x49, 0}, /* INSERT */ - {0x4a, 0}, /* HOME */ - {0x4b, 0}, /* PAGE_UP */ - {0x4e, 0}, /* PAGE_DOWN */ - - {0x4f, 0}, /* RIGHT_ARROW */ - {0x50, 0}, /* LEFT_ARROW */ - {0x51, 0}, /* DOWN_ARROW */ - {0x52, 0}, /* UP_ARROW */ -}; - -#else -/* UK keyboard */ -#define KEYMAP_SIZE (152) -const KEYMAP keymap[KEYMAP_SIZE] = { - {0, 0}, /* NUL */ - {0, 0}, /* SOH */ - {0, 0}, /* STX */ - {0, 0}, /* ETX */ - {0, 0}, /* EOT */ - {0, 0}, /* ENQ */ - {0, 0}, /* ACK */ - {0, 0}, /* BEL */ - {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ - {0x2b, 0}, /* TAB */ /* Keyboard Tab */ - {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ - {0, 0}, /* VT */ - {0, 0}, /* FF */ - {0, 0}, /* CR */ - {0, 0}, /* SO */ - {0, 0}, /* SI */ - {0, 0}, /* DEL */ - {0, 0}, /* DC1 */ - {0, 0}, /* DC2 */ - {0, 0}, /* DC3 */ - {0, 0}, /* DC4 */ - {0, 0}, /* NAK */ - {0, 0}, /* SYN */ - {0, 0}, /* ETB */ - {0, 0}, /* CAN */ - {0, 0}, /* EM */ - {0, 0}, /* SUB */ - {0, 0}, /* ESC */ - {0, 0}, /* FS */ - {0, 0}, /* GS */ - {0, 0}, /* RS */ - {0, 0}, /* US */ - {0x2c, 0}, /* */ - {0x1e, KEY_SHIFT}, /* ! */ - {0x1f, KEY_SHIFT}, /* " */ - {0x32, 0}, /* # */ - {0x21, KEY_SHIFT}, /* $ */ - {0x22, KEY_SHIFT}, /* % */ - {0x24, KEY_SHIFT}, /* & */ - {0x34, 0}, /* ' */ - {0x26, KEY_SHIFT}, /* ( */ - {0x27, KEY_SHIFT}, /* ) */ - {0x25, KEY_SHIFT}, /* * */ - {0x2e, KEY_SHIFT}, /* + */ - {0x36, 0}, /* , */ - {0x2d, 0}, /* - */ - {0x37, 0}, /* . */ - {0x38, 0}, /* / */ - {0x27, 0}, /* 0 */ - {0x1e, 0}, /* 1 */ - {0x1f, 0}, /* 2 */ - {0x20, 0}, /* 3 */ - {0x21, 0}, /* 4 */ - {0x22, 0}, /* 5 */ - {0x23, 0}, /* 6 */ - {0x24, 0}, /* 7 */ - {0x25, 0}, /* 8 */ - {0x26, 0}, /* 9 */ - {0x33, KEY_SHIFT}, /* : */ - {0x33, 0}, /* ; */ - {0x36, KEY_SHIFT}, /* < */ - {0x2e, 0}, /* = */ - {0x37, KEY_SHIFT}, /* > */ - {0x38, KEY_SHIFT}, /* ? */ - {0x34, KEY_SHIFT}, /* @ */ - {0x04, KEY_SHIFT}, /* A */ - {0x05, KEY_SHIFT}, /* B */ - {0x06, KEY_SHIFT}, /* C */ - {0x07, KEY_SHIFT}, /* D */ - {0x08, KEY_SHIFT}, /* E */ - {0x09, KEY_SHIFT}, /* F */ - {0x0a, KEY_SHIFT}, /* G */ - {0x0b, KEY_SHIFT}, /* H */ - {0x0c, KEY_SHIFT}, /* I */ - {0x0d, KEY_SHIFT}, /* J */ - {0x0e, KEY_SHIFT}, /* K */ - {0x0f, KEY_SHIFT}, /* L */ - {0x10, KEY_SHIFT}, /* M */ - {0x11, KEY_SHIFT}, /* N */ - {0x12, KEY_SHIFT}, /* O */ - {0x13, KEY_SHIFT}, /* P */ - {0x14, KEY_SHIFT}, /* Q */ - {0x15, KEY_SHIFT}, /* R */ - {0x16, KEY_SHIFT}, /* S */ - {0x17, KEY_SHIFT}, /* T */ - {0x18, KEY_SHIFT}, /* U */ - {0x19, KEY_SHIFT}, /* V */ - {0x1a, KEY_SHIFT}, /* W */ - {0x1b, KEY_SHIFT}, /* X */ - {0x1c, KEY_SHIFT}, /* Y */ - {0x1d, KEY_SHIFT}, /* Z */ - {0x2f, 0}, /* [ */ - {0x64, 0}, /* \ */ - {0x30, 0}, /* ] */ - {0x23, KEY_SHIFT}, /* ^ */ - {0x2d, KEY_SHIFT}, /* _ */ - {0x35, 0}, /* ` */ - {0x04, 0}, /* a */ - {0x05, 0}, /* b */ - {0x06, 0}, /* c */ - {0x07, 0}, /* d */ - {0x08, 0}, /* e */ - {0x09, 0}, /* f */ - {0x0a, 0}, /* g */ - {0x0b, 0}, /* h */ - {0x0c, 0}, /* i */ - {0x0d, 0}, /* j */ - {0x0e, 0}, /* k */ - {0x0f, 0}, /* l */ - {0x10, 0}, /* m */ - {0x11, 0}, /* n */ - {0x12, 0}, /* o */ - {0x13, 0}, /* p */ - {0x14, 0}, /* q */ - {0x15, 0}, /* r */ - {0x16, 0}, /* s */ - {0x17, 0}, /* t */ - {0x18, 0}, /* u */ - {0x19, 0}, /* v */ - {0x1a, 0}, /* w */ - {0x1b, 0}, /* x */ - {0x1c, 0}, /* y */ - {0x1d, 0}, /* z */ - {0x2f, KEY_SHIFT}, /* { */ - {0x64, KEY_SHIFT}, /* | */ - {0x30, KEY_SHIFT}, /* } */ - {0x32, KEY_SHIFT}, /* ~ */ - {0, 0}, /* DEL */ - - {0x3a, 0}, /* F1 */ - {0x3b, 0}, /* F2 */ - {0x3c, 0}, /* F3 */ - {0x3d, 0}, /* F4 */ - {0x3e, 0}, /* F5 */ - {0x3f, 0}, /* F6 */ - {0x40, 0}, /* F7 */ - {0x41, 0}, /* F8 */ - {0x42, 0}, /* F9 */ - {0x43, 0}, /* F10 */ - {0x44, 0}, /* F11 */ - {0x45, 0}, /* F12 */ - - {0x46, 0}, /* PRINT_SCREEN */ - {0x47, 0}, /* SCROLL_LOCK */ - {0x39, 0}, /* CAPS_LOCK */ - {0x53, 0}, /* NUM_LOCK */ - {0x49, 0}, /* INSERT */ - {0x4a, 0}, /* HOME */ - {0x4b, 0}, /* PAGE_UP */ - {0x4e, 0}, /* PAGE_DOWN */ - - {0x4f, 0}, /* RIGHT_ARROW */ - {0x50, 0}, /* LEFT_ARROW */ - {0x51, 0}, /* DOWN_ARROW */ - {0x52, 0}, /* UP_ARROW */ -}; -#endif - - -USBKeyboard::USBKeyboard(bool connect, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) -{ - _lock_status = 0; - if (connect) { - USBDevice::connect(); - wait_ready(); - } else { - init(); - } -} - -USBKeyboard::USBKeyboard(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(phy, 0, 0, vendor_id, product_id, product_release) -{ - _lock_status = 0; - - // User or child responsible for calling connect or init -} - -USBKeyboard::~USBKeyboard() -{ - deinit(); -} - -const uint8_t *USBKeyboard::report_desc() -{ - static const uint8_t reportDescriptor[] = { - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x06, // Keyboard - COLLECTION(1), 0x01, // Application - REPORT_ID(1), REPORT_ID_KEYBOARD, - - USAGE_PAGE(1), 0x07, // Key Codes - USAGE_MINIMUM(1), 0xE0, - USAGE_MAXIMUM(1), 0xE7, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x08, - INPUT(1), 0x02, // Data, Variable, Absolute - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x08, - INPUT(1), 0x01, // Constant - - - REPORT_COUNT(1), 0x05, - REPORT_SIZE(1), 0x01, - USAGE_PAGE(1), 0x08, // LEDs - USAGE_MINIMUM(1), 0x01, - USAGE_MAXIMUM(1), 0x05, - OUTPUT(1), 0x02, // Data, Variable, Absolute - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x03, - OUTPUT(1), 0x01, // Constant - - - REPORT_COUNT(1), 0x06, - REPORT_SIZE(1), 0x08, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x65, - USAGE_PAGE(1), 0x07, // Key Codes - USAGE_MINIMUM(1), 0x00, - USAGE_MAXIMUM(1), 0x65, - INPUT(1), 0x00, // Data, Array - END_COLLECTION(0), - - // Media Control - USAGE_PAGE(1), 0x0C, - USAGE(1), 0x01, - COLLECTION(1), 0x01, - REPORT_ID(1), REPORT_ID_VOLUME, - USAGE_PAGE(1), 0x0C, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x07, - USAGE(1), 0xB5, // Next Track - USAGE(1), 0xB6, // Previous Track - USAGE(1), 0xB7, // Stop - USAGE(1), 0xCD, // Play / Pause - USAGE(1), 0xE2, // Mute - USAGE(1), 0xE9, // Volume Up - USAGE(1), 0xEA, // Volume Down - INPUT(1), 0x02, // Input (Data, Variable, Absolute) - REPORT_COUNT(1), 0x01, - INPUT(1), 0x01, - END_COLLECTION(0), - }; - reportLength = sizeof(reportDescriptor); - return reportDescriptor; -} - - -void USBKeyboard::report_rx() -{ - assert_locked(); - - HID_REPORT report; - read_nb(&report); - - // we take [1] because [0] is the report ID - _lock_status = report.data[1] & 0x07; -} - -uint8_t USBKeyboard::lock_status() -{ - return _lock_status; -} - -int USBKeyboard::_putc(int c) -{ - return key_code(c, keymap[c].modifier); -} - -bool USBKeyboard::key_code(uint8_t key, uint8_t modifier) -{ - _mutex.lock(); - - // Send a simulated keyboard keypress. Returns true if successful. - HID_REPORT report; - - report.data[0] = REPORT_ID_KEYBOARD; - report.data[1] = modifier; - report.data[2] = 0; - report.data[3] = keymap[key].usage; - report.data[4] = 0; - report.data[5] = 0; - report.data[6] = 0; - report.data[7] = 0; - report.data[8] = 0; - - report.length = 9; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - report.data[1] = 0; - report.data[3] = 0; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - _mutex.unlock(); - return true; - -} - - -bool USBKeyboard::media_control(MEDIA_KEY key) -{ - _mutex.lock(); - - HID_REPORT report; - - report.data[0] = REPORT_ID_VOLUME; - report.data[1] = (1 << key) & 0x7f; - - report.length = 2; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - report.data[0] = REPORT_ID_VOLUME; - report.data[1] = 0; - - report.length = 2; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - _mutex.unlock(); - return true; -} - -#define DEFAULT_CONFIGURATION (1) -#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ - + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ - + (1 * HID_DESCRIPTOR_LENGTH) \ - + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) - -const uint8_t *USBKeyboard::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - uint8_t configuration_descriptor_temp[] = { - CONFIGURATION_DESCRIPTOR_LENGTH, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) - MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) - 0x01, // bNumInterfaces - DEFAULT_CONFIGURATION, // bConfigurationValue - 0x00, // iConfiguration - C_RESERVED | C_SELF_POWERED, // bmAttributes - C_POWER(0), // bMaxPower - - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - HID_CLASS, // bInterfaceClass - HID_SUBCLASS_BOOT, // bInterfaceSubClass - HID_PROTOCOL_KEYBOARD, // bInterfaceProtocol - 0x00, // iInterface - - HID_DESCRIPTOR_LENGTH, // bLength - HID_DESCRIPTOR, // bDescriptorType - LSB(HID_VERSION_1_11), // bcdHID (LSB) - MSB(HID_VERSION_1_11), // bcdHID (MSB) - 0x00, // bCountryCode - 0x01, // bNumDescriptors - REPORT_DESCRIPTOR, // bDescriptorType - (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) - (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - }; - MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor)); - memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor)); - return _configuration_descriptor; -} - -int USBKeyboard::_getc() -{ - return -1; -} diff --git a/drivers/source/usb/USBMIDI.cpp b/drivers/source/usb/USBMIDI.cpp deleted file mode 100644 index 594b1b9..0000000 --- a/drivers/source/usb/USBMIDI.cpp +++ /dev/null @@ -1,399 +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 "USBMIDI.h" -#include "EndpointResolver.h" -#include "usb_phy_api.h" - -#define FLAG_WRITE_DONE (1 << 0) -#define FLAG_DISCONNECT (1 << 1) -#define FLAG_CONNECT (1 << 2) - -USBMIDI::USBMIDI(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) -{ - _init(); - - if (connect_blocking) { - USBDevice::connect(); - wait_ready(); - } else { - init(); - } -} - -USBMIDI::USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) - : USBDevice(phy, vendor_id, product_id, product_release) -{ - _init(); - - // User or child responsible for calling connect or init -} - -USBMIDI::~USBMIDI() -{ - deinit(); -} - -void USBMIDI::_init() -{ - _bulk_buf_pos = 0; - _bulk_buf_size = 0; - - _data_ready = false; - _cur_data = 0; - - EndpointResolver resolver(endpoint_table()); - - resolver.endpoint_ctrl(64); - _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MaxSize); - _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MaxSize); - MBED_ASSERT(resolver.valid()); -} - -bool USBMIDI::ready() -{ - return _flags.get() & FLAG_CONNECT ? true : false; -} - -void USBMIDI::wait_ready() -{ - _flags.wait_any(FLAG_CONNECT, osWaitForever, false); -} - -// write plain MIDIMessage that will be converted to USBMidi event packet -bool USBMIDI::write(MIDIMessage m) -{ - _write_mutex.lock(); - - bool ret = true; - // first byte keeped for retro-compatibility - for (int p = 1; p < m.length; p += 3) { - uint8_t buf[4]; - // Midi message to USBMidi event packet - buf[0] = m.data[1] >> 4; - // SysEx - if (buf[0] == 0xF) { - if ((m.length - p) > 3) { - // SysEx start or continue - buf[0] = 0x4; - } else { - switch (m.length - p) { - case 1: - // SysEx end with one byte - buf[0] = 0x5; - break; - case 2: - // SysEx end with two bytes - buf[0] = 0x6; - break; - case 3: - // SysEx end with three bytes - buf[0] = 0x7; - break; - } - } - } - buf[1] = m.data[p]; - - if (p + 1 < m.length) { - buf[2] = m.data[p + 1]; - } else { - buf[2] = 0; - } - - if (p + 2 < m.length) { - buf[3] = m.data[p + 2]; - } else { - buf[3] = 0; - } - - _flags.clear(FLAG_WRITE_DONE); - USBDevice::write_start(_bulk_in, buf, 4); - uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false); - if (flags & FLAG_DISCONNECT) { - ret = false; - break; - } - USBDevice::write_finish(_bulk_in); - } - - _write_mutex.unlock(); - return ret; -} - -bool USBMIDI::readable() -{ - lock(); - - bool ret = _data_ready; - - unlock(); - - return ret; -} - -bool USBMIDI::read(MIDIMessage *m) -{ - lock(); - - // Invalidate message - m->length = 0; - - if (!_data_ready) { - unlock(); - return false; - } - - m->from_raw(_data, _cur_data); - _cur_data = 0; - _next_message(); - - if (!_data_ready) { - read_start(_bulk_out, _bulk_buf, MaxSize); - } - - unlock(); - - return true; -} - -void USBMIDI::attach(mbed::Callback callback) -{ - lock(); - - _callback = callback; - - unlock(); -} - -void USBMIDI::callback_state_change(DeviceState new_state) -{ - assert_locked(); - - if (new_state == Configured) { - _flags.set(FLAG_CONNECT); - _flags.clear(FLAG_DISCONNECT); - } else { - _flags.set(FLAG_DISCONNECT); - _flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE); - } -} - -void USBMIDI::callback_request(const setup_packet_t *setup) -{ - assert_locked(); - - RequestResult result = PassThrough; - uint8_t *data = NULL; - uint32_t size = 0; - - complete_request(result, data, size); -} - -void USBMIDI::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) -{ - assert_locked(); - - complete_request_xfer_done(false); -} - -void USBMIDI::callback_set_configuration(uint8_t configuration) -{ - assert_locked(); - - if (configuration == DEFAULT_CONFIGURATION) { - complete_set_configuration(false); - } - - endpoint_remove_all(); - endpoint_add(_bulk_in, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_in_callback); - endpoint_add(_bulk_out, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_out_callback); - - read_start(_bulk_out, _bulk_buf, MaxSize); - - complete_set_configuration(true); -} - -void USBMIDI::callback_set_interface(uint16_t interface, uint8_t alternate) -{ - assert_locked(); - - complete_set_interface(true); -} - -const uint8_t *USBMIDI::string_iinterface_desc() -{ - static const uint8_t string_iinterface_descriptor[] = { - 0x0c, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio - }; - return string_iinterface_descriptor; -} - -const uint8_t *USBMIDI::string_iproduct_desc() -{ - static const uint8_t string_iproduct_descriptor[] = { - 0x16, //bLength - STRING_DESCRIPTOR, //bDescriptorType 0x03 - 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio - }; - return string_iproduct_descriptor; -} - -const uint8_t *USBMIDI::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - - uint8_t config_descriptor_temp[] = { - // configuration descriptor - 0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50, - - // The Audio Interface Collection - 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor - 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor - 0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors - 0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor - - // MIDI IN JACKS - 0x06, 0x24, 0x02, 0x01, 0x01, 0x00, - 0x06, 0x24, 0x02, 0x02, 0x02, 0x00, - - // MIDI OUT JACKS - 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, - 0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00, - - // OUT endpoint - Standard MS Bulk Data Endpoint Descriptor - 0x09, // bLength - 0x05, // bDescriptorType - _bulk_out, // bEndpointAddress - 0x02, // bmAttributes - 0x40, // wMaxPacketSize (LSB) - 0x00, // wMaxPacketSize (MSB) - 0x00, // bInterval (milliseconds) - 0x00, // bRefresh - 0x00, // bSynchAddress - - 0x05, 0x25, 0x01, 0x01, 0x01, - - // IN endpoint - Standard MS Bulk Data Endpoint Descriptor - 0x09, // bLength - 0x05, // bDescriptorType - _bulk_in, // bEndpointAddress - 0x02, // bmAttributes - 0x40, // wMaxPacketSize (LSB) - 0x00, // wMaxPacketSize (MSB) - 0x00, // bInterval (milliseconds) - 0x00, // bRefresh - 0x00, // bSynchAddress - - 0x05, 0x25, 0x01, 0x01, 0x03, - }; - MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); - memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp)); - return _config_descriptor; -} - -void USBMIDI::_in_callback() -{ - assert_locked(); - - _flags.set(FLAG_WRITE_DONE); -} - -void USBMIDI::_out_callback() -{ - assert_locked(); - - _bulk_buf_size = read_finish(_bulk_out); - _bulk_buf_pos = 0; - - if (_callback && _next_message()) { - _callback(); - return; - } - - read_start(_bulk_out, _bulk_buf, MaxSize); -} - -bool USBMIDI::_next_message() -{ - assert_locked(); - - bool data_ready = false; - while (_bulk_buf_pos < _bulk_buf_size) { - uint8_t data_read; - bool data_end = true; - switch (_bulk_buf[_bulk_buf_pos]) { - case 0x2: - // Two-bytes System Common Message - undefined in USBMidi 1.0 - data_read = 2; - break; - case 0x4: - // SysEx start or continue - data_end = false; - data_read = 3; - break; - case 0x5: - // Single-byte System Common Message or SysEx end with one byte - data_read = 1; - break; - case 0x6: - // SysEx end with two bytes - data_read = 2; - break; - case 0xC: - // Program change - data_read = 2; - break; - case 0xD: - // Channel pressure - data_read = 2; - break; - case 0xF: - // Single byte - data_read = 1; - break; - default: - // Others three-bytes messages - data_read = 3; - break; - } - - for (uint8_t j = 1; j < data_read + 1; j++) { - if (_cur_data < sizeof(_data)) { - _data[_cur_data] = _bulk_buf[_bulk_buf_pos + j]; - } - _cur_data++; - } - _bulk_buf_pos += 4; - - if (data_end) { - // Message is ready to be read - data_ready = true; - break; - } - } - - _data_ready = data_ready; - return data_ready; -} diff --git a/drivers/source/usb/USBMSD.cpp b/drivers/source/usb/USBMSD.cpp deleted file mode 100644 index 6845c02..0000000 --- a/drivers/source/usb/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(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, 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) -{ - bd_addr_t addr = block * _bd->get_erase_size(); - 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) -{ - bd_addr_t addr = block * _bd->get_erase_size(); - 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/source/usb/USBMouse.cpp b/drivers/source/usb/USBMouse.cpp deleted file mode 100644 index fc22522..0000000 --- a/drivers/source/usb/USBMouse.cpp +++ /dev/null @@ -1,351 +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 "USBMouse.h" -#include "ThisThread.h" -#include "usb_phy_api.h" - -using namespace std::chrono_literals; - -USBMouse::USBMouse(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) -{ - _button = 0; - _mouse_type = mouse_type; - - if (connect_blocking) { - USBDevice::connect(); - wait_ready(); - } else { - init(); - } -} - -USBMouse::USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) -{ - _button = 0; - _mouse_type = mouse_type; -} - -USBMouse::~USBMouse() -{ - deinit(); -} - -bool USBMouse::update(int16_t x, int16_t y, uint8_t button, int8_t z) -{ - bool ret; - switch (_mouse_type) { - case REL_MOUSE: - _mutex.lock(); - - while (x > 127) { - if (!mouse_send(127, 0, button, z)) { - _mutex.unlock(); - return false; - } - x = x - 127; - } - while (x < -128) { - if (!mouse_send(-128, 0, button, z)) { - _mutex.unlock(); - return false; - } - x = x + 128; - } - while (y > 127) { - if (!mouse_send(0, 127, button, z)) { - _mutex.unlock(); - return false; - } - y = y - 127; - } - while (y < -128) { - if (!mouse_send(0, -128, button, z)) { - _mutex.unlock(); - return false; - } - y = y + 128; - } - ret = mouse_send(x, y, button, z); - - _mutex.unlock(); - return ret; - case ABS_MOUSE: - _mutex.lock(); - - HID_REPORT report; - - report.data[0] = x & 0xff; - report.data[1] = (x >> 8) & 0xff; - report.data[2] = y & 0xff; - report.data[3] = (y >> 8) & 0xff; - report.data[4] = -z; - report.data[5] = button & 0x07; - - report.length = 6; - - ret = send(&report); - - _mutex.unlock(); - return ret; - default: - return false; - } -} - -bool USBMouse::mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z) -{ - _mutex.lock(); - - HID_REPORT report; - report.data[0] = buttons & 0x07; - report.data[1] = x; - report.data[2] = y; - report.data[3] = -z; // >0 to scroll down, <0 to scroll up - - report.length = 4; - - bool ret = send(&report); - - _mutex.unlock(); - return ret; -} - -bool USBMouse::move(int16_t x, int16_t y) -{ - _mutex.lock(); - - bool ret = update(x, y, _button, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouse::scroll(int8_t z) -{ - _mutex.lock(); - - bool ret = update(0, 0, _button, z); - - _mutex.unlock(); - return ret; -} - - -bool USBMouse::double_click() -{ - _mutex.lock(); - - if (!click(MOUSE_LEFT)) { - _mutex.unlock(); - return false; - } - rtos::ThisThread::sleep_for(100ms); - bool ret = click(MOUSE_LEFT); - - _mutex.unlock(); - return ret; -} - -bool USBMouse::click(uint8_t button) -{ - _mutex.lock(); - - if (!update(0, 0, button, 0)) { - _mutex.unlock(); - return false; - } - rtos::ThisThread::sleep_for(10ms); - bool ret = update(0, 0, 0, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouse::press(uint8_t button) -{ - _mutex.lock(); - - _button = button & 0x07; - bool ret = update(0, 0, _button, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouse::release(uint8_t button) -{ - _mutex.lock(); - - _button = (_button & (~button)) & 0x07; - bool ret = update(0, 0, _button, 0); - - _mutex.unlock(); - return ret; -} - - -const uint8_t *USBMouse::report_desc() -{ - - if (_mouse_type == REL_MOUSE) { - static const uint8_t report_descriptor[] = { - USAGE_PAGE(1), 0x01, // Genric Desktop - USAGE(1), 0x02, // Mouse - COLLECTION(1), 0x01, // Application - USAGE(1), 0x01, // Pointer - COLLECTION(1), 0x00, // Physical - - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x01, - USAGE_PAGE(1), 0x09, // Buttons - USAGE_MINIMUM(1), 0x1, - USAGE_MAXIMUM(1), 0x3, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - INPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x05, - INPUT(1), 0x01, - - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x08, - USAGE_PAGE(1), 0x01, - USAGE(1), 0x30, // X - USAGE(1), 0x31, // Y - USAGE(1), 0x38, // scroll - LOGICAL_MINIMUM(1), 0x81, - LOGICAL_MAXIMUM(1), 0x7f, - INPUT(1), 0x06, // Relative data - - END_COLLECTION(0), - END_COLLECTION(0), - }; - reportLength = sizeof(report_descriptor); - return report_descriptor; - } else if (_mouse_type == ABS_MOUSE) { - static const uint8_t report_descriptor[] = { - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x02, // Mouse - COLLECTION(1), 0x01, // Application - USAGE(1), 0x01, // Pointer - COLLECTION(1), 0x00, // Physical - - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x30, // X - USAGE(1), 0x31, // Y - LOGICAL_MINIMUM(1), 0x00, // 0 - LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767 - REPORT_SIZE(1), 0x10, - REPORT_COUNT(1), 0x02, - INPUT(1), 0x02, // Data, Variable, Absolute - - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x38, // scroll - LOGICAL_MINIMUM(1), 0x81, // -127 - LOGICAL_MAXIMUM(1), 0x7f, // 127 - REPORT_SIZE(1), 0x08, - REPORT_COUNT(1), 0x01, - INPUT(1), 0x06, // Data, Variable, Relative - - USAGE_PAGE(1), 0x09, // Buttons - USAGE_MINIMUM(1), 0x01, - USAGE_MAXIMUM(1), 0x03, - LOGICAL_MINIMUM(1), 0x00, // 0 - LOGICAL_MAXIMUM(1), 0x01, // 1 - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x01, - INPUT(1), 0x02, // Data, Variable, Absolute - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x05, - INPUT(1), 0x01, // Constant - - END_COLLECTION(0), - END_COLLECTION(0) - }; - reportLength = sizeof(report_descriptor); - return report_descriptor; - } - return NULL; -} - -#define DEFAULT_CONFIGURATION (1) -#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ - + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ - + (1 * HID_DESCRIPTOR_LENGTH) \ - + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) - -const uint8_t *USBMouse::configuration_desc(uint8_t index) -{ - if (index != 0) { - return NULL; - } - uint8_t configuration_descriptor_temp[] = { - CONFIGURATION_DESCRIPTOR_LENGTH, // bLength - CONFIGURATION_DESCRIPTOR, // bDescriptorType - LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) - MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) - 0x01, // bNumInterfaces - DEFAULT_CONFIGURATION, // bConfigurationValue - 0x00, // iConfiguration - C_RESERVED | C_SELF_POWERED, // bmAttributes - C_POWER(0), // bMaxPower - - INTERFACE_DESCRIPTOR_LENGTH, // bLength - INTERFACE_DESCRIPTOR, // bDescriptorType - 0x00, // bInterfaceNumber - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints - HID_CLASS, // bInterfaceClass - HID_SUBCLASS_BOOT, // bInterfaceSubClass - HID_PROTOCOL_MOUSE, // bInterfaceProtocol - 0x00, // iInterface - - HID_DESCRIPTOR_LENGTH, // bLength - HID_DESCRIPTOR, // bDescriptorType - LSB(HID_VERSION_1_11), // bcdHID (LSB) - MSB(HID_VERSION_1_11), // bcdHID (MSB) - 0x00, // bCountryCode - 0x01, // bNumDescriptors - REPORT_DESCRIPTOR, // bDescriptorType - (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) - (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_in, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - - ENDPOINT_DESCRIPTOR_LENGTH, // bLength - ENDPOINT_DESCRIPTOR, // bDescriptorType - _int_out, // bEndpointAddress - E_INTERRUPT, // bmAttributes - LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) - MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) - 1, // bInterval (milliseconds) - }; - MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor)); - memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor)); - return _configuration_descriptor; -} diff --git a/drivers/source/usb/USBMouseKeyboard.cpp b/drivers/source/usb/USBMouseKeyboard.cpp deleted file mode 100644 index f6da5c6..0000000 --- a/drivers/source/usb/USBMouseKeyboard.cpp +++ /dev/null @@ -1,832 +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 "stdint.h" -#include "USBMouseKeyboard.h" -#include "usb_phy_api.h" -#include "ThisThread.h" - -using namespace std::chrono_literals; - -typedef struct { - unsigned char usage; - unsigned char modifier; -} KEYMAP; - -#ifdef US_KEYBOARD -/* US keyboard (as HID standard) */ -#define KEYMAP_SIZE (152) -const KEYMAP keymap[KEYMAP_SIZE] = { - {0, 0}, /* NUL */ - {0, 0}, /* SOH */ - {0, 0}, /* STX */ - {0, 0}, /* ETX */ - {0, 0}, /* EOT */ - {0, 0}, /* ENQ */ - {0, 0}, /* ACK */ - {0, 0}, /* BEL */ - {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ - {0x2b, 0}, /* TAB */ /* Keyboard Tab */ - {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ - {0, 0}, /* VT */ - {0, 0}, /* FF */ - {0, 0}, /* CR */ - {0, 0}, /* SO */ - {0, 0}, /* SI */ - {0, 0}, /* DEL */ - {0, 0}, /* DC1 */ - {0, 0}, /* DC2 */ - {0, 0}, /* DC3 */ - {0, 0}, /* DC4 */ - {0, 0}, /* NAK */ - {0, 0}, /* SYN */ - {0, 0}, /* ETB */ - {0, 0}, /* CAN */ - {0, 0}, /* EM */ - {0, 0}, /* SUB */ - {0, 0}, /* ESC */ - {0, 0}, /* FS */ - {0, 0}, /* GS */ - {0, 0}, /* RS */ - {0, 0}, /* US */ - {0x2c, 0}, /* */ - {0x1e, KEY_SHIFT}, /* ! */ - {0x34, KEY_SHIFT}, /* " */ - {0x20, KEY_SHIFT}, /* # */ - {0x21, KEY_SHIFT}, /* $ */ - {0x22, KEY_SHIFT}, /* % */ - {0x24, KEY_SHIFT}, /* & */ - {0x34, 0}, /* ' */ - {0x26, KEY_SHIFT}, /* ( */ - {0x27, KEY_SHIFT}, /* ) */ - {0x25, KEY_SHIFT}, /* * */ - {0x2e, KEY_SHIFT}, /* + */ - {0x36, 0}, /* , */ - {0x2d, 0}, /* - */ - {0x37, 0}, /* . */ - {0x38, 0}, /* / */ - {0x27, 0}, /* 0 */ - {0x1e, 0}, /* 1 */ - {0x1f, 0}, /* 2 */ - {0x20, 0}, /* 3 */ - {0x21, 0}, /* 4 */ - {0x22, 0}, /* 5 */ - {0x23, 0}, /* 6 */ - {0x24, 0}, /* 7 */ - {0x25, 0}, /* 8 */ - {0x26, 0}, /* 9 */ - {0x33, KEY_SHIFT}, /* : */ - {0x33, 0}, /* ; */ - {0x36, KEY_SHIFT}, /* < */ - {0x2e, 0}, /* = */ - {0x37, KEY_SHIFT}, /* > */ - {0x38, KEY_SHIFT}, /* ? */ - {0x1f, KEY_SHIFT}, /* @ */ - {0x04, KEY_SHIFT}, /* A */ - {0x05, KEY_SHIFT}, /* B */ - {0x06, KEY_SHIFT}, /* C */ - {0x07, KEY_SHIFT}, /* D */ - {0x08, KEY_SHIFT}, /* E */ - {0x09, KEY_SHIFT}, /* F */ - {0x0a, KEY_SHIFT}, /* G */ - {0x0b, KEY_SHIFT}, /* H */ - {0x0c, KEY_SHIFT}, /* I */ - {0x0d, KEY_SHIFT}, /* J */ - {0x0e, KEY_SHIFT}, /* K */ - {0x0f, KEY_SHIFT}, /* L */ - {0x10, KEY_SHIFT}, /* M */ - {0x11, KEY_SHIFT}, /* N */ - {0x12, KEY_SHIFT}, /* O */ - {0x13, KEY_SHIFT}, /* P */ - {0x14, KEY_SHIFT}, /* Q */ - {0x15, KEY_SHIFT}, /* R */ - {0x16, KEY_SHIFT}, /* S */ - {0x17, KEY_SHIFT}, /* T */ - {0x18, KEY_SHIFT}, /* U */ - {0x19, KEY_SHIFT}, /* V */ - {0x1a, KEY_SHIFT}, /* W */ - {0x1b, KEY_SHIFT}, /* X */ - {0x1c, KEY_SHIFT}, /* Y */ - {0x1d, KEY_SHIFT}, /* Z */ - {0x2f, 0}, /* [ */ - {0x31, 0}, /* \ */ - {0x30, 0}, /* ] */ - {0x23, KEY_SHIFT}, /* ^ */ - {0x2d, KEY_SHIFT}, /* _ */ - {0x35, 0}, /* ` */ - {0x04, 0}, /* a */ - {0x05, 0}, /* b */ - {0x06, 0}, /* c */ - {0x07, 0}, /* d */ - {0x08, 0}, /* e */ - {0x09, 0}, /* f */ - {0x0a, 0}, /* g */ - {0x0b, 0}, /* h */ - {0x0c, 0}, /* i */ - {0x0d, 0}, /* j */ - {0x0e, 0}, /* k */ - {0x0f, 0}, /* l */ - {0x10, 0}, /* m */ - {0x11, 0}, /* n */ - {0x12, 0}, /* o */ - {0x13, 0}, /* p */ - {0x14, 0}, /* q */ - {0x15, 0}, /* r */ - {0x16, 0}, /* s */ - {0x17, 0}, /* t */ - {0x18, 0}, /* u */ - {0x19, 0}, /* v */ - {0x1a, 0}, /* w */ - {0x1b, 0}, /* x */ - {0x1c, 0}, /* y */ - {0x1d, 0}, /* z */ - {0x2f, KEY_SHIFT}, /* { */ - {0x31, KEY_SHIFT}, /* | */ - {0x30, KEY_SHIFT}, /* } */ - {0x35, KEY_SHIFT}, /* ~ */ - {0, 0}, /* DEL */ - - {0x3a, 0}, /* F1 */ - {0x3b, 0}, /* F2 */ - {0x3c, 0}, /* F3 */ - {0x3d, 0}, /* F4 */ - {0x3e, 0}, /* F5 */ - {0x3f, 0}, /* F6 */ - {0x40, 0}, /* F7 */ - {0x41, 0}, /* F8 */ - {0x42, 0}, /* F9 */ - {0x43, 0}, /* F10 */ - {0x44, 0}, /* F11 */ - {0x45, 0}, /* F12 */ - - {0x46, 0}, /* PRINT_SCREEN */ - {0x47, 0}, /* SCROLL_LOCK */ - {0x39, 0}, /* CAPS_LOCK */ - {0x53, 0}, /* NUM_LOCK */ - {0x49, 0}, /* INSERT */ - {0x4a, 0}, /* HOME */ - {0x4b, 0}, /* PAGE_UP */ - {0x4e, 0}, /* PAGE_DOWN */ - - {0x4f, 0}, /* RIGHT_ARROW */ - {0x50, 0}, /* LEFT_ARROW */ - {0x51, 0}, /* DOWN_ARROW */ - {0x52, 0}, /* UP_ARROW */ -}; - -#else -/* UK keyboard */ -#define KEYMAP_SIZE (152) -const KEYMAP keymap[KEYMAP_SIZE] = { - {0, 0}, /* NUL */ - {0, 0}, /* SOH */ - {0, 0}, /* STX */ - {0, 0}, /* ETX */ - {0, 0}, /* EOT */ - {0, 0}, /* ENQ */ - {0, 0}, /* ACK */ - {0, 0}, /* BEL */ - {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ - {0x2b, 0}, /* TAB */ /* Keyboard Tab */ - {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ - {0, 0}, /* VT */ - {0, 0}, /* FF */ - {0, 0}, /* CR */ - {0, 0}, /* SO */ - {0, 0}, /* SI */ - {0, 0}, /* DEL */ - {0, 0}, /* DC1 */ - {0, 0}, /* DC2 */ - {0, 0}, /* DC3 */ - {0, 0}, /* DC4 */ - {0, 0}, /* NAK */ - {0, 0}, /* SYN */ - {0, 0}, /* ETB */ - {0, 0}, /* CAN */ - {0, 0}, /* EM */ - {0, 0}, /* SUB */ - {0, 0}, /* ESC */ - {0, 0}, /* FS */ - {0, 0}, /* GS */ - {0, 0}, /* RS */ - {0, 0}, /* US */ - {0x2c, 0}, /* */ - {0x1e, KEY_SHIFT}, /* ! */ - {0x1f, KEY_SHIFT}, /* " */ - {0x32, 0}, /* # */ - {0x21, KEY_SHIFT}, /* $ */ - {0x22, KEY_SHIFT}, /* % */ - {0x24, KEY_SHIFT}, /* & */ - {0x34, 0}, /* ' */ - {0x26, KEY_SHIFT}, /* ( */ - {0x27, KEY_SHIFT}, /* ) */ - {0x25, KEY_SHIFT}, /* * */ - {0x2e, KEY_SHIFT}, /* + */ - {0x36, 0}, /* , */ - {0x2d, 0}, /* - */ - {0x37, 0}, /* . */ - {0x38, 0}, /* / */ - {0x27, 0}, /* 0 */ - {0x1e, 0}, /* 1 */ - {0x1f, 0}, /* 2 */ - {0x20, 0}, /* 3 */ - {0x21, 0}, /* 4 */ - {0x22, 0}, /* 5 */ - {0x23, 0}, /* 6 */ - {0x24, 0}, /* 7 */ - {0x25, 0}, /* 8 */ - {0x26, 0}, /* 9 */ - {0x33, KEY_SHIFT}, /* : */ - {0x33, 0}, /* ; */ - {0x36, KEY_SHIFT}, /* < */ - {0x2e, 0}, /* = */ - {0x37, KEY_SHIFT}, /* > */ - {0x38, KEY_SHIFT}, /* ? */ - {0x34, KEY_SHIFT}, /* @ */ - {0x04, KEY_SHIFT}, /* A */ - {0x05, KEY_SHIFT}, /* B */ - {0x06, KEY_SHIFT}, /* C */ - {0x07, KEY_SHIFT}, /* D */ - {0x08, KEY_SHIFT}, /* E */ - {0x09, KEY_SHIFT}, /* F */ - {0x0a, KEY_SHIFT}, /* G */ - {0x0b, KEY_SHIFT}, /* H */ - {0x0c, KEY_SHIFT}, /* I */ - {0x0d, KEY_SHIFT}, /* J */ - {0x0e, KEY_SHIFT}, /* K */ - {0x0f, KEY_SHIFT}, /* L */ - {0x10, KEY_SHIFT}, /* M */ - {0x11, KEY_SHIFT}, /* N */ - {0x12, KEY_SHIFT}, /* O */ - {0x13, KEY_SHIFT}, /* P */ - {0x14, KEY_SHIFT}, /* Q */ - {0x15, KEY_SHIFT}, /* R */ - {0x16, KEY_SHIFT}, /* S */ - {0x17, KEY_SHIFT}, /* T */ - {0x18, KEY_SHIFT}, /* U */ - {0x19, KEY_SHIFT}, /* V */ - {0x1a, KEY_SHIFT}, /* W */ - {0x1b, KEY_SHIFT}, /* X */ - {0x1c, KEY_SHIFT}, /* Y */ - {0x1d, KEY_SHIFT}, /* Z */ - {0x2f, 0}, /* [ */ - {0x64, 0}, /* \ */ - {0x30, 0}, /* ] */ - {0x23, KEY_SHIFT}, /* ^ */ - {0x2d, KEY_SHIFT}, /* _ */ - {0x35, 0}, /* ` */ - {0x04, 0}, /* a */ - {0x05, 0}, /* b */ - {0x06, 0}, /* c */ - {0x07, 0}, /* d */ - {0x08, 0}, /* e */ - {0x09, 0}, /* f */ - {0x0a, 0}, /* g */ - {0x0b, 0}, /* h */ - {0x0c, 0}, /* i */ - {0x0d, 0}, /* j */ - {0x0e, 0}, /* k */ - {0x0f, 0}, /* l */ - {0x10, 0}, /* m */ - {0x11, 0}, /* n */ - {0x12, 0}, /* o */ - {0x13, 0}, /* p */ - {0x14, 0}, /* q */ - {0x15, 0}, /* r */ - {0x16, 0}, /* s */ - {0x17, 0}, /* t */ - {0x18, 0}, /* u */ - {0x19, 0}, /* v */ - {0x1a, 0}, /* w */ - {0x1b, 0}, /* x */ - {0x1c, 0}, /* y */ - {0x1d, 0}, /* z */ - {0x2f, KEY_SHIFT}, /* { */ - {0x64, KEY_SHIFT}, /* | */ - {0x30, KEY_SHIFT}, /* } */ - {0x32, KEY_SHIFT}, /* ~ */ - {0, 0}, /* DEL */ - - {0x3a, 0}, /* F1 */ - {0x3b, 0}, /* F2 */ - {0x3c, 0}, /* F3 */ - {0x3d, 0}, /* F4 */ - {0x3e, 0}, /* F5 */ - {0x3f, 0}, /* F6 */ - {0x40, 0}, /* F7 */ - {0x41, 0}, /* F8 */ - {0x42, 0}, /* F9 */ - {0x43, 0}, /* F10 */ - {0x44, 0}, /* F11 */ - {0x45, 0}, /* F12 */ - - {0x46, 0}, /* PRINT_SCREEN */ - {0x47, 0}, /* SCROLL_LOCK */ - {0x39, 0}, /* CAPS_LOCK */ - {0x53, 0}, /* NUM_LOCK */ - {0x49, 0}, /* INSERT */ - {0x4a, 0}, /* HOME */ - {0x4b, 0}, /* PAGE_UP */ - {0x4e, 0}, /* PAGE_DOWN */ - - {0x4f, 0}, /* RIGHT_ARROW */ - {0x50, 0}, /* LEFT_ARROW */ - {0x51, 0}, /* DOWN_ARROW */ - {0x52, 0}, /* UP_ARROW */ -}; -#endif - -USBMouseKeyboard::USBMouseKeyboard(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) -{ - _lock_status = 0; - _button = 0; - _mouse_type = mouse_type; - - if (connect_blocking) { - USBDevice::connect(); - wait_ready(); - } else { - init(); - } -}; - -USBMouseKeyboard::USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBHID(phy, 0, 0, vendor_id, product_id, product_release) -{ - _lock_status = 0; - _button = 0; - _mouse_type = mouse_type; -}; - -/** - * Destroy this object - * - * Any classes which inherit from this class must call deinit - * before this destructor runs. - */ -USBMouseKeyboard::~USBMouseKeyboard() -{ - deinit(); -} - -const uint8_t *USBMouseKeyboard::report_desc() -{ - if (_mouse_type == REL_MOUSE) { - static const uint8_t reportDescriptor[] = { - // Keyboard - USAGE_PAGE(1), 0x01, - USAGE(1), 0x06, - COLLECTION(1), 0x01, - REPORT_ID(1), REPORT_ID_KEYBOARD, - USAGE_PAGE(1), 0x07, - USAGE_MINIMUM(1), 0xE0, - USAGE_MAXIMUM(1), 0xE7, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x08, - INPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x08, - INPUT(1), 0x01, - REPORT_COUNT(1), 0x05, - REPORT_SIZE(1), 0x01, - USAGE_PAGE(1), 0x08, - USAGE_MINIMUM(1), 0x01, - USAGE_MAXIMUM(1), 0x05, - OUTPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x03, - OUTPUT(1), 0x01, - REPORT_COUNT(1), 0x06, - REPORT_SIZE(1), 0x08, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(2), 0xff, 0x00, - USAGE_PAGE(1), 0x07, - USAGE_MINIMUM(1), 0x00, - USAGE_MAXIMUM(2), 0xff, 0x00, - INPUT(1), 0x00, - END_COLLECTION(0), - - // Mouse - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x02, // Mouse - COLLECTION(1), 0x01, // Application - USAGE(1), 0x01, // Pointer - COLLECTION(1), 0x00, // Physical - REPORT_ID(1), REPORT_ID_MOUSE, - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x01, - USAGE_PAGE(1), 0x09, // Buttons - USAGE_MINIMUM(1), 0x1, - USAGE_MAXIMUM(1), 0x3, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - INPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x05, - INPUT(1), 0x01, - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x08, - USAGE_PAGE(1), 0x01, - USAGE(1), 0x30, // X - USAGE(1), 0x31, // Y - USAGE(1), 0x38, // scroll - LOGICAL_MINIMUM(1), 0x81, - LOGICAL_MAXIMUM(1), 0x7f, - INPUT(1), 0x06, - END_COLLECTION(0), - END_COLLECTION(0), - - - // Media Control - USAGE_PAGE(1), 0x0C, - USAGE(1), 0x01, - COLLECTION(1), 0x01, - REPORT_ID(1), REPORT_ID_VOLUME, - USAGE_PAGE(1), 0x0C, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x07, - USAGE(1), 0xB5, // Next Track - USAGE(1), 0xB6, // Previous Track - USAGE(1), 0xB7, // Stop - USAGE(1), 0xCD, // Play / Pause - USAGE(1), 0xE2, // Mute - USAGE(1), 0xE9, // Volume Up - USAGE(1), 0xEA, // Volume Down - INPUT(1), 0x02, // Input (Data, Variable, Absolute) - REPORT_COUNT(1), 0x01, - INPUT(1), 0x01, - END_COLLECTION(0), - }; - reportLength = sizeof(reportDescriptor); - return reportDescriptor; - } else if (_mouse_type == ABS_MOUSE) { - static const uint8_t reportDescriptor[] = { - - // Keyboard - USAGE_PAGE(1), 0x01, - USAGE(1), 0x06, - COLLECTION(1), 0x01, - REPORT_ID(1), REPORT_ID_KEYBOARD, - USAGE_PAGE(1), 0x07, - USAGE_MINIMUM(1), 0xE0, - USAGE_MAXIMUM(1), 0xE7, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x08, - INPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x08, - INPUT(1), 0x01, - REPORT_COUNT(1), 0x05, - REPORT_SIZE(1), 0x01, - USAGE_PAGE(1), 0x08, - USAGE_MINIMUM(1), 0x01, - USAGE_MAXIMUM(1), 0x05, - OUTPUT(1), 0x02, - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x03, - OUTPUT(1), 0x01, - REPORT_COUNT(1), 0x06, - REPORT_SIZE(1), 0x08, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(2), 0xff, 0x00, - USAGE_PAGE(1), 0x07, - USAGE_MINIMUM(1), 0x00, - USAGE_MAXIMUM(2), 0xff, 0x00, - INPUT(1), 0x00, - END_COLLECTION(0), - - // Mouse - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x02, // Mouse - COLLECTION(1), 0x01, // Application - USAGE(1), 0x01, // Pointer - COLLECTION(1), 0x00, // Physical - REPORT_ID(1), REPORT_ID_MOUSE, - - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x30, // X - USAGE(1), 0x31, // Y - LOGICAL_MINIMUM(1), 0x00, // 0 - LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767 - REPORT_SIZE(1), 0x10, - REPORT_COUNT(1), 0x02, - INPUT(1), 0x02, // Data, Variable, Absolute - - USAGE_PAGE(1), 0x01, // Generic Desktop - USAGE(1), 0x38, // scroll - LOGICAL_MINIMUM(1), 0x81, // -127 - LOGICAL_MAXIMUM(1), 0x7f, // 127 - REPORT_SIZE(1), 0x08, - REPORT_COUNT(1), 0x01, - INPUT(1), 0x06, // Data, Variable, Relative - - USAGE_PAGE(1), 0x09, // Buttons - USAGE_MINIMUM(1), 0x01, - USAGE_MAXIMUM(1), 0x03, - LOGICAL_MINIMUM(1), 0x00, // 0 - LOGICAL_MAXIMUM(1), 0x01, // 1 - REPORT_COUNT(1), 0x03, - REPORT_SIZE(1), 0x01, - INPUT(1), 0x02, // Data, Variable, Absolute - REPORT_COUNT(1), 0x01, - REPORT_SIZE(1), 0x05, - INPUT(1), 0x01, // Constant - - END_COLLECTION(0), - END_COLLECTION(0), - - // Media Control - USAGE_PAGE(1), 0x0C, - USAGE(1), 0x01, - COLLECTION(1), 0x01, - REPORT_ID(1), REPORT_ID_VOLUME, - USAGE_PAGE(1), 0x0C, - LOGICAL_MINIMUM(1), 0x00, - LOGICAL_MAXIMUM(1), 0x01, - REPORT_SIZE(1), 0x01, - REPORT_COUNT(1), 0x07, - USAGE(1), 0xB5, // Next Track - USAGE(1), 0xB6, // Previous Track - USAGE(1), 0xB7, // Stop - USAGE(1), 0xCD, // Play / Pause - USAGE(1), 0xE2, // Mute - USAGE(1), 0xE9, // Volume Up - USAGE(1), 0xEA, // Volume Down - INPUT(1), 0x02, // Input (Data, Variable, Absolute) - REPORT_COUNT(1), 0x01, - INPUT(1), 0x01, - END_COLLECTION(0), - }; - reportLength = sizeof(reportDescriptor); - return reportDescriptor; - } - - return NULL; -} - -void USBMouseKeyboard::report_rx() -{ - assert_locked(); - - HID_REPORT report; - read_nb(&report); - - // we take [1] because [0] is the report ID - _lock_status = report.data[1] & 0x07; -} - -uint8_t USBMouseKeyboard::lock_status() -{ - return _lock_status; -} - -bool USBMouseKeyboard::update(int16_t x, int16_t y, uint8_t button, int8_t z) -{ - bool ret; - switch (_mouse_type) { - case REL_MOUSE: - _mutex.lock(); - - while (x > 127) { - if (!_mouse_send(127, 0, button, z)) { - _mutex.unlock(); - return false; - } - x = x - 127; - } - while (x < -128) { - if (!_mouse_send(-128, 0, button, z)) { - _mutex.unlock(); - return false; - } - x = x + 128; - } - while (y > 127) { - if (!_mouse_send(0, 127, button, z)) { - _mutex.unlock(); - return false; - } - y = y - 127; - } - while (y < -128) { - if (!_mouse_send(0, -128, button, z)) { - _mutex.unlock(); - return false; - } - y = y + 128; - } - ret = _mouse_send(x, y, button, z); - - _mutex.unlock(); - return ret; - case ABS_MOUSE: - _mutex.lock(); - - HID_REPORT report; - - report.data[0] = REPORT_ID_MOUSE; - report.data[1] = x & 0xff; - report.data[2] = (x >> 8) & 0xff; - report.data[3] = y & 0xff; - report.data[4] = (y >> 8) & 0xff; - report.data[5] = -z; - report.data[6] = button & 0x07; - - report.length = 7; - - ret = send(&report); - - _mutex.unlock(); - return ret; - default: - return false; - } -} - -bool USBMouseKeyboard::_mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z) -{ - _mutex.lock(); - - HID_REPORT report; - report.data[0] = REPORT_ID_MOUSE; - report.data[1] = buttons & 0x07; - report.data[2] = x; - report.data[3] = y; - report.data[4] = -z; // >0 to scroll down, <0 to scroll up - - report.length = 5; - - bool ret = send(&report); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::move(int16_t x, int16_t y) -{ - _mutex.lock(); - - bool ret = update(x, y, _button, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::scroll(int8_t z) -{ - _mutex.lock(); - - bool ret = update(0, 0, _button, z); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::doubleClick() -{ - _mutex.lock(); - - if (!click(MOUSE_LEFT)) { - _mutex.unlock(); - return false; - } - rtos::ThisThread::sleep_for(100ms); - bool ret = click(MOUSE_LEFT); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::click(uint8_t button) -{ - _mutex.lock(); - - if (!update(0, 0, button, 0)) { - _mutex.unlock(); - return false; - } - rtos::ThisThread::sleep_for(10ms); - bool ret = update(0, 0, 0, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::press(uint8_t button) -{ - _mutex.lock(); - - _button = button & 0x07; - bool ret = update(0, 0, button, 0); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::release(uint8_t button) -{ - _mutex.lock(); - - _button = (_button & (~button)) & 0x07; - bool ret = update(0, 0, _button, 0); - - _mutex.unlock(); - return ret; -} - -int USBMouseKeyboard::_putc(int c) -{ - _mutex.lock(); - - bool ret = key_code(c, keymap[c].modifier); - - _mutex.unlock(); - return ret; -} - -bool USBMouseKeyboard::key_code(uint8_t key, uint8_t modifier) -{ - // Send a simulated keyboard keypress. Returns true if successful. - _mutex.lock(); - - HID_REPORT report; - - report.data[0] = REPORT_ID_KEYBOARD; - report.data[1] = modifier; - report.data[2] = 0; - report.data[3] = keymap[key].usage; - report.data[4] = 0; - report.data[5] = 0; - report.data[6] = 0; - report.data[7] = 0; - report.data[8] = 0; - - report.length = 9; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - report.data[1] = 0; - report.data[3] = 0; - - if (!send(&report)) { - _mutex.unlock(); - return false; - } - - _mutex.unlock(); - return true; -} - - -bool USBMouseKeyboard::media_control(MEDIA_KEY key) -{ - _mutex.lock(); - - HID_REPORT report; - - report.data[0] = REPORT_ID_VOLUME; - report.data[1] = (1 << key) & 0x7f; - - report.length = 2; - - send(&report); - - report.data[0] = REPORT_ID_VOLUME; - report.data[1] = 0; - - report.length = 2; - - bool ret = send(&report); - - _mutex.unlock(); - return ret; -} - -int USBMouseKeyboard::_getc() -{ - return -1; -} diff --git a/drivers/source/usb/USBSerial.cpp b/drivers/source/usb/USBSerial.cpp deleted file mode 100644 index b0907d1..0000000 --- a/drivers/source/usb/USBSerial.cpp +++ /dev/null @@ -1,92 +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 "stdint.h" -#include "USBSerial.h" -#include "usb_phy_api.h" - - -USBSerial::USBSerial(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBCDC(get_usb_phy(), vendor_id, product_id, product_release) -{ - _settings_changed_callback = 0; - - if (connect_blocking) { - connect(); - wait_ready(); - } else { - init(); - } -} - -USBSerial::USBSerial(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): - USBCDC(phy, vendor_id, product_id, product_release) -{ - _settings_changed_callback = 0; -} - -USBSerial::~USBSerial() -{ - deinit(); -} - -int USBSerial::_putc(int c) -{ - if (send((uint8_t *)&c, 1)) { - return c; - } else { - return -1; - } -} - -int USBSerial::_getc() -{ - uint8_t c = 0; - if (receive(&c, sizeof(c))) { - return c; - } else { - return -1; - } -} - -void USBSerial::data_rx() -{ - assert_locked(); - - //call a potential handler - if (rx) { - rx.call(); - } -} - -uint8_t USBSerial::available() -{ - USBCDC::lock(); - - uint8_t size = 0; - if (!_rx_in_progress) { - size = _rx_size > 0xFF ? 0xFF : _rx_size; - } - - USBCDC::unlock(); - return size; -} - -bool USBSerial::connected() -{ - return _terminal_connected; -} diff --git a/drivers/source/usb/mbed_lib.json b/drivers/source/usb/mbed_lib.json deleted file mode 100644 index 32a28d4..0000000 --- a/drivers/source/usb/mbed_lib.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "drivers-usb" -} diff --git a/drivers/usb/include/usb/USBAudio.h b/drivers/usb/include/usb/USBAudio.h new file mode 100644 index 0000000..58a84cd --- /dev/null +++ b/drivers/usb/include/usb/USBAudio.h @@ -0,0 +1,389 @@ +/* + * 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 USBAudio_H +#define USBAudio_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" + +#include "USBDevice.h" +#include "Callback.h" +#include "OperationList.h" +#include "ByteBuffer.h" +#include "rtos/EventFlags.h" + +/** \defgroup drivers-public-api-usb USB + * \ingroup drivers-public-api + */ + +/** + * \defgroup drivers_USBAudio USBAudio class + * \ingroup drivers-public-api-usb + * @{ + */ + + +/** +* USBAudio example +* +* @code +* #include "mbed.h" +* #include "USBAudio.h" +* +* // Audio loopback example use: +* // 1. Select "Mbed Audio" as your sound device +* // 2. Play a song or audio file +* // 3. Record the output using a program such as Audacity +* +* int main() { +* +* USBAudio audio(true, 44100, 2, 44100, 2); +* +* printf("Looping audio\r\n"); +* static uint8_t buf[128]; +* while (true) { +* if (!audio.read(buf, sizeof(buf))) { +* memset(buf, 0, sizeof(buf)); +* } +* audio.write(buf, sizeof(buf)); +* } +* } +* @endcode +*/ +class USBAudio: protected USBDevice { +public: + + enum AudioEvent { + Start, + Transfer, + End + }; + + /** + * Basic constructor + * + * Construct this object optionally connecting. + * + * @note Do not use this constructor in derived classes. + * + * @param connect Call connect on initialization + * @param frequency_rx frequency in Hz (default: 48000) + * @param channel_count_rx channel number (1 or 2) (default: 1) + * @param frequency_tx frequency in Hz (default: 8000) + * @param channel_count_tx channel number (1 or 2) (default: 1) + * @param buffer_ms time audio can be buffered without overflowing in milliseconds + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBAudio(bool connect = true, uint32_t frequency_rx = 48000, uint8_t channel_count_rx = 1, uint32_t frequency_tx = 8000, uint8_t channel_count_tx = 1, uint32_t buffer_ms = 10, uint16_t vendor_id = 0x7bb8, uint16_t product_id = 0x1111, uint16_t product_release = 0x0100); + + /** + * 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 frequency_rx frequency in Hz (default: 48000) + * @param channel_count_rx channel number (1 or 2) (default: 1) + * @param frequency_tx frequency in Hz (default: 8000) + * @param channel_count_tx channel number (1 or 2) (default: 1) + * @param buffer_ms time audio can be buffered without overflowing in milliseconds + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, 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 ~USBAudio(); + + /** + * Connect USBAudio + */ + void connect(); + + /** + * Disconnect USBAudio + * + * This unblocks all calls to read_ready and write_ready. + */ + void disconnect(); + + /** + * Read audio data + * + * @param buf pointer on a buffer which will be filled with audio data + * @param size size to read + * + * @returns true if successful + */ + bool read(uint8_t *buf, uint32_t size); + + /** + * Nonblocking audio data read + * + * Read the available audio data. + * + * @param buf pointer on a buffer which will be filled with audio data + * @param size size to read + * @param actual size actually read + * @note This function is safe to call from USBAudio callbacks. + */ + void read_nb(uint8_t *buf, uint32_t size, uint32_t *actual); + + /** + * Return the number read packets dropped due to overflow + * + * @param clear Reset the overflow count back to 0 + * @return Number of packets dropped due to overflow + */ + uint32_t read_overflows(bool clear = false); + + /** + * Check if the audio read channel is open + * + * @return true if the audio read channel open, false otherwise + */ + bool read_ready(); + + /** + * Wait until the audio read channel is open + */ + void read_wait_ready(); + + /** + * Write audio data + * + * @param buf pointer to audio data to write + * @param size size to write + * + * @returns true if successful + */ + bool write(uint8_t *buf, uint32_t size); + + /** + * Nonblocking audio data write + * + * Write the available audio data. + * + * @param buf pointer to audio data to write + * @param size size to write + * @param actual actual size written + * @note This function is safe to call from USBAudio callbacks. + */ + void write_nb(uint8_t *buf, uint32_t size, uint32_t *actual); + + /** + * Return the number write packets not sent due to underflow + * + * @param clear Reset the underflow count back to 0 + * @return Number of packets that should have been + * sent but weren't due to overflow + */ + uint32_t write_underflows(bool clear = false); + + /** + * Check if the audio write channel is open + * + * @return true if the audio write channel open, false otherwise + */ + bool write_ready(); + + /** + * Wait until the audio write channel is open + */ + void write_wait_ready(); + + /** + * Get current volume between 0.0 and 1.0 + * + * @returns volume + */ + float get_volume(); + + /** Attach a Callback to update the volume + * + * @param cb Callback to attach + * + */ + void attach(mbed::Callback &cb); + + /** attach a Callback to Tx Done + * + * @param cb Callback to attach + * + */ + void attach_tx(mbed::Callback &cb); + + /** attach a Callback to Rx Done + * + * @param cb Callback to attach + * + */ + void attach_rx(mbed::Callback &cb); + +protected: + + 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); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + virtual const uint8_t *string_iproduct_desc(); + virtual const uint8_t *string_iinterface_desc(); + virtual const uint8_t *configuration_desc(uint8_t index); + +private: + + class AsyncWrite; + class AsyncRead; + + enum ChannelState { + Powerdown, + Closed, + Opened + }; + + void _init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms); + + /* + * Call to rebuild the configuration descriptor + * + * This function should be called on creation or when any + * value that is part of the configuration descriptor + * changes. + * @note This function uses ~200 bytes of stack so + * make sure your stack is big enough for it. + */ + void _build_configuration_desc(); + + void _receive_change(ChannelState new_state); + void _receive_isr(); + void _send_change(ChannelState new_state); + void _send_isr_start(); + void _send_isr_next_sync(); + void _send_isr(); + + // has connect been called + bool _connected; + + // audio volume + float _volume; + + // mute state + uint8_t _mute; + + // Volume Current Value + uint16_t _vol_cur; + + // Volume Minimum Value + uint16_t _vol_min; + + // Volume Maximum Value + uint16_t _vol_max; + + // Volume Resolution + uint16_t _vol_res; + + // callback to update volume + mbed::Callback _update_vol; + + // callback transmit Done + mbed::Callback _tx_done; + + // callback receive Done + mbed::Callback _rx_done; + + // Number of times data was dropped due to an overflow + uint32_t _rx_overflow; + + // Number of times data was not sent due to an underflow + uint32_t _tx_underflow; + + // frequency in Hz + uint32_t _tx_freq; + uint32_t _rx_freq; + + // mono, stereo,... + uint8_t _rx_channel_count; + uint8_t _tx_channel_count; + + bool _tx_idle; + uint16_t _tx_frame_fract; + uint16_t _tx_whole_frames_per_xfer; + uint16_t _tx_fract_frames_per_xfer; + + // size of the maximum packet for the isochronous endpoint + uint16_t _tx_packet_size_max; + uint16_t _rx_packet_size_max; + + // Buffer used for the isochronous transfer + uint8_t *_tx_packet_buf; + uint8_t *_rx_packet_buf; + + // Holding buffer + ByteBuffer _tx_queue; + ByteBuffer _rx_queue; + + // State of the audio channels + ChannelState _tx_state; + ChannelState _rx_state; + + + // sample - a single PCM audio sample + // frame - a group of samples from each channel + // packet - a group of frames sent over USB in one transfer + + // Blocking primitives + OperationList _write_list; + OperationList _read_list; + rtos::EventFlags _flags; + + // endpoint numbers + usb_ep_t _episo_out; // rx endpoint + usb_ep_t _episo_in; // tx endpoint + + // channel config in the configuration descriptor: master, left, right + uint16_t _channel_config_rx; + uint16_t _channel_config_tx; + + // configuration descriptor + uint8_t _config_descriptor[183]; + + // buffer for control requests + uint8_t _control_receive[2]; + +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBCDC.h b/drivers/usb/include/usb/USBCDC.h new file mode 100644 index 0000000..777b352 --- /dev/null +++ b/drivers/usb/include/usb/USBCDC.h @@ -0,0 +1,237 @@ +/* + * 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_H +#define USBCDC_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" + +#include "USBDevice.h" +#include "OperationList.h" + +class AsyncOp; + +/** + * \defgroup drivers_USBCDC USBCDC class + * \ingroup drivers-public-api-usb + * @{ + */ + +#define CDC_MAX_PACKET_SIZE 64 + +class USBCDC: 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(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, 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(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(); + + /** + * Check if this class is ready + * + * @return true if a terminal is connected, false otherwise + */ + bool ready(); + + /** + * Block until the terminal is connected + */ + 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); + + /** + * Send what there is room for + * + * @param buffer data to send + * @param size maximum number of bytes to send + * @param actual a pointer to where to store the number of bytes sent + * @param now true to start data transmission, false to wait + */ + void send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now = true); + + /* + * Read a buffer from a certain endpoint. Warning: blocking + * + * Blocks until at least one byte of data has been read (actual != NULL) or + * until the full size has been read (actual == NULL). + * + * @param buffer buffer where will be stored bytes + * @param size the maximum number of bytes to read + * @param actual A pointer to where to store the number of bytes actually read + * or NULL to read the full size + * @returns true if successful false if interrupted due to a state change + */ + bool receive(uint8_t *buffer, uint32_t size, uint32_t *actual = NULL); + + /** + * 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); + +protected: + /* + * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @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 interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get configuration descriptor + * + * @param index descriptor index + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + + /* + * Called by USBCallback_requestCompleted when CDC line coding is changed + * Warning: Called in ISR + * + * @param baud The baud rate + * @param bits The number of bits in a word (5-8) + * @param parity The parity + * @param stop The number of stop bits (1 or 2) + */ + virtual void line_coding_changed(int baud, int bits, int parity, int stop) {}; + + /* + * Called when there is data that can be read + */ + virtual void data_rx() {} + + /* + * Called when there is space in the TX buffer + */ + virtual void data_tx() {} + +protected: + + class AsyncWrite; + class AsyncRead; + class AsyncWait; + + virtual void callback_reset(); + 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); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + void _init(); + + void _change_terminal_connected(bool connected); + + void _send_isr_start(); + void _send_isr(); + + void _receive_isr_start(); + void _receive_isr(); + + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + usb_ep_t _int_in; + + uint8_t _cdc_line_coding[7]; + uint8_t _cdc_new_line_coding[7]; + uint8_t _config_descriptor[75]; + + OperationList _connected_list; + bool _terminal_connected; + + OperationList _tx_list; + bool _tx_in_progress; + uint8_t _tx_buffer[CDC_MAX_PACKET_SIZE]; + uint8_t *_tx_buf; + uint32_t _tx_size; + + OperationList _rx_list; + bool _rx_in_progress; + uint8_t _rx_buffer[CDC_MAX_PACKET_SIZE]; + uint8_t *_rx_buf; + uint32_t _rx_size; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBCDC_ECM.h b/drivers/usb/include/usb/USBCDC_ECM.h new file mode 100644 index 0000000..70ddb9a --- /dev/null +++ b/drivers/usb/include/usb/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 "Mutex.h" +#include "EventFlags.h" +#include "events/EventQueue.h" +#include "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/USBHID.h b/drivers/usb/include/usb/USBHID.h new file mode 100644 index 0000000..70b1b3f --- /dev/null +++ b/drivers/usb/include/usb/USBHID.h @@ -0,0 +1,277 @@ +/* + * 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 USB_HID_H +#define USB_HID_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice.h" + +#include "USBHID_Types.h" +#include "OperationList.h" + +/** + * \defgroup drivers_USBHID USBHID class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** + * USBHID example + * @code + * #include "mbed.h" + * #include "USBHID.h" + * + * USBHID hid; + * HID_REPORT recv; + * BusOut leds(LED1,LED2,LED3,LED4); + * + * int main(void) { + * while (1) { + * hid.read(&recv); + * leds = recv.data[0]; + * } + * } + * @endcode + */ + +class USBHID: 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 output_report_length Maximum length of a sent report (up to 64 bytes) + * @param input_report_length Maximum length of a received report (up to 64 bytes) + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBHID(bool connect_blocking = true, uint8_t output_report_length = 64, uint8_t input_report_length = 64, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0006, 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 output_report_length Maximum length of a sent report (up to 64 bytes) + * @param input_report_length Maximum length of a received report (up to 64 bytes) + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, 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 ~USBHID(); + + /** + * Check if this class is ready + * + * @return true if the device is in the configured state + */ + bool ready(); + + /** + * Block until this HID device is in the configured state + */ + void wait_ready(); + + /** + * Send a Report. warning: blocking + * + * @param report Report which will be sent (a report is defined by all data and the length) + * @returns true if successful + */ + bool send(const HID_REPORT *report); + + + /** + * Send a Report. warning: non blocking + * + * @param report Report which will be sent (a report is defined by all data and the length) + * @returns true if successful + */ + bool send_nb(const HID_REPORT *report); + + /** + * Read a report: blocking + * + * @param report pointer to the report to fill + * @returns true if successful + */ + bool read(HID_REPORT *report); + + /** + * Read a report: non blocking + * + * @param report pointer to the report to fill + * @returns true if successful + */ + bool read_nb(HID_REPORT *report); + +protected: + uint16_t reportLength; + uint8_t reportDescriptor[27]; + + /* + * Get the Report descriptor + * + * @returns pointer to the report descriptor + */ + virtual const uint8_t *report_desc(); + + /* + * Get the length of the report descriptor + * + * @returns the length of the report descriptor + */ + virtual uint16_t report_desc_length(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + + + /* + * HID Report received by SET_REPORT request. Warning: Called in ISR context + * First byte of data will be the report ID + * + * @param report Data and length received + */ + virtual void HID_callbackSetReport(HID_REPORT *report) {}; + + /** + * 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 + */ + virtual void callback_request(const setup_packet_t *setup); + + /* + * 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); + + /* + * Called when there is a hid report that can be read + */ + virtual void report_rx() {} + + /* + * Called when there is space to send a hid report + */ + virtual void report_tx() {} + +protected: + usb_ep_t _int_in; + usb_ep_t _int_out; + +private: + void _init(uint8_t output_report_length, uint8_t input_report_length); + void _send_isr(); + void _read_isr(); + + class AsyncSend; + class AsyncRead; + class AsyncWait; + + OperationList _connect_list; + OperationList _send_list; + bool _send_idle; + OperationList _read_list; + bool _read_idle; + + uint8_t _configuration_descriptor[41]; + HID_REPORT _input_report; + HID_REPORT _output_report; + uint8_t _output_length; + uint8_t _input_length; + + +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBKeyboard.h b/drivers/usb/include/usb/USBKeyboard.h new file mode 100644 index 0000000..d9f6d0b --- /dev/null +++ b/drivers/usb/include/usb/USBKeyboard.h @@ -0,0 +1,220 @@ +/* + * 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 USBKEYBOARD_H +#define USBKEYBOARD_H + +#include "USBHID.h" +#include "platform/Stream.h" +#include "PlatformMutex.h" + +/* Modifiers, left keys then right keys. */ +enum MODIFIER_KEY { + KEY_CTRL = 0x01, + KEY_SHIFT = 0x02, + KEY_ALT = 0x04, + KEY_LOGO = 0x08, + KEY_RCTRL = 0x10, + KEY_RSHIFT = 0x20, + KEY_RALT = 0x40, + KEY_RLOGO = 0x80, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +/** + * \defgroup drivers_USBKeyboard USBKeyboard class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** + * USBKeyboard example + * @code + * + * #include "mbed.h" + * #include "USBKeyboard.h" + * + * USBKeyboard key; + * + * int main(void) + * { + * while (1) { + * key.printf("Hello World\r\n"); + * ThisThread::sleep_for(1000); + * } + * } + * + * @endcode + * + * @note Synchronization level: Thread safe + */ +class USBKeyboard: public USBHID, public mbed::Stream { +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 + */ + USBKeyboard(bool connect_blocking = true, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, 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 + */ + USBKeyboard(USBPhy *phy, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, uint16_t product_release = 0x0001); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ + virtual ~USBKeyboard(); + + /** + * To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key + * + * @code + * //To send CTRL + s (save) + * keyboard.key_code('s', KEY_CTRL); + * @endcode + * + * @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0) + * @param key character to send + * @returns true if there is no error, false otherwise + */ + bool key_code(uint8_t key, uint8_t modifier = 0); + + /** + * Send a character + * + * @param c character to be sent + * @returns true if there is no error, false otherwise + */ + virtual int _putc(int c); + + /** + * Control media keys + * + * @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN) + * @returns true if there is no error, false otherwise + */ + bool media_control(MEDIA_KEY key); + + /* + * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the report descriptor + */ + virtual const uint8_t *report_desc(); + + /* + * Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys + */ + virtual void report_rx(); + + /** + * Read status of lock keys. Useful to switch-on/off LEDs according to key pressed. Only the first three bits of the result is important: + * - First bit: NUM_LOCK + * - Second bit: CAPS_LOCK + * - Third bit: SCROLL_LOCK + * + * @returns status of lock keys + */ + uint8_t lock_status(); + +protected: + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + +private: + + //dummy otherwise it doesn't compile (we must define all methods of an abstract class) + virtual int _getc(); + + uint8_t _configuration_descriptor[41]; + uint8_t _lock_status; + PlatformMutex _mutex; + +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBMIDI.h b/drivers/usb/include/usb/USBMIDI.h new file mode 100644 index 0000000..3d7582b --- /dev/null +++ b/drivers/usb/include/usb/USBMIDI.h @@ -0,0 +1,193 @@ +/* + * 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 USBMIDI_H +#define USBMIDI_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" + +#include "USBDevice.h" +#include "MIDIMessage.h" +#include "EventFlags.h" +#include "Mutex.h" +#include "Callback.h" + +#define DEFAULT_CONFIGURATION (1) + +/** + * \defgroup drivers_USBMIDI USBMIDI class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** +* USBMIDI example +* +* @code +* #include "mbed.h" +* #include "USBMIDI.h" +* +* USBMIDI midi; +* +* int main() { +* while (1) { +* for(int i=48; i<83; i++) { // send some messages! +* midi.write(MIDIMessage::NoteOn(i)); +* ThisThread::sleep_for(250); +* midi.write(MIDIMessage::NoteOff(i)); +* ThisThread::sleep_for(500); +* } +* } +* } +* @endcode +*/ +class USBMIDI: 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 + */ + USBMIDI(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 + */ + USBMIDI(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 ~USBMIDI(); + + /** + * 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 MIDIMessage + * + * @param m The MIDIMessage to send + * @return true if the message was sent, false otherwise + */ + bool write(MIDIMessage m); + + /** + * Check if a message can be read + * + * @return true if a packet can be read false otherwise + * @note USBMIDI::attach must be called to enable the receiver + */ + bool readable(); + + /** + * Read a message + * + * @param m The MIDIMessage to fill + * @return true if a message was read, false otherwise + */ + bool read(MIDIMessage *m); + + /** + * Attach a callback for when a MIDIEvent is received + * + * @param callback code to call when a packet is received + */ + void attach(mbed::Callback callback); + + +protected: + + 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); + + virtual void callback_set_configuration(uint8_t configuration); + + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + virtual const uint8_t *string_iproduct_desc(); + + virtual const uint8_t *string_iinterface_desc(); + + virtual const uint8_t *configuration_desc(uint8_t index); + +private: + static const uint32_t MaxSize = 64; + + uint8_t _bulk_buf[MaxSize]; + uint32_t _bulk_buf_pos; + uint32_t _bulk_buf_size; + + bool _data_ready; + uint8_t _data[MAX_MIDI_MESSAGE_SIZE + 1]; + uint32_t _cur_data; + + rtos::EventFlags _flags; + rtos::Mutex _write_mutex; + + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + uint8_t _config_descriptor[0x65]; + + mbed::Callback _callback; + + void _init(); + void _in_callback(); + void _out_callback(); + bool _next_message(); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBMSD.h b/drivers/usb/include/usb/USBMSD.h new file mode 100644 index 0000000..f26e07c --- /dev/null +++ b/drivers/usb/include/usb/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 "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(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, 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; + + 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/USBMouse.h b/drivers/usb/include/usb/USBMouse.h new file mode 100644 index 0000000..f74e826 --- /dev/null +++ b/drivers/usb/include/usb/USBMouse.h @@ -0,0 +1,243 @@ +/* + * 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 USBMOUSE_H +#define USBMOUSE_H + +#include "USBHID.h" +#include "PlatformMutex.h" + +#define REPORT_ID_MOUSE 2 + +/* Common usage */ + +enum MOUSE_BUTTON { + MOUSE_LEFT = 1, + MOUSE_RIGHT = 2, + MOUSE_MIDDLE = 4, +}; + +/* X and Y limits */ +/* These values do not directly map to screen pixels */ +/* Zero may be interpreted as meaning 'no movement' */ +#define X_MIN_ABS (1) /*!< Minimum value on x-axis */ +#define Y_MIN_ABS (1) /*!< Minimum value on y-axis */ +#define X_MAX_ABS (0x7fff) /*!< Maximum value on x-axis */ +#define Y_MAX_ABS (0x7fff) /*!< Maximum value on y-axis */ + +#define X_MIN_REL (-127) /*!< The maximum value that we can move to the left on the x-axis */ +#define Y_MIN_REL (-127) /*!< The maximum value that we can move up on the y-axis */ +#define X_MAX_REL (127) /*!< The maximum value that we can move to the right on the x-axis */ +#define Y_MAX_REL (127) /*!< The maximum value that we can move down on the y-axis */ + +enum MOUSE_TYPE { + ABS_MOUSE, + REL_MOUSE, +}; + +/** + * \defgroup drivers_USBMouse USBMouse class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** + * + * USBMouse example + * @code + * #include "mbed.h" + * #include "USBMouse.h" + * + * USBMouse mouse; + * + * int main(void) + * { + * while (1) + * { + * mouse.move(20, 0); + * ThisThread::sleep_for(500); + * } + * } + * + * @endcode + * + * + * @code + * #include "mbed.h" + * #include "USBMouse.h" + * #include + * + * USBMouse mouse(true, ABS_MOUSE); + * + * int main(void) + * { + * uint16_t x_center = (X_MAX_ABS - X_MIN_ABS)/2; + * uint16_t y_center = (Y_MAX_ABS - Y_MIN_ABS)/2; + * uint16_t x_screen = 0; + * uint16_t y_screen = 0; + * + * uint32_t x_origin = x_center; + * uint32_t y_origin = y_center; + * uint32_t radius = 5000; + * uint32_t angle = 0; + * + * while (1) + * { + * x_screen = x_origin + cos((double)angle*3.14/180.0)*radius; + * y_screen = y_origin + sin((double)angle*3.14/180.0)*radius; + * + * mouse.move(x_screen, y_screen); + * angle += 3; + * ThisThread::sleep_for(10); + * } + * } + * + * @endcode + * + * @note Synchronization level: Thread safe + */ +class USBMouse: public USBHID { +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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBMouse(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, 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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your product_release + */ + USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, uint16_t product_release = 0x0001); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ + virtual ~USBMouse(); + + /** + * Write a state of the mouse + * + * @param x x-axis position + * @param y y-axis position + * @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE) + * @param z wheel state (>0 to scroll down, <0 to scroll up) + * @returns true if there is no error, false otherwise + */ + bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z); + + /** + * Move the cursor to (x, y) + * + * @param x x-axis position + * @param y y-axis position + * @returns true if there is no error, false otherwise + */ + bool move(int16_t x, int16_t y); + + /** + * Press one or several buttons + * + * @param button button state (ex: press(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool press(uint8_t button); + + /** + * Release one or several buttons + * + * @param button button state (ex: release(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool release(uint8_t button); + + /** + * Double click (MOUSE_LEFT) + * + * @returns true if there is no error, false otherwise + */ + bool double_click(); + + /** + * Click + * + * @param button state of the buttons ( ex: clic(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool click(uint8_t button); + + /** + * Scrolling + * + * @param z value of the wheel (>0 to go down, <0 to go up) + * @returns true if there is no error, false otherwise + */ + bool scroll(int8_t z); + + /* + * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the report descriptor + */ + virtual const uint8_t *report_desc(); + +protected: + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index); + +private: + MOUSE_TYPE _mouse_type; + uint8_t _button; + uint8_t _configuration_descriptor[41]; + PlatformMutex _mutex; + + bool mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBMouseKeyboard.h b/drivers/usb/include/usb/USBMouseKeyboard.h new file mode 100644 index 0000000..54144ba --- /dev/null +++ b/drivers/usb/include/usb/USBMouseKeyboard.h @@ -0,0 +1,254 @@ +/* + * 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 USBMOUSEKEYBOARD_H +#define USBMOUSEKEYBOARD_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_MOUSE 2 +#define REPORT_ID_VOLUME 3 + +#include "USBMouse.h" +#include "USBKeyboard.h" +#include "platform/Stream.h" +#include "USBHID.h" +#include "PlatformMutex.h" + +/** + * \defgroup drivers_USBMouseKeyboard USBMouseKeyboard class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** + * USBMouseKeyboard example + * @code + * + * #include "mbed.h" + * #include "USBMouseKeyboard.h" + * + * USBMouseKeyboard key_mouse; + * + * int main(void) + * { + * while(1) + * { + * key_mouse.move(20, 0); + * key_mouse.printf("Hello From MBED\r\n"); + * ThisThread::sleep_for(1000); + * } + * } + * @endcode + * + * + * @code + * + * #include "mbed.h" + * #include "USBMouseKeyboard.h" + * + * USBMouseKeyboard key_mouse(ABS_MOUSE); + * + * int main(void) + * { + * while(1) + * { + * key_mouse.move(X_MAX_ABS/2, Y_MAX_ABS/2); + * key_mouse.printf("Hello from MBED\r\n"); + * ThisThread::sleep_for(1000); + * } + * } + * @endcode + * + * @note Synchronization level: Thread safe + */ +class USBMouseKeyboard: public USBHID, public mbed::Stream { +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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) + * @param vendor_id Your vendor_id (default: 0x1234) + * @param product_id Your product_id (default: 0x0001) + * @param product_release Your preoduct_release (default: 0x0001) + * + */ + USBMouseKeyboard(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, 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 mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE) + * @param vendor_id Your vendor_id (default: 0x1234) + * @param product_id Your product_id (default: 0x0001) + * @param product_release Your preoduct_release (default: 0x0001) + * + */ + USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, uint16_t product_release = 0x0001); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ + virtual ~USBMouseKeyboard(); + + /** + * Write a state of the mouse + * + * @param x x-axis position + * @param y y-axis position + * @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE) + * @param z wheel state (>0 to scroll down, <0 to scroll up) + * @returns true if there is no error, false otherwise + */ + bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z); + + + /** + * Move the cursor to (x, y) + * + * @param x x-axis position + * @param y y-axis position + * @returns true if there is no error, false otherwise + */ + bool move(int16_t x, int16_t y); + + /** + * Press one or several buttons + * + * @param button button state (ex: press(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool press(uint8_t button); + + /** + * Release one or several buttons + * + * @param button button state (ex: release(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool release(uint8_t button); + + /** + * Double click (MOUSE_LEFT) + * + * @returns true if there is no error, false otherwise + */ + bool doubleClick(); + + /** + * Click + * + * @param button state of the buttons ( ex: clic(MOUSE_LEFT)) + * @returns true if there is no error, false otherwise + */ + bool click(uint8_t button); + + /** + * Scrolling + * + * @param z value of the wheel (>0 to go down, <0 to go up) + * @returns true if there is no error, false otherwise + */ + bool scroll(int8_t z); + + /** + * To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key + * + * @code + * //To send CTRL + s (save) + * keyboard.keyCode('s', KEY_CTRL); + * @endcode + * + * @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0) + * @param key character to send + * @returns true if there is no error, false otherwise + */ + bool key_code(uint8_t key, uint8_t modifier = 0); + + /** + * Send a character + * + * @param c character to be sent + * @returns true if there is no error, false otherwise + */ + virtual int _putc(int c); + + /** + * Control media keys + * + * @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN) + * @returns true if there is no error, false otherwise + */ + bool media_control(MEDIA_KEY key); + + /** + * Read status of lock keys. Useful to switch-on/off LEDs according to key pressed. Only the first three bits of the result is important: + * - First bit: NUM_LOCK + * - Second bit: CAPS_LOCK + * - Third bit: SCROLL_LOCK + * + * @returns status of lock keys + */ + uint8_t lock_status(); + + /* + * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the report descriptor + */ + virtual const uint8_t *report_desc(); + + /* + * Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys + * + * @returns if handle by subclass, return true + */ + virtual void report_rx(); + + +private: + MOUSE_TYPE _mouse_type; + uint8_t _button; + uint8_t _lock_status; + PlatformMutex _mutex; + + bool _mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z); + + //dummy otherwise it doesn't compile (we must define all methods of an abstract class) + virtual int _getc(); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/USBSerial.h b/drivers/usb/include/usb/USBSerial.h new file mode 100644 index 0000000..8103739 --- /dev/null +++ b/drivers/usb/include/usb/USBSerial.h @@ -0,0 +1,227 @@ +/* + * 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 USBSERIAL_H +#define USBSERIAL_H + +#include "USBCDC.h" +#include "platform/Stream.h" +#include "Callback.h" + +/** + * \defgroup drivers_USBSerial USBSerial class + * \ingroup drivers-public-api-usb + * @{ + */ + +/** +* USBSerial example +* +* @code +* #include "mbed.h" +* #include "USBSerial.h" +* +* //Virtual serial port over USB +* USBSerial serial; +* +* int main(void) { +* +* while(1) +* { +* serial.printf("I am a virtual serial port\n"); +* ThisThread::sleep_for(1000); +* } +* } +* @endcode +*/ +class USBSerial: public USBCDC, public mbed::Stream { +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 (default: 0x1f00) + * @param product_id Your product_id (default: 0x2012) + * @param product_release Your product_release (default: 0x0001) + * + */ + USBSerial(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, 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 (default: 0x1f00) + * @param product_id Your product_id (default: 0x2012) + * @param product_release Your product_release (default: 0x0001) + * + */ + USBSerial(USBPhy *phy, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001); + + /** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ + virtual ~USBSerial(); + + /** + * Send a character. You can use puts, printf. + * + * @param c character to be sent + * @returns true if there is no error, false otherwise + */ + virtual int _putc(int c); + + /** + * Read a character: blocking + * + * @returns character read + */ + virtual int _getc(); + + /** + * Check the number of bytes available. + * + * @returns the number of bytes available + */ + uint8_t available(); + + /** + * Check if the terminal is connected. + * + * @returns connection status + */ + bool connected(); + + /** Determine if there is a character available to read + * + * @returns + * 1 if there is a character available to read, + * 0 otherwise + */ + int readable() + { + return available() ? 1 : 0; + } + + /** Determine if there is space available to write a character + * + * @returns + * 1 if there is space to write a character, + * 0 otherwise + */ + int writeable() + { + return 1; // always return 1, for write operation is blocking + } + + /** + * Attach a member function to call when a packet is received. + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template + void attach(T *tptr, void (T::*mptr)(void)) + { + USBCDC::lock(); + + if ((mptr != NULL) && (tptr != NULL)) { + rx = mbed::Callback(tptr, mptr); + } + + USBCDC::unlock(); + } + + /** + * Attach a callback called when a packet is received + * + * @param fptr function pointer + */ + void attach(void (*fptr)(void)) + { + USBCDC::lock(); + + if (fptr != NULL) { + rx = mbed::Callback(fptr); + } + + USBCDC::unlock(); + } + + /** + * Attach a Callback called when a packet is received + * + * @param cb Callback to attach + */ + void attach(mbed::Callback &cb) + { + USBCDC::lock(); + + rx = cb; + + USBCDC::unlock(); + } + + /** + * Attach a callback to call when serial's settings are changed. + * + * @param fptr function pointer + */ + void attach(void (*fptr)(int baud, int bits, int parity, int stop)) + { + USBCDC::lock(); + + _settings_changed_callback = fptr; + + USBCDC::unlock(); + } + +protected: + virtual void data_rx(); + virtual void line_coding_changed(int baud, int bits, int parity, int stop) + { + assert_locked(); + + if (_settings_changed_callback) { + _settings_changed_callback(baud, bits, parity, stop); + } + } + +private: + mbed::Callback rx; + void (*_settings_changed_callback)(int baud, int bits, int parity, int stop); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/AsyncOp.h b/drivers/usb/include/usb/internal/AsyncOp.h new file mode 100644 index 0000000..84789fb --- /dev/null +++ b/drivers/usb/include/usb/internal/AsyncOp.h @@ -0,0 +1,129 @@ +/* + * 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 MBED_ASYNC_OP_H +#define MBED_ASYNC_OP_H + +#include "Mutex.h" +#include "Semaphore.h" +#include "Callback.h" + +#include "LinkEntry.h" +#include "OperationListBase.h" + +/** \defgroup mbed-os-internal Internal API */ + +/** \addtogroup drivers-internal-api Drivers + * \ingroup mbed-os-internal + */ + +/** \defgroup drivers-internal-api-usb USB + * \ingroup drivers-internal-api + */ + +/** + * \defgroup drivers_AsyncOp AsyncOp class + * \ingroup drivers-internal-api-usb + * @{ + */ +class AsyncOp: public LinkEntry { +public: + + /** + * Construct a new AsyncOp object + */ + AsyncOp(); + + /** + * Construct a new AsyncOp object + * + * @param callback Completion callback + */ + AsyncOp(mbed::Callback &callback); + + /** + * Cleanup resources used by this AsyncOp + */ + virtual ~AsyncOp(); + + /** + * Wait for this asynchronous operation to complete + * + * If the timeout expires then this asynchronous operation is + * aborted and the timeout flag is set. + * + * @note - the host object's lock MUST NOT be held when this call is made + */ + void wait(rtos::Mutex *host_mutex, rtos::Kernel::Clock::duration_u32 rel_time = rtos::Kernel::wait_for_u32_forever); + + /** + * Abort this asynchronous operation + * + * This function has no effect if the operation is complete. Otherwise + * the aborted flag is set. + * + * @note - the host object's lock MUST be held when this call is made + */ + void abort(); + + /** + * Check if this operation timed out + * + * @return true if this operation timed out, false otherwise + */ + bool timeout(); + + /** + * Check if this operation was aborted + * + * @return true if this operation was aborted, false otherwise + */ + bool aborted(); + +protected: + + /** + * Callback indicating that something changed + * + * @return true if finished false if not + */ + virtual bool process() = 0; + + /** + * Callback indicating that this event finished + */ + virtual void complete(); + +private: + friend class OperationListBase; + + mbed::Callback _callback; + OperationListBase *_list; + rtos::Semaphore *_wait; + bool _aborted; + bool _timeout; + + void _abort(bool timeout); + + static void _host_lock(rtos::Mutex *host_mutex); + + static void _host_unlock(rtos::Mutex *host_mutex); +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/ByteBuffer.h b/drivers/usb/include/usb/internal/ByteBuffer.h new file mode 100644 index 0000000..45f2cb1 --- /dev/null +++ b/drivers/usb/include/usb/internal/ByteBuffer.h @@ -0,0 +1,126 @@ +/* + * 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 BYTE_BUFFER_H +#define BYTE_BUFFER_H + +#include + +/** + * \defgroup drivers_ByteBuffer ByteBuffer class + * \ingroup drivers-internal-api-usb + * @{ + */ +class ByteBuffer { +public: + + /** + * Create a byte buffer of the given size + * + * @param size Number of bytes this buffer can hold + */ + ByteBuffer(uint32_t size = 0); + + /** + * Delete this byte buffer + */ + ~ByteBuffer(); + + /** + * Set the size of the buffer + * + * Buffer contents are reset. + * + * @param size New buffer size + */ + void resize(uint32_t size); + + /** + * Add a single byte to this buffer + * + * There must be enough space in the buffer or the behavior is undefined. + * + * @param data byte to add + */ + void push(uint8_t data); + + /** + * Write a block of data to this ByteBuffer + * + * There must be enough space in the ByteBuffer or the behavior is undefined. + * + * @param data Block of data to write + * @param size Size of data to write + */ + void write(uint8_t *data, uint32_t size); + + /** + * Remove a byte from this buffer + * + * @return data byte + */ + uint8_t pop(); + + /** + * Read a block of data from this ByteBuffer into a buffer pointed by 'data' + * + * There must be enough data in the ByteBuffer or the behavior is undefined. + * + * @param data Block of data to read + * @param size Size of data to read + */ + void read(uint8_t *data, uint32_t size); + + /** + * Return the number bytes in this byte buffer + * + * @return Number of used bytes + */ + uint32_t size(); + + /** + * Return the number of additional bytes this buffer can hold + * + * @return Number of free bytes + */ + uint32_t free(); + + /** + * Check if this byte buffer is full + * + * @return true if full, false otherwise + */ + bool full(); + + /** + * Check if this byte buffer is empty + * + * @return true if empty, false otherwise + */ + bool empty(); + +private: + + uint32_t _head; + uint32_t _tail; + uint32_t _size; + uint8_t *_buf; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/EndpointResolver.h b/drivers/usb/include/usb/internal/EndpointResolver.h new file mode 100644 index 0000000..7ea13f4 --- /dev/null +++ b/drivers/usb/include/usb/internal/EndpointResolver.h @@ -0,0 +1,99 @@ +/* + * 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 ENDPOINT_RESOLVER_H +#define ENDPOINT_RESOLVER_H + +#include "USBPhy.h" + +/** + * \defgroup drivers_EndpointResolver EndpointResolver class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** + * Utility class for resolving endpoints + * + * This class is intended to make the process of + * selecting the correct endpoint from a device endpoint + * table easier. It also provides a verification function + * to check if the device has enough resources for the + * given configuration. + * + */ +class EndpointResolver { +public: + EndpointResolver(const usb_ep_table_t *table); + ~EndpointResolver(); + + /** + * Add control endpoint size + * + * @param size Space reserved for control in and control out + */ + void endpoint_ctrl(uint32_t size); + + /** + * Return a free IN endpoint of the given size + * + * @param type Desired endpoint type + * @param size Space to reserve for this endpoint + * @return Endpoint index or 0 if there are not enough resources + */ + usb_ep_t endpoint_in(usb_ep_type_t type, uint32_t size); + + /** + * Return a free OUT endpoint of the given size + * + * @param type Desired endpoint type + * @param size Space to reserve for this endpoint + * @return Endpoint index or 0 if there are not enough resources + */ + usb_ep_t endpoint_out(usb_ep_type_t type, uint32_t size); + + /** + * Get next free endpoint + */ + usb_ep_t next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size); + + /** + * Check if the endpoint configuration created so far is valid + * + * @return true if all endpoint sizes are available and fit, false otherwise + */ + bool valid(); + + /** + * Reset this class's state to when it was constructed + */ + void reset(); + +private: + + usb_ep_t index_to_endpoint(int index); + int next_index(usb_ep_type_t type, bool in_not_out); + + const usb_ep_table_t *_table; + uint32_t _cost; + uint32_t _used; + bool _valid; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/LinkEntry.h b/drivers/usb/include/usb/internal/LinkEntry.h new file mode 100644 index 0000000..f3fc947 --- /dev/null +++ b/drivers/usb/include/usb/internal/LinkEntry.h @@ -0,0 +1,42 @@ +/* + * 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 MBED_LINKED_ENTRY_H +#define MBED_LINKED_ENTRY_H + +#include + +/** + * \defgroup drivers_LinkEntry LinkEntry class + * \ingroup drivers-internal-api-usb + * @{ + */ +class LinkEntry { +public: + LinkEntry(): _next(NULL) + { + + } + +private: + friend class LinkedListBase; + LinkEntry *_next; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/LinkedList.h b/drivers/usb/include/usb/internal/LinkedList.h new file mode 100644 index 0000000..2347861 --- /dev/null +++ b/drivers/usb/include/usb/internal/LinkedList.h @@ -0,0 +1,68 @@ +/* + * 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 MBED_LINKED_LIST_H +#define MBED_LINKED_LIST_H + +#include "LinkEntry.h" +#include "LinkedListBase.h" + +/** + * \defgroup drivers_LinkedList LinkedList class + * \ingroup drivers-internal-api-usb + * @{ + */ +template +class LinkedList: public LinkedListBase { +public: + LinkedList() {} + ~LinkedList() {} + + /** + * Return the element at the head of the list + * + * @return The element at the head of the list or NULL if the list is empty + */ + T *head() + { + return static_cast(LinkedListBase::head()); + } + + /** + * Add an element to the tail of the list + * + * @param entry New element to add + */ + void enqueue(T *entry) + { + LinkedListBase::enqueue(static_cast(entry)); + } + + /** + * Remove the element at the head of the list + * + * @return The element at the head of the list or NULL if the list is empty + */ + T *dequeue() + { + return static_cast(LinkedListBase::dequeue()); + } +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/LinkedListBase.h b/drivers/usb/include/usb/internal/LinkedListBase.h new file mode 100644 index 0000000..27d8610 --- /dev/null +++ b/drivers/usb/include/usb/internal/LinkedListBase.h @@ -0,0 +1,68 @@ +/* + * 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 MBED_LINKED_LIST_BASE_H +#define MBED_LINKED_LIST_BASE_H + +#include "LinkEntry.h" + +/** + * \defgroup drivers_LinkedListBase LinkedListBase class + * \ingroup drivers-internal-api-usb + * @{ + */ +class LinkedListBase { +public: + LinkedListBase(); + ~LinkedListBase(); + + /** + * Return the element at the head of the list + * + * @return The element at the head of the list or NULL if the list is empty + */ + LinkEntry *head(); + + /** + * Add an element to the tail of the list + * + * @param entry New element to add + */ + void enqueue(LinkEntry *entry); + + /** + * Remove the element at the head of the list + * + * @return The element at the head of the list or NULL if the list is empty + */ + LinkEntry *dequeue(); + + /** + * Remove the specified element if it is in the list + * + * @param entry Element to remove from the list + */ + void remove(LinkEntry *entry); + +private: + LinkEntry *_head; + LinkEntry *_tail; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/MIDIMessage.h b/drivers/usb/include/usb/internal/MIDIMessage.h new file mode 100644 index 0000000..5e41765 --- /dev/null +++ b/drivers/usb/include/usb/internal/MIDIMessage.h @@ -0,0 +1,484 @@ +/* + * 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 MIDIMESSAGE_H +#define MIDIMESSAGE_H + +#include + +#define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage + +// MIDI Message Format +// +// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ] +// +// MIDI Data Messages (Channel Specific) +// +// Message msg n m +// --------------------------------------------- +// Note Off 0x8 Key Velocity +// Note On 0x9 Key Velocity +// Polyphonic Aftertouch 0xA Key Pressure +// Control Change 0xB Controller Value +// Program Change 0xC Program - +// Channel Aftertouch 0xD Pressure - +// Pitch Wheel 0xE LSB MSB + +#define CABLE_NUM (0<<4) + + +/** + * \defgroup drivers_MIDIMessage MIDIMessage class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** A MIDI message container */ +class MIDIMessage { +public: + + MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {} + + MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) + { + for (int i = 0; i < 4; i++) { + data[i] = buf[i]; + } + } + + /** + * Copy constructor + */ + MIDIMessage(const MIDIMessage &other) + { + *this = other; + } + + /** + * Assignment operator + */ + MIDIMessage &operator=(const MIDIMessage &other) + { + length = other.length; + for (int i = 0; i < length; i++) { + data[i] = other.data[i]; + } + + return *this; + } + + ~MIDIMessage() + { + delete[] data; + } + + /** + * Set this MIDIMessage to a raw MIDI message + * + * @param buf is a true MIDI message (not USBMidi message) + * @param buf_len size of message + */ + void from_raw(uint8_t *buf, int buf_len) + { + length = buf_len + 1; + if (length > MAX_MIDI_MESSAGE_SIZE) { + // Message is too big + length = 0; + return; + } + + // first byte keeped for retro-compatibility + data[0] = CABLE_NUM | 0x08; + + for (int i = 0; i < buf_len; i++) { + data[i + 1] = buf[i]; + } + } + + // create messages + + /** Create a NoteOff message + * @param key Key ID + * @param velocity Key velocity (0-127, default = 127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x08; + msg.data[1] = 0x80 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = velocity & 0x7F; + msg.length = 4; + return msg; + } + + /** Create a NoteOn message + * @param key Key ID + * @param velocity Key velocity (0-127, default = 127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x09; + msg.data[1] = 0x90 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = velocity & 0x7F; + msg.length = 4; + return msg; + } + + /** Create a PolyPhonic Aftertouch message + * @param key Key ID + * @param pressure Aftertouch pressure (0-127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0A; + msg.data[1] = 0xA0 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = pressure & 0x7F; + msg.length = 4; + return msg; + } + + /** Create a Control Change message + * @param control Controller ID + * @param value Controller value (0-127) + * @param channel Controller channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ControlChange(int control, int value, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0B; + msg.data[1] = 0xB0 | (channel & 0x0F); + msg.data[2] = control & 0x7F; + msg.data[3] = value & 0x7F; + msg.length = 4; + return msg; + } + + /** Create a Program Change message + * @param program Program ID + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ProgramChange(int program, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0C; + msg.data[1] = 0xC0 | (channel & 0x0F); + msg.data[2] = program & 0x7F; + msg.data[3] = 0x00; + msg.length = 4; + return msg; + } + + /** Create a Channel Aftertouch message + * @param pressure Pressure + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ChannelAftertouch(int pressure, int channel = 0) + { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0D; + msg.data[1] = 0xD0 | (channel & 0x0F); + msg.data[2] = pressure & 0x7F; + msg.data[3] = 0x00; + msg.length = 4; + return msg; + } + + /** Create a Pitch Wheel message + * @param pitch Pitch (-8192 - 8191, default = 0) + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage PitchWheel(int pitch = 0, int channel = 0) + { + MIDIMessage msg; + int p = pitch + 8192; // 0 - 16383, 8192 is center + msg.data[0] = CABLE_NUM | 0x0E; + msg.data[1] = 0xE0 | (channel & 0x0F); + msg.data[2] = p & 0x7F; + msg.data[3] = (p >> 7) & 0x7F; + msg.length = 4; + return msg; + } + + /** Create an All Notes Off message + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage AllNotesOff(int channel = 0) + { + return ControlChange(123, 0, channel); + } + + /** Create a SysEx message + * @param data SysEx data (including 0xF0 .. 0xF7) + * @param len SysEx data length + * @returns A MIDIMessage + */ + static MIDIMessage SysEx(uint8_t *data, int len) + { + MIDIMessage msg; + msg.from_raw(data, len); + return msg; + } + + // decode messages + + /** MIDI Message Types */ + enum MIDIMessageType { + ErrorType, + NoteOffType, + NoteOnType, + PolyphonicAftertouchType, + ControlChangeType, + ProgramChangeType, + ChannelAftertouchType, + PitchWheelType, + ResetAllControllersType, + AllNotesOffType, + SysExType + }; + + /** Read the message type + * + * @returns MIDIMessageType + */ + MIDIMessageType type() + { + MIDIMessageType message_type; + uint8_t min_size; + switch ((data[1] >> 4) & 0xF) { + case 0x8: + // message, channel + // key + // velocity + min_size = 3; + message_type = NoteOffType; + break; + case 0x9: + // message, channel + // key + // velocity + min_size = 3; + message_type = NoteOnType; + break; + case 0xA: + // message, channel + // key + // pressure + min_size = 3; + message_type = PolyphonicAftertouchType; + break; + case 0xB: + // message, channel + // controller + min_size = 2; + if ((data[2] & 0x7F) < 120) { // standard controllers + message_type = ControlChangeType; + } else if ((data[2] & 0x7F) == 121) { + message_type = ResetAllControllersType; + } else if ((data[2] & 0x7F) == 123) { + message_type = AllNotesOffType; + } else { + message_type = ErrorType; // unsupported atm + } + break; + case 0xC: + // message, channel + // program + min_size = 2; + message_type = ProgramChangeType; + break; + case 0xD: + // message, channel + // pressure + min_size = 2; + message_type = ChannelAftertouchType; + break; + case 0xE: + // message, channel + // pitch lsb + // pitch msb + min_size = 3; + message_type = PitchWheelType; + break; + case 0xF: + min_size = 2; + message_type = SysExType; + break; + default: + message_type = ErrorType; + break; + } + + + if (length < min_size) { + // too small to be a valid message + message_type = ErrorType; + } + return message_type; + } + + /** + * Read the channel number + * + * @return channel number or -1 on error + */ + + int channel() + { + return (data[1] & 0x0F); + } + + /** + * Read the key ID + * + * @return key ID or -1 on error + */ + int key() + { + MIDIMessageType msg_type = type(); + if ((msg_type != NoteOffType) && + (msg_type != NoteOnType) && + (msg_type != PolyphonicAftertouchType)) { + return -1; + } + + return data[2] & 0x7F; + } + + /** + * Read the velocity + * + * @return velocity or -1 on error + */ + int velocity() + { + MIDIMessageType msg_type = type(); + if ((msg_type != NoteOffType) && + (msg_type != NoteOnType)) { + return -1; + } + + return data[3] & 0x7F; + } + + /** + * Read the controller value + * + * @return controller value or -1 on error + */ + int value() + { + MIDIMessageType msg_type = type(); + if ((msg_type != ControlChangeType) && + (msg_type != ResetAllControllersType) && + (msg_type != AllNotesOffType)) { + return -1; + } + + return data[3] & 0x7F; + } + + /** + * Read the aftertouch pressure + * + * @return aftertouch pressure or -1 on error + */ + int pressure() + { + MIDIMessageType msg_type = type(); + if ((msg_type != PolyphonicAftertouchType) && + (msg_type != ChannelAftertouchType)) { + return -1; + } + + if (type() == PolyphonicAftertouchType) { + return data[3] & 0x7F; + } else { + return data[2] & 0x7F; + } + } + + /** + * Read the controller number + * + * @return controller number or -1 on error + */ + int controller() + { + MIDIMessageType msg_type = type(); + if ((msg_type != ControlChangeType) && + (msg_type != ResetAllControllersType) && + (msg_type != AllNotesOffType)) { + return -1; + } + + return data[2] & 0x7F; + } + + /** + * Read the program number + * + * @return program number or -1 on error + */ + int program() + { + MIDIMessageType msg_type = type(); + if (msg_type != ProgramChangeType) { + return -1; + } + + return data[2] & 0x7F; + } + + /** + * Read the pitch value + * + * @return pitch value or -1 on error + */ + int pitch() + { + MIDIMessageType msg_type = type(); + if (msg_type != PitchWheelType) { + return -1; + } + + int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F); + return p - 8192; // 0 - 16383, 8192 is center + } + + uint8_t *data; + uint16_t length; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/OperationList.h b/drivers/usb/include/usb/internal/OperationList.h new file mode 100644 index 0000000..9f5e756 --- /dev/null +++ b/drivers/usb/include/usb/internal/OperationList.h @@ -0,0 +1,94 @@ +/* + * 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 MBED_OPERATION_LIST_H +#define MBED_OPERATION_LIST_H + +#include "OperationListBase.h" +#include "AsyncOp.h" + +/** + * \defgroup drivers_OperationList OperationList class + * \ingroup drivers-internal-api-usb + * @{ + */ +template +class OperationList: public OperationListBase { +public: + + /** + * Create a new empty operation list + */ + OperationList() + { + + } + + /** + * Destroy this object and abort all operations + */ + ~OperationList() + { + + } + /** + * Add an operation to the list + * + * If the list was empty then call process on this + * operation + * + * @param op Operation to add + */ + void add(T *op) + { + OperationListBase::add(op); + } + + /** + * Remove an operation from the list + * + * If this was the head of the list then process the + * next element in the list. + * + * @param op Operation to remove + */ + void remove(T *op) + { + OperationListBase::remove(op); + } + + /** + * Dequeue the head of the list + * + * Remove the head of the operation list without completing it + * or processing the next element. The caller must call the + * AsnycOp::complete() function of the returned object. + * Additionally process() must be called on this object + * if there are still elements in the list. + * + * @return The asynchronous op at the head of the list + */ + T *dequeue_raw() + { + return static_cast(OperationListBase::dequeue_raw()); + } + +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/OperationListBase.h b/drivers/usb/include/usb/internal/OperationListBase.h new file mode 100644 index 0000000..9cd6f9f --- /dev/null +++ b/drivers/usb/include/usb/internal/OperationListBase.h @@ -0,0 +1,103 @@ +/* + * 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 MBED_OPERATION_LIST_BASE_H +#define MBED_OPERATION_LIST_BASE_H + +#include "usb/internal/LinkedListBase.h" + +class AsyncOp; + +/** + * \defgroup drivers_OperationListBase OperationListBase class + * \ingroup drivers-internal-api-usb + * @{ + */ +class OperationListBase { +public: + + /** + * Create a new empty operation list + */ + OperationListBase(); + + /** + * Destroy this object and abort all operations + */ + ~OperationListBase(); + + /** + * Check if the list is empty + * + * @return true if the list is empty false otherwise + */ + bool empty(); + + /** + * Add an operation to the list + * + * If the list was empty then call process on this + * operation + * + * @param op Operation to add + */ + void add(AsyncOp *op); + + /** + * Remove an operation from the list + * + * If this was the head of the list then process the + * next element in the list. + * + * @param op Operation to remove + */ + void remove(AsyncOp *op); + + /** + * Dequeue the head of the list + * + * Remove the head of the operation list without completing it + * or processing the next element. The caller must call the + * AsnycOp::complete() function of the returned object. + * Additionally process() must be called on this object + * if there are still elements in the list. + * + * @return The asynchronous op at the head of the list + */ + AsyncOp *dequeue_raw(); + + /** + * Abort all operations + */ + void remove_all(); + + /** + * Process the operation list + * + * This allow the operation at the head of the list to perform processing + */ + void process(); + +private: + friend class AsyncOp; + + LinkedListBase _list; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/PolledQueue.h b/drivers/usb/include/usb/internal/PolledQueue.h new file mode 100644 index 0000000..539ea0c --- /dev/null +++ b/drivers/usb/include/usb/internal/PolledQueue.h @@ -0,0 +1,76 @@ +/* + * 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 POLLED_QUEUE_H +#define POLLED_QUEUE_H + +#include "usb/internal/TaskQueue.h" +#include "platform/Callback.h" +#include "LinkedList.h" + +namespace events { +/** + * \defgroup drivers_PolledQueue PolledQueue class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** PolledQueue + * + * This class is an implementation of TaskQueue which is + * processed synchronously by calls to dispatch. + */ +class PolledQueue: public TaskQueue { +public: + + /** Create a PolledQueue + * + * Create an event queue. + * + * @param cb Callback called when dispatch needs to be called + */ + PolledQueue(mbed::Callback cb = nullptr); + + virtual ~PolledQueue(); + + virtual void post(TaskBase *event); + + virtual void cancel(TaskBase *event); + + /** + * Process all the events in this queue + */ + void dispatch(); + + /** + * Attach a callback indicating that this queue needs to be processed + * + * @param cb Callback called when dispatch needs to be called + */ + void attach(mbed::Callback cb); + +protected: + + mbed::Callback _cb; + LinkedList _list; + +}; + +/** @}*/ + +} +#endif diff --git a/drivers/usb/include/usb/internal/Task.h b/drivers/usb/include/usb/internal/Task.h new file mode 100644 index 0000000..b629fdd --- /dev/null +++ b/drivers/usb/include/usb/internal/Task.h @@ -0,0 +1,655 @@ +/* + * 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 MBED_TASK_H +#define MBED_TASK_H + +#include "events/EventQueue.h" +#include "usb/internal/TaskBase.h" +#include "platform/mbed_assert.h" +#include "platform/Callback.h" + +namespace events { +/** \addtogroup drivers-internal-api-usb + * @{ + */ + + +template +struct AllArgs; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + + AllArgs(B0 b0 = B0()): b0(b0) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + B1 b1; + + AllArgs(B0 b0 = B0(), B1 b1 = B1()): b0(b0), b1(b1) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(s->b1); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + B1 b1; + B2 b2; + + + AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2()): b0(b0), b1(b1), b2(b2) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(s->b1, s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + B1 b1; + B2 b2; + B3 b3; + + + AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3()): b0(b0), b1(b1), b2(b2), b3(b3) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + B1 b1; + B2 b2; + B3 b3; + B4 b4; + + + AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + B1 b1; + B2 b2; + B3 b3; + B4 b4; + B5 b5; + + + AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4(), B5 b5 = B5()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self *)_src); + } + + static void call(void *data) + { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + typedef Operations ops; +}; + + +template +class Task; + +template +class Task: public TaskBase { +public: + + Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) + : TaskBase(q), _args(cb) + { + } + + Task &operator=(mbed::Callback cb) + { + _args.b0 = cb; + return *this; + } + + void call() + { + post(); + } + +protected: + + virtual uint32_t size() + { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) + { + All::ops::copy(data, (void *)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs > All; + All _args; +}; + +template +class Task: public TaskBase { +public: + + Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) + : TaskBase(q), _args(cb) + { + } + + Task &operator=(mbed::Callback cb) + { + _args.b0 = cb; + return *this; + } + + void call(A0 a0) + { + _args.b1 = a0; + post(); + } + +protected: + + virtual uint32_t size() + { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) + { + All::ops::copy(data, (void *)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs, A0> All; + All _args; +}; + +/** Task + * + * Representation of a postable task + */ +template +class Task: public TaskBase { +public: + + /** + * Construct a new task + * + * @param q TaskQueue to post to + * @param cb Callback to run + */ + Task(TaskQueue *q = NULL, mbed::Callback cb = mbed::Callback()) + : TaskBase(q), _args(cb) + { + } + + /** + * Set the callback of this task + * + * @param cb Callback to run + */ + Task &operator=(mbed::Callback cb) + { + _args.b0 = cb; + return *this; + } + + /** + * Post this task for execution + * + * The number of arguments to call should match + * the type of the callback. For example Task + * expects two integers as arguments to call, while Task + * expects no arguments. + * + * @param a0 First callback parameter + * @param a1 Second callback parameter + */ + void call(A0 a0, A1 a1) + { + _args.b1 = a0; + _args.b2 = a1; + post(); + } + +protected: + + virtual uint32_t size() + { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) + { + All::ops::copy(data, (void *)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs, A0, A1> All; + All _args; +}; + +/** @}*/ + +} + +#endif diff --git a/drivers/usb/include/usb/internal/TaskBase.h b/drivers/usb/include/usb/internal/TaskBase.h new file mode 100644 index 0000000..e8a38b4 --- /dev/null +++ b/drivers/usb/include/usb/internal/TaskBase.h @@ -0,0 +1,168 @@ +/* + * 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 TASK_BASE_H +#define TASK_BASE_H + +#include "platform/Callback.h" +#include "platform/mbed_assert.h" +#include "LinkEntry.h" + +namespace rtos { +class Semaphore; +} + +namespace events { + +class TaskQueue; + +/** + * \defgroup drivers_TaskBase TaskBase class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** TaskBase + * + * Representation of a caller allocated task + */ +class TaskBase : public LinkEntry { +public: + + typedef void (*run_callback_t)(void *data); + + /** + * Construct a new TaskBase object + * + * @param q Queue for posting to + */ + TaskBase(TaskQueue *q); + + /** + * Destroy this TaskBase + */ + virtual ~TaskBase(); + + /** + * Set the queue of this task + * + * @param q TaskQueue to post to + */ + void set(TaskQueue *q); + + /** + * Cancel the execution of this task + * + * Once cancelled the task can be posted again. Previous + * calls to post may still run. If you need to ensure the + * callback has finished the function wait() can be used. + * + * @note This function is interrupt safe + */ + void cancel(); + + /** + * Return true if this task is ready to be posted + * + * Check if this task is on a queue waiting to be run. + * + * @return true if it is safe to call post + */ + bool ready(); + + /** + * Wait for this task to finish execution + * + * When this function returns then this task is in the finished state. + */ + void wait(); + + /** + * Check if the callback has run to completion or been fully canceled + * + * When an task is finished the queue is completely done with it and the + * callback is either fully complete or has been canceled and will not run. + * + * @return true if this task has been flushed from the queue, false otherwise + */ + bool finished(); + +protected: + + /** + * Size of buffer required for TaskBase::start + * + * @return requested buffer size + */ + virtual uint32_t size() = 0; + + /** + * Copy any callback data and return a callback to run + * + * @param data Buffer to copy data to. Do not copy more than TaskBase::size() data. + * @param size Maximum size to copy + */ + virtual run_callback_t start(void *data, uint32_t size) = 0; + + /** + * Inform this task that execution has finished. + * + */ + virtual void finish(); + + /** + * Post this task to the set TaskQueue for execution + */ + void post(); + +private: + + TaskQueue *_queue; + bool _posted; + uint16_t _start_count; + rtos::Semaphore *_flush_sem; + + friend class TaskQueue; + + /* + * Must be called in a critical section + * + * This function should not be called directly. Instead + * TaskQueue::task_start should be used instead. + */ + run_callback_t _start(void *buffer, uint32_t size); + + /* + * Must be called in a critical section + * + * This function should not be called directly. Instead + * TaskQueue::task_finish should be used instead. + * + */ + void _finish(); + + /* + * Unblock wait if this task is finished + */ + void _wake_check(); +}; + +/** @}*/ + +} + +#endif diff --git a/drivers/usb/include/usb/internal/TaskQueue.h b/drivers/usb/include/usb/internal/TaskQueue.h new file mode 100644 index 0000000..f38d6c1 --- /dev/null +++ b/drivers/usb/include/usb/internal/TaskQueue.h @@ -0,0 +1,142 @@ +/* + * 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 TASK_QUEUE_H +#define TASK_QUEUE_H + +#include "usb/internal/TaskBase.h" +#include "platform/Callback.h" +#include "mbed_critical.h" + +#define MBED_MAX_TASK_SIZE 32 + +namespace events { + +/** + * \defgroup drivers_TaskQueue TaskQueue class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** TaskQueue + * + * Flexible task queue for dispatching tasks + */ +class TaskQueue { +public: + + /** Create a TaskQueue + * + * Create an event queue. + */ + TaskQueue() + { + + } + + /** Destroy a TaskQueue + */ + virtual ~TaskQueue() + { + + } + + /** + * Add this event to the queue for execution + * + * If the event is already in the queue then it is canceled and + * added to the end of the queue. + * + * @param event Pointer to the event + */ + virtual void post(TaskBase *event) = 0; + + /** Cancel an in-flight event + * + * Cancels the given event so the event's memory can be reused. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. It does + * guarantee that the event queue is no longer using event data so + * the event can be freed or reused. + * + * @param event Pointer to the event + */ + virtual void cancel(TaskBase *event) = 0; + +protected: + + /** + * Get the size required to run this task + * + * Get the minimum size required for TaskQueue::task_start + * + * @param task The task to check size on + * @return required size + * @note This call must be made in a critical section + */ + static uint32_t task_size(TaskBase *task) + { + + return task->size(); + } + + /** + * Start processing this event by copying out its data + * + * Inform this event both that callback execution has started + * and that the event is free to be posted again. + * + * @param task The task to start processing + * @param dest The buffer to copy the callback arguments to + * @param size maximum size to copy + * @return Pointer to function run + * + * @note event_start must not be called on a canceled event as the + * memory may have been freed already + * @note Every call to event_start must be paired with event_finish + * @note This call must be made in a critical section + */ + static TaskBase::run_callback_t task_start(TaskBase *task, uint8_t *dest, uint32_t size) + { + + return task->_start(dest, size); + } + + /** + * Finish processing this event + * + * Inform this event that the callback has run to completion. + * + * @param task The task to finish processing + * + * @note Every call to event_finish must be preceded by a call to event_start + * @note This call must be made in a critical section + */ + static void task_finish(TaskBase *task) + { + task->_finish(); + } +}; + +/** @}*/ + +} +#endif diff --git a/drivers/usb/include/usb/internal/USBAudio_Types.h b/drivers/usb/include/usb/internal/USBAudio_Types.h new file mode 100644 index 0000000..f13a867 --- /dev/null +++ b/drivers/usb/include/usb/internal/USBAudio_Types.h @@ -0,0 +1,96 @@ +/* + * 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 USBAUDIO_TYPES_H +#define USBAUDIO_TYPES_H + + +#define DEFAULT_CONFIGURATION (1) + +// Audio Request Codes +#define REQUEST_SET_CUR 0x01 +#define REQUEST_GET_CUR 0x81 +#define REQUEST_SET_MIN 0x02 +#define REQUEST_GET_MIN 0x82 +#define REQUEST_SET_MAX 0x03 +#define REQUEST_GET_MAX 0x83 +#define REQUEST_SET_RES 0x04 +#define REQUEST_GET_RES 0x84 + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 + + +// Audio Descriptor Sizes +#define CONTROL_INTERFACE_DESCRIPTOR_LENGTH 0x09 +#define STREAMING_INTERFACE_DESCRIPTOR_LENGTH 0x07 +#define INPUT_TERMINAL_DESCRIPTOR_LENGTH 0x0C +#define OUTPUT_TERMINAL_DESCRIPTOR_LENGTH 0x09 +#define FEATURE_UNIT_DESCRIPTOR_LENGTH 0x09 +#define STREAMING_ENDPOINT_DESCRIPTOR_LENGTH 0x07 + +// Audio Format Type Descriptor Sizes +#define FORMAT_TYPE_I_DESCRIPTOR_LENGTH 0x0b + +#define AUDIO_CLASS 0x01 +#define SUBCLASS_AUDIOCONTROL 0x01 +#define SUBCLASS_AUDIOSTREAMING 0x02 + +// Audio Descriptor Types +#define INTERFACE_DESCRIPTOR_TYPE 0x24 +#define ENDPOINT_DESCRIPTOR_TYPE 0x25 + +// Audio Control Interface Descriptor Subtypes +#define CONTROL_HEADER 0x01 +#define CONTROL_INPUT_TERMINAL 0x02 +#define CONTROL_OUTPUT_TERMINAL 0x03 +#define CONTROL_FEATURE_UNIT 0x06 + +// USB Terminal Types +#define TERMINAL_USB_STREAMING 0x0101 + +// Predefined Audio Channel Configuration Bits +// Mono +#define CHANNEL_M 0x0000 +#define CHANNEL_L 0x0001 /* Left Front */ +#define CHANNEL_R 0x0002 /* Right Front */ + +// Feature Unit Control Bits +#define CONTROL_MUTE 0x0001 +#define CONTROL_VOLUME 0x0002 + +// Input Terminal Types +#define TERMINAL_MICROPHONE 0x0201 + +// Output Terminal Types +#define TERMINAL_SPEAKER 0x0301 +#define TERMINAL_HEADPHONES 0x0302 + +// Audio Streaming Interface Descriptor Subtypes +#define STREAMING_GENERAL 0x01 +#define STREAMING_FORMAT_TYPE 0x02 + +// Audio Data Format Type I Codes +#define FORMAT_PCM 0x0001 + +// Audio Format Types +#define FORMAT_TYPE_I 0x01 + +// Audio Endpoint Descriptor Subtypes +#define ENDPOINT_GENERAL 0x01 + +#endif diff --git a/drivers/usb/include/usb/internal/USBDescriptor.h b/drivers/usb/include/usb/internal/USBDescriptor.h new file mode 100644 index 0000000..5add2fe --- /dev/null +++ b/drivers/usb/include/usb/internal/USBDescriptor.h @@ -0,0 +1,75 @@ +/* + * 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 USBDESCRIPTOR_H +#define USBDESCRIPTOR_H + +/* Standard descriptor types */ +#define DEVICE_DESCRIPTOR (1) +#define CONFIGURATION_DESCRIPTOR (2) +#define STRING_DESCRIPTOR (3) +#define INTERFACE_DESCRIPTOR (4) +#define ENDPOINT_DESCRIPTOR (5) +#define QUALIFIER_DESCRIPTOR (6) + +/* Standard descriptor lengths */ +#define DEVICE_DESCRIPTOR_LENGTH (0x12) +#define CONFIGURATION_DESCRIPTOR_LENGTH (0x09) +#define INTERFACE_DESCRIPTOR_LENGTH (0x09) +#define ENDPOINT_DESCRIPTOR_LENGTH (0x07) + + +/*string offset*/ +#define STRING_OFFSET_LANGID (0) +#define STRING_OFFSET_IMANUFACTURER (1) +#define STRING_OFFSET_IPRODUCT (2) +#define STRING_OFFSET_ISERIAL (3) +#define STRING_OFFSET_ICONFIGURATION (4) +#define STRING_OFFSET_IINTERFACE (5) + +/* USB Specification Release Number */ +#define USB_VERSION_2_0 (0x0200) + +/* Least/Most significant byte of short integer */ +#define LSB(n) ((n)&0xff) +#define MSB(n) (((n)&0xff00)>>8) + +/* bmAttributes in configuration descriptor */ +/* C_RESERVED must always be set */ +#define C_RESERVED (1U<<7) +#define C_SELF_POWERED (1U<<6) +#define C_REMOTE_WAKEUP (1U<<5) + +/* bMaxPower in configuration descriptor */ +#define C_POWER(mA) ((mA)/2) + +/* bmAttributes in endpoint descriptor */ +#define E_CONTROL (0x00) +#define E_ISOCHRONOUS (0x01) +#define E_BULK (0x02) +#define E_INTERRUPT (0x03) + +/* For isochronous endpoints only: */ +#define E_NO_SYNCHRONIZATION (0x00) +#define E_ASYNCHRONOUS (0x04) +#define E_ADAPTIVE (0x08) +#define E_SYNCHRONOUS (0x0C) +#define E_DATA (0x00) +#define E_FEEDBACK (0x10) +#define E_IMPLICIT_FEEDBACK (0x20) + +#endif diff --git a/drivers/usb/include/usb/internal/USBDevice.h b/drivers/usb/include/usb/internal/USBDevice.h new file mode 100644 index 0000000..f03f7f8 --- /dev/null +++ b/drivers/usb/include/usb/internal/USBDevice.h @@ -0,0 +1,623 @@ +/* + * 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 USBDEVICE_H +#define USBDEVICE_H + +#include +#include "USBDevice_Types.h" +#include "USBPhy.h" +#include "mbed_critical.h" +#include "Callback.h" + +/** + * \defgroup drivers_USBDevice USBDevice class + * \ingroup drivers-internal-api-usb + * @{ + */ + +/** + * Core USB Device driver + * + * USB driver which wraps and provides synchronization for a USBPhy object. + */ +class USBDevice: public USBPhyEvents { +public: + typedef void (USBDevice::*ep_cb_t)(); + + enum RequestResult { + Receive = 0, + Send = 1, + Success = 2, + Failure = 3, + PassThrough = 4, + }; + + enum DeviceState { + Attached, + Powered, + Default, + Address, + Configured + }; + + struct setup_packet_t { + struct { + uint8_t dataTransferDirection; + uint8_t Type; + uint8_t Recipient; + } bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + }; + + /** + * Instantiate a new USBDevice with the given parameters + * + * @param phy The USBPhy providing physical USB access + * @param vendor_id The USB vendor ID + * @param product_id The USB product ID + * @param product_release The device release number + */ + USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release); + + /** + * Cleanup this USBDevice + * + * This USBDevice must be uninitialized when the destructor is + * called or the behavior is undefined. + */ + virtual ~USBDevice(); + + /** + * Initialize this instance + * + * This function must be called before calling + * any other functions of this class, unless specifically + */ + void init(); + + /** + * Power down this instance + * + * Disable interrupts and stop sending events. + * This method can be used for temporary power-saving; This call can allow + * USB to be temporarily disabled to permit power saving. + * However, it is up to the user to make sure all the + * transfers have concluded (for example when USB power is lost). + * USBDevice::connect can be used to resume USB operation. + */ + void deinit(); + + /** + * Check if the device is configured + * + * @returns true if configured, false otherwise + */ + bool configured(); + + /** + * Connect a device + * This method can also be used to resume USB operation when USB power is + * detected after it was suspended via USBDevice::deinit. + */ + void connect(); + + /** + * Disconnect a device + */ + void disconnect(); + + /** + * Enable the start of frame interrupt + * + * Call USBDevice::callback_sof on every frame. + */ + void sof_enable(); + + /** + * Disable the start of frame interrupt + * + * Stop calling USBDevice::callback_sof. + */ + void sof_disable(); + + /** + * Add an endpoint + * + * @param endpoint Endpoint to enable + * @param max_packet Maximum size of a packet which can be sent or received on this endpoint + * @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO + * @param callback Method pointer to be called when a packet is transferred + * @returns true if successful, false otherwise + */ + bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, mbed::Callback callback = nullptr); + + /** + * Add an endpoint + * + * @param endpoint Endpoint to enable + * @param max_packet Maximum size of a packet which can be sent or received on this endpoint + * @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO + * @param callback Method pointer to be called when a packet is transferred + * @returns true if successful, false otherwise + */ + template + bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, void (T::*callback)()) + { + return endpoint_add(endpoint, max_packet, type, mbed::callback(this, static_cast(callback))); + } + + /** + * Remove an endpoint + * + * @param endpoint Endpoint to disable + * @note This endpoint must already have been setup with endpoint_add + */ + void endpoint_remove(usb_ep_t endpoint); + + /** + * Remove all non-zero endpoints + */ + void endpoint_remove_all(); + + /** + * Stall an endpoint + * + * If there is an ongoing transfer on this endpoint then it will + * be aborted. + * + * @param endpoint Endpoint to stall + * @note You cannot stall endpoint 0 with this function + * @note This endpoint must already have been setup with endpoint_add + */ + void endpoint_stall(usb_ep_t endpoint); + + /** + * Un-stall an endpoint + * + * Un-stalling an endpoint resets data toggle back to DATA0. + * Additionally, if there is an ongoing transfer on this endpoint + * it will be aborted. + * + * @param endpoint Endpoint to un-stall + * @note This endpoint must already have been setup with endpoint_add + */ + void endpoint_unstall(usb_ep_t endpoint); + + /** + * Get the current maximum size for this endpoint + * + * Return the currently configured maximum packet size, wMaxPacketSize, + * for this endpoint. + * @note This endpoint must already have been setup with endpoint_add + */ + uint32_t endpoint_max_packet_size(usb_ep_t endpoint); + + /** + * Abort the current transfer on this endpoint + * + * @param endpoint endpoint with transfer to abort + * @note This endpoint must already have been setup with endpoint_add + */ + void endpoint_abort(usb_ep_t endpoint); + + /** + * start a read on the given endpoint + * + * Start a read on the given endpoint. The data buffer must remain + * unchanged until the transfer either completes or is aborted. + * + * @param endpoint endpoint to read data from + * @param buffer buffer to fill with read data + * @param size The size of data to read. This must be greater than or equal + * to the max packet size for this endpoint + * @return true if the read was completed, otherwise false + * @note This endpoint must already have been setup with endpoint_add + */ + bool read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size); + + /** + * Get the status of a read + * + * @param endpoint endpoint to get the status of + * @return number of bytes read by this endpoint + */ + uint32_t read_finish(usb_ep_t endpoint); + + /** + * Write a data to the given endpoint + * + * Write data to an endpoint. The data sent must remain unchanged until + * the transfer either completes or is aborted. + * + * @param endpoint endpoint to write data to + * @param buffer data to write + * @param size the size of data to send. This must be less than or equal to the + * max packet size of this endpoint + * @note This endpoint must already have been setup with endpoint_add + */ + bool write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size); + + /** + * Get the status of a write + * + * @param endpoint endpoint to get the status of + * @return number of bytes sent by this endpoint + */ + uint32_t write_finish(usb_ep_t endpoint); + + /* + * Get device descriptor. + * + * @returns pointer to the device descriptor + */ + virtual const uint8_t *device_desc(); + + /* + * Get configuration descriptor + * + * @param index descriptor index + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(uint8_t index) = 0; + + /* + * Get string lang id descriptor + * + * @return pointer to the string lang id descriptor + */ + virtual const uint8_t *string_langid_desc(); + + /* + * Get string manufacturer descriptor + * + * @returns pointer to the string manufacturer descriptor + */ + virtual const uint8_t *string_imanufacturer_desc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string serial descriptor + * + * @returns pointer to the string serial descriptor + */ + virtual const uint8_t *string_iserial_desc(); + + /* + * Get string configuration descriptor + * + * @returns pointer to the string configuration descriptor + */ + virtual const uint8_t *string_iconfiguration_desc(); + + /* + * Get string interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get the length of the report descriptor + * + * @returns length of the report descriptor + */ + virtual uint16_t report_desc_dength() + { + return 0; + }; + +protected: + + /** + * Called by USBDevice layer on power state change. + * + * @param powered true if device is powered, false otherwise + * + * Warning: Called in ISR context + */ + virtual void callback_power(bool powered) + { + + } + + /** + * Called by USBDevice layer on each new USB frame. + * + * Callbacks are enabled and disabled by calling sof_enable + * and sof_disable. + * + * @param frame_number The current frame number + * + * Warning: Called in ISR context + */ + virtual void callback_sof(int frame_number) + { + + } + + /** + * 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() + { + + } + + /** + * 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) = 0; + + /** + * Called by USBDevice on Endpoint0 request. + * + * This is used to handle extensions to standard requests + * and class specific requests. The function complete_request + * must be always be called in response to this callback. + * + * Warning: Called in ISR context + */ + virtual void callback_request(const setup_packet_t *setup) = 0; + + /** + * Called to complete the setup stage of a callback request + * + * Possible options that can be passed as a result are: + * - Receive - Start the data OUT phase of this control transfer + * - Send - Start the data IN phase of this control transfer + * - Success - Operation was a success so start the status phase + * - Failure - Operation failed or is unsupported so send a stall + * - PassThrough - Pass on the request for standard processing + * + * @param result The result of the setup phase. + * @param data Buffer to send or receive if the result is Send or Receive + * @param size Size to transfer if the result is Send or Receive + */ + void complete_request(RequestResult result, uint8_t *data = NULL, uint32_t size = 0); + + /** + * Called by USBDevice on data stage completion + * + * The function complete_request_xfer_done must be always be called + * in response to this callback. + * + * @param setup Setup packet of the current request + * @param aborted false if the operation was aborted, true otherwise + * + * Warning: Called in ISR context + */ + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted) = 0; + + /** + * Called to complete the data stage of a callback request + * + * @param success true if the operation was successful, false otherwise + */ + void complete_request_xfer_done(bool success); + + /* + * Called by USBDevice layer in response to set_configuration. + * + * Upon reception of this command endpoints of the previous configuration + * 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_configuration(uint8_t configuration) = 0; + + /** + * Called to complete a set configuration command + * + * @param success true if the configuration was set, false otherwise + */ + void complete_set_configuration(bool success); + + /* + * 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) = 0; + + /** + * Called to complete a set interface command + * + * @param success true if the interface was set, false otherwise + */ + void complete_set_interface(bool success); + + /** + * Find a descriptor type inside the configuration descriptor + * + * @param descriptor_type Type of descriptor to find + * @param index Configuration descriptor index ( 0 if only one configuration present ) + * @return A descriptor of the given type or NULL if none were found + */ + uint8_t *find_descriptor(uint8_t descriptor_type, uint8_t index = 0); + + /** + * Get the endpoint table of this device + * + * @return Endpoint table of the USBPhy attached to this USBDevice + */ + const usb_ep_table_t *endpoint_table(); + + /** + * Callback called to indicate the USB processing needs to be done + */ + virtual void start_process(); + + /** + * Acquire exclusive access to this instance USBDevice + */ + virtual void lock(); + + /** + * Release exclusive access to this instance USBDevice + */ + virtual void unlock(); + + /** + * Assert that the current thread of execution holds the lock + * + */ + virtual void assert_locked(); + + uint16_t vendor_id; + uint16_t product_id; + uint16_t product_release; + uint8_t device_descriptor[18]; + +private: + // USBPhyEvents + virtual void power(bool powered); + virtual void suspend(bool suspended); + virtual void sof(int frame_number); + virtual void reset(); + virtual void ep0_setup(); + virtual void ep0_out(); + virtual void ep0_in(); + virtual void out(usb_ep_t endpoint); + virtual void in(usb_ep_t endpoint); + + bool _request_get_descriptor(); + bool _control_out(); + bool _control_in(); + bool _request_set_address(); + bool _request_set_configuration(); + bool _request_set_feature(); + bool _request_clear_feature(); + bool _request_get_status(); + bool _request_setup(); + void _control_setup(); + void _control_abort(); + void _control_abort_start(); + void _control_setup_continue(); + void _decode_setup_packet(uint8_t *data, setup_packet_t *packet); + bool _request_get_configuration(); + bool _request_get_interface(); + bool _request_set_interface(); + void _change_state(DeviceState state); + void _run_later(void (USBDevice::*function)()); + + void _complete_request(); + void _complete_request_xfer_done(); + void _complete_set_configuration(); + void _complete_set_interface(); + + struct endpoint_info_t { + mbed::Callback callback; + uint16_t max_packet_size; + uint16_t transfer_size; + uint8_t flags; + uint8_t pending; + }; + + struct usb_device_t { + volatile DeviceState state; + uint8_t configuration; + bool suspended; + }; + + enum ControlState { + Setup, + DataOut, + DataIn, + Status + }; + + enum UserCallback { + None, + Request, + RequestXferDone, + SetConfiguration, + SetInterface + }; + + struct complete_request_t { + RequestResult result; + uint8_t *data; + uint32_t size; + }; + + union complete_args_t { + complete_request_t request; + bool status; + }; + + struct control_transfer_t { + setup_packet_t setup; + uint8_t *ptr; + uint32_t remaining; + uint8_t direction; + bool zlp; + bool notify; + ControlState stage; + UserCallback user_callback; + complete_args_t args; + }; + + endpoint_info_t _endpoint_info[32 - 2]; + + USBPhy *_phy; + bool _initialized; + bool _connected; + bool _endpoint_add_remove_allowed; + control_transfer_t _transfer; + usb_device_t _device; + uint32_t _max_packet_size_ep0; + void (USBDevice::*_post_process)(); + + bool _setup_ready; + bool _abort_control; + + uint16_t _current_interface; + uint8_t _current_alternate; + uint32_t _locked; +}; + +/** @}*/ + +#endif diff --git a/drivers/usb/include/usb/internal/USBDevice_Types.h b/drivers/usb/include/usb/internal/USBDevice_Types.h new file mode 100644 index 0000000..488f61a --- /dev/null +++ b/drivers/usb/include/usb/internal/USBDevice_Types.h @@ -0,0 +1,53 @@ +/* + * 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 USBDEVICE_TYPES_H +#define USBDEVICE_TYPES_H + +/* Standard requests */ +#define GET_STATUS (0) +#define CLEAR_FEATURE (1) +#define SET_FEATURE (3) +#define SET_ADDRESS (5) +#define GET_DESCRIPTOR (6) +#define SET_DESCRIPTOR (7) +#define GET_CONFIGURATION (8) +#define SET_CONFIGURATION (9) +#define GET_INTERFACE (10) +#define SET_INTERFACE (11) + +/* bmRequestType.dataTransferDirection */ +#define HOST_TO_DEVICE (0) +#define DEVICE_TO_HOST (1) + +/* bmRequestType.Type*/ +#define STANDARD_TYPE (0) +#define CLASS_TYPE (1) +#define VENDOR_TYPE (2) +#define RESERVED_TYPE (3) + +/* bmRequestType.Recipient */ +#define DEVICE_RECIPIENT (0) +#define INTERFACE_RECIPIENT (1) +#define ENDPOINT_RECIPIENT (2) +#define OTHER_RECIPIENT (3) + +/* Descriptors */ +#define DESCRIPTOR_TYPE(wValue) (wValue >> 8) +#define DESCRIPTOR_INDEX(wValue) (wValue & 0xff) + +#endif diff --git a/drivers/usb/include/usb/internal/USBHID_Types.h b/drivers/usb/include/usb/internal/USBHID_Types.h new file mode 100644 index 0000000..9a8d9c8 --- /dev/null +++ b/drivers/usb/include/usb/internal/USBHID_Types.h @@ -0,0 +1,93 @@ +/* + * 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 USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_SUBCLASS_BOOT (1) +#define HID_PROTOCOL_NONE (0) +#define HID_PROTOCOL_KEYBOARD (1) +#define HID_PROTOCOL_MOUSE (2) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/drivers/usb/mbed_lib.json b/drivers/usb/mbed_lib.json new file mode 100644 index 0000000..32a28d4 --- /dev/null +++ b/drivers/usb/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "drivers-usb" +} diff --git a/drivers/usb/source/AsyncOp.cpp b/drivers/usb/source/AsyncOp.cpp new file mode 100644 index 0000000..85ac553 --- /dev/null +++ b/drivers/usb/source/AsyncOp.cpp @@ -0,0 +1,146 @@ +/* + * 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 "AsyncOp.h" +#include "mbed_critical.h" +#include "mbed_assert.h" + +using namespace rtos; + +AsyncOp::AsyncOp(): + _list(NULL), _wait(NULL), _aborted(false), _timeout(false) +{ + +} + +AsyncOp::AsyncOp(mbed::Callback &callback): + _list(NULL), _wait(NULL), _aborted(false), _timeout(false) +{ + _callback = callback; +} + +AsyncOp::~AsyncOp() +{ + MBED_ASSERT(_list == NULL); +} + +void AsyncOp::wait(rtos::Mutex *host_mutex, rtos::Kernel::Clock::duration_u32 rel_time) +{ + // Optimization so semaphore is only created if necessary + core_util_critical_section_enter(); + bool done = _list == NULL; + core_util_critical_section_exit(); + if (done) { + return; + } + + // Construct semaphore to wait on + Semaphore sem(0); + + core_util_critical_section_enter(); + done = _list == NULL; + // Wait is only allowed to be called from one thread + MBED_ASSERT(_wait == NULL); + _wait = &sem; + core_util_critical_section_exit(); + + if (done) { + // Operation was signaled before semaphore was set + return; + } + + if (sem.try_acquire_for(rel_time)) { + // Operation completion signaled semaphore + return; + } + + _host_lock(host_mutex); + _abort(true); + _host_unlock(host_mutex); +} + +void AsyncOp::abort() +{ + // Host lock must be held + + _abort(false); +} + +void AsyncOp::complete() +{ + core_util_critical_section_enter(); + + mbed::Callback cb = _callback; + _callback = nullptr; + _list = nullptr; + if (_wait != nullptr) { + _wait->release(); + } + + core_util_critical_section_exit(); + + if (cb) { + cb(); + } +} + +bool AsyncOp::timeout() +{ + core_util_critical_section_enter(); + + bool ret = _timeout; + + core_util_critical_section_exit(); + return ret; +} + +void AsyncOp::_abort(bool timeout) +{ + // host lock must be held + + core_util_critical_section_enter(); + OperationListBase *list = _list; + if (list) { + _callback = nullptr; + _aborted = true; + _wait = nullptr; + _timeout = timeout; + _list = nullptr; + } + core_util_critical_section_exit(); + if (list) { + list->remove(this); + } +} + +void AsyncOp::_host_lock(rtos::Mutex *host_mutex) +{ + if (host_mutex) { + host_mutex->lock(); + } else { + core_util_critical_section_enter(); + } +} + +void AsyncOp::_host_unlock(rtos::Mutex *host_mutex) +{ + if (host_mutex) { + host_mutex->unlock(); + } else { + core_util_critical_section_exit(); + } +} diff --git a/drivers/usb/source/ByteBuffer.cpp b/drivers/usb/source/ByteBuffer.cpp new file mode 100644 index 0000000..87ee3db --- /dev/null +++ b/drivers/usb/source/ByteBuffer.cpp @@ -0,0 +1,151 @@ +/* + * 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 "ByteBuffer.h" +#include "mbed_assert.h" +#include + +ByteBuffer::ByteBuffer(uint32_t size): _head(0), _tail(0), _size(0), _buf(NULL) +{ + resize(_size); +} + +ByteBuffer::~ByteBuffer() +{ + delete[] _buf; + _buf = 0; +} + +void ByteBuffer::resize(uint32_t size) +{ + delete[] _buf; + _head = 0; + _tail = 0; + _size = size + 1; + _buf = new uint8_t[_size](); +} + +void ByteBuffer::push(uint8_t data) +{ + _buf[_tail] = data; + _tail++; + if (_tail >= _size) { + _tail -= _size; + } + // Overflow not allowed + MBED_ASSERT(_head != _tail); +} + +void ByteBuffer::write(uint8_t *data, uint32_t size) +{ + MBED_ASSERT(size <= free()); + + if (size == 0) { + return; + } + + uint32_t new_tail = _tail + size; + if (new_tail >= _size) { + new_tail -= _size; + } + + // Perform first memcpy + uint32_t until_end = _size - _tail; + uint32_t copy_size = until_end < size ? until_end : size; + memcpy(_buf + _tail, data, copy_size); + data += copy_size; + size -= copy_size; + + // Perform second memcpy + if (size > 0) { + memcpy(_buf, data, size); + } + + // Update tail + _tail = new_tail; +} + +uint8_t ByteBuffer::pop() +{ + // Underflow not allowed + MBED_ASSERT(_head != _tail); + uint8_t val = _buf[_head]; + _head++; + if (_head >= _size) { + _head -= _size; + } + return val; +} + +void ByteBuffer::read(uint8_t *data, uint32_t size) +{ + MBED_ASSERT(size <= ByteBuffer::size()); + + if (size == 0) { + return; + } + + uint32_t new_head = _head + size; + if (new_head >= _size) { + new_head -= _size; + } + + // Perform first memcpy + uint32_t until_end = _size - _head; + uint32_t copy_size = until_end < size ? until_end : size; + memcpy(data, _buf + _head, copy_size); + data += copy_size; + size -= copy_size; + + // Perform second memcpy + if (size > 0) { + memcpy(data, _buf, size); + } + + // Update head + _head = new_head; +} + +uint32_t ByteBuffer::size() +{ + uint32_t size; + if (_tail < _head) { + size = _size + _tail - _head; + } else { + size = _tail - _head; + } + return size; +} + +uint32_t ByteBuffer::free() +{ + return _size - size() - 1; +} + +bool ByteBuffer::full() +{ + uint32_t next = _tail + 1; + if (next >= _size) { + next -= _size; + } + return next == _head; +} + +bool ByteBuffer::empty() +{ + return _head == _tail; +} diff --git a/drivers/usb/source/EndpointResolver.cpp b/drivers/usb/source/EndpointResolver.cpp new file mode 100644 index 0000000..90f549b --- /dev/null +++ b/drivers/usb/source/EndpointResolver.cpp @@ -0,0 +1,132 @@ +/* + * 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 "EndpointResolver.h" + +static uint32_t logical_to_index(uint32_t logical, bool in_not_out) +{ + return (logical << 1) | (in_not_out ? 1 : 0); +} + +static uint32_t index_to_logical(uint32_t index) +{ + return index >> 1; +} + + +EndpointResolver::EndpointResolver(const usb_ep_table_t *table) : _table(table), _cost(0), _used(0), _valid(true) +{ + // Do nothing +} + +EndpointResolver::~EndpointResolver() +{ + // Do nothing +} + +void EndpointResolver::endpoint_ctrl(uint32_t size) +{ + endpoint_in(USB_EP_TYPE_CTRL, size); + endpoint_out(USB_EP_TYPE_CTRL, size); +} + +usb_ep_t EndpointResolver::next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size) +{ + int index = next_index(type, in_not_out); + if (index < 0) { + _valid = false; + return 0; + } + + const usb_ep_entry_t &entry = _table->table[index_to_logical(index)]; + _cost += entry.base_cost + entry.byte_cost * size; + _used |= 1 << index; + + return index_to_endpoint(index); + +} +usb_ep_t EndpointResolver::endpoint_in(usb_ep_type_t type, uint32_t size) +{ + return next_free_endpoint(true, type, size); +} + +usb_ep_t EndpointResolver::endpoint_out(usb_ep_type_t type, uint32_t size) +{ + return next_free_endpoint(false, type, size); +} + +bool EndpointResolver::valid() +{ + return _valid && (_cost <= _table->resources); +} + +void EndpointResolver::reset() +{ + _cost = 0; + _used = 0; + _valid = true; +} + +usb_ep_t EndpointResolver::index_to_endpoint(int index) +{ + return index_to_logical(index) | ((index & 1) ? 0x80 : 0); +} + +int EndpointResolver::next_index(usb_ep_type_t type, bool in_not_out) +{ + for (int logical = 0; logical < (int)(sizeof(_table->table) / sizeof(_table->table[0])); logical++) { + uint32_t index = logical_to_index(logical, in_not_out); + uint32_t other = logical_to_index(logical, !in_not_out); + const usb_ep_entry_t &entry = _table->table[logical]; + + usb_ep_attr_t dir = entry.attributes & USB_EP_ATTR_DIR_MASK; + bool in_allowed = dir != USB_EP_ATTR_DIR_OUT; + bool out_allowed = dir != USB_EP_ATTR_DIR_IN; + bool shared = dir == USB_EP_ATTR_DIR_IN_OR_OUT; + + if (!(entry.attributes & (1 << type))) { + // This type is not supported + continue; + } + + if (in_not_out && !in_allowed) { + // In endpoint not supported + continue; + } + + if (!in_not_out && !out_allowed) { + // Out endpoint not supported + continue; + } + + if (_used & (1 << index)) { + // This endpoint is in use + continue; + } + + if (shared && (_used & (1 << other))) { + // This endpoint can only be one direction at a time and is in + // use by the other direction + continue; + } + + return index; + } + + // Not found + return -1; +} diff --git a/drivers/usb/source/LinkedListBase.cpp b/drivers/usb/source/LinkedListBase.cpp new file mode 100644 index 0000000..597b395 --- /dev/null +++ b/drivers/usb/source/LinkedListBase.cpp @@ -0,0 +1,85 @@ +/* + * 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 "LinkedList.h" +#include "LinkEntry.h" +#include "mbed_assert.h" + +LinkedListBase::LinkedListBase(): _head(0), _tail(0) +{ + +} + +LinkedListBase::~LinkedListBase() +{ + +} + +LinkEntry *LinkedListBase::head() +{ + return _head; +} + +void LinkedListBase::enqueue(LinkEntry *entry) +{ + entry->_next = NULL; + if (_tail == NULL) { + _head = entry; + } else { + _tail->_next = entry; + } + _tail = entry; +} + +LinkEntry *LinkedListBase::dequeue() +{ + if (_head == NULL) { + return NULL; + } + if (_head->_next == NULL) { + _tail = NULL; + } + LinkEntry *entry = _head; + _head = _head->_next; + entry->_next = NULL; + return entry; +} + +void LinkedListBase::remove(LinkEntry *entry) +{ + LinkEntry *prev = NULL; + LinkEntry *cur = _head; + while (cur != entry) { + if (cur == NULL) { + // Element is not in the list + return; + } + prev = cur; + cur = cur->_next; + } + + if (prev != NULL) { + prev->_next = entry->_next; + } + if (entry == _head) { + _head = entry->_next; + } + if (entry == _tail) { + _tail = prev; + } + entry->_next = NULL; +} diff --git a/drivers/usb/source/OperationListBase.cpp b/drivers/usb/source/OperationListBase.cpp new file mode 100644 index 0000000..0cbcac6 --- /dev/null +++ b/drivers/usb/source/OperationListBase.cpp @@ -0,0 +1,88 @@ +/* + * 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 "OperationListBase.h" +#include "AsyncOp.h" +#include "mbed_assert.h" + +OperationListBase::OperationListBase() +{ + +} + +OperationListBase::~OperationListBase() +{ + remove_all(); +} + +bool OperationListBase::empty() +{ + return _list.head() == NULL; +} + +void OperationListBase::add(AsyncOp *op) +{ + bool was_empty = _list.head() == NULL; + op->_list = this; + _list.enqueue(op); + if (was_empty) { + process(); + } +} + +void OperationListBase::process() +{ + while (true) { + AsyncOp *op = static_cast(_list.head()); + if (op == NULL) { + // List empty, nothing left to do + break; + } + if (!op->process()) { + // Processing is in progress + break; + } + _list.dequeue(); + op->complete(); + } +} + +void OperationListBase::remove(AsyncOp *op) +{ + bool head = _list.head() == op; + _list.remove(op); + if (head) { + process(); + } +} + +AsyncOp *OperationListBase::dequeue_raw() +{ + return static_cast(_list.dequeue()); +} + +void OperationListBase::remove_all() +{ + while (true) { + AsyncOp *op = static_cast(_list.head()); + if (op == NULL) { + // List empty, nothing left to do + break; + } + op->complete(); + } +} diff --git a/drivers/usb/source/PolledQueue.cpp b/drivers/usb/source/PolledQueue.cpp new file mode 100644 index 0000000..3f8cd9d --- /dev/null +++ b/drivers/usb/source/PolledQueue.cpp @@ -0,0 +1,93 @@ +/* + * 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 "usb/internal/PolledQueue.h" + +#include "events/mbed_events.h" +#include "platform/Callback.h" + + +PolledQueue::PolledQueue(mbed::Callback cb): _cb(cb) +{ + +} + +PolledQueue::~PolledQueue() +{ + +} + +void PolledQueue::dispatch() +{ + core_util_critical_section_enter(); + uint64_t buf[MBED_MAX_TASK_SIZE / sizeof(uint64_t)]; + + while (true) { + + // Atomically dequeue the task and copy the callback + TaskBase *task = _list.dequeue(); + if (!task) { + break; + } + MBED_ASSERT(sizeof(buf) >= task_size(task)); + TaskBase::run_callback_t callback = task_start(task, (uint8_t *)buf, sizeof(buf)); + + // Run the callback outside the critical section + core_util_critical_section_exit(); + callback((uint8_t *)buf); + core_util_critical_section_enter(); + + // Finish + task_finish(task); + task = NULL; + + } + + core_util_critical_section_exit(); +} + +void PolledQueue::attach(mbed::Callback cb) +{ + core_util_critical_section_enter(); + + _cb = cb; + + core_util_critical_section_exit(); +} + +void PolledQueue::post(TaskBase *task) +{ + core_util_critical_section_enter(); + + bool empty = _list.head() == NULL; + _list.remove(task); + _list.enqueue(task); + if (empty && _cb) { + _cb(); + } + + core_util_critical_section_exit(); +} + +void PolledQueue::cancel(TaskBase *task) +{ + core_util_critical_section_enter(); + + _list.remove(task); + + core_util_critical_section_exit(); +} diff --git a/drivers/usb/source/TaskBase.cpp b/drivers/usb/source/TaskBase.cpp new file mode 100644 index 0000000..a8292ed --- /dev/null +++ b/drivers/usb/source/TaskBase.cpp @@ -0,0 +1,152 @@ +/* + * 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 "usb/internal/TaskBase.h" +#include "usb/internal/TaskQueue.h" +#include "events/mbed_events.h" +#include "rtos/Semaphore.h" +#include "platform/mbed_critical.h" + +TaskBase::TaskBase(TaskQueue *q) + : _queue(q), _posted(false), _start_count(0), _flush_sem(NULL) +{ + +} + +TaskBase::~TaskBase() +{ + cancel(); + wait(); +} + +void TaskBase::set(TaskQueue *q) +{ + core_util_critical_section_enter(); + + // Cannot set the queue when it has been posted but has not been finished + MBED_ASSERT(!_posted); + _queue = q; + + core_util_critical_section_exit(); +} + +void TaskBase::cancel() +{ + core_util_critical_section_enter(); + + if (_posted) { + _queue->cancel(this); + _posted = false; + _wake_check(); + } + + core_util_critical_section_exit(); +} + +void TaskBase::wait() +{ + // Fast path check for finished + core_util_critical_section_enter(); + if (finished()) { + core_util_critical_section_exit(); + return; + } + core_util_critical_section_exit(); + + rtos::Semaphore sem; + + // If the event is in-flight then wait for it to complete + core_util_critical_section_enter(); + if (finished()) { + // This element has been flushed from the queue + core_util_critical_section_exit(); + return; + } + _flush_sem = &sem; + core_util_critical_section_exit(); + + sem.acquire(); +} + +bool TaskBase::ready() +{ + core_util_critical_section_enter(); + + bool is_ready = !_posted; + + core_util_critical_section_exit(); + return is_ready; +} + +bool TaskBase::finished() +{ + core_util_critical_section_enter(); + + bool is_finished = !_posted && (_start_count == 0); + + core_util_critical_section_exit(); + return is_finished; +} + +void TaskBase::finish() +{ + // Nothing to do +} + +void TaskBase::post() +{ + core_util_critical_section_enter(); + + MBED_ASSERT(_queue); + if (_queue) { + MBED_ASSERT(!_posted); + _queue->post(this); + _posted = true; + } + + core_util_critical_section_exit(); +} + +TaskBase::run_callback_t TaskBase::_start(void *buffer, uint32_t size) +{ + // Each call to _start must result in a call to _finish + MBED_ASSERT(_start_count < 0xFFFF); + _start_count++; + _posted = false; + + return start(buffer, size); +} + +void TaskBase::_finish() +{ + // Each call to _finish must be preceded by a call to _start + MBED_ASSERT(_start_count > 0); + _start_count--; + _wake_check(); + finish(); +} + +void TaskBase::_wake_check() +{ + if (!finished()) { + return; + } + if (_flush_sem) { + _flush_sem->release(); + _flush_sem = NULL; + } +} diff --git a/drivers/usb/source/USBAudio.cpp b/drivers/usb/source/USBAudio.cpp new file mode 100644 index 0000000..dd29b21 --- /dev/null +++ b/drivers/usb/source/USBAudio.cpp @@ -0,0 +1,1001 @@ +/* + * 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 "USBAudio.h" +#include "USBAudio_Types.h" +#include "EndpointResolver.h" +#include "usb_phy_api.h" + +#define SAMPLE_SIZE 2 +#define XFER_FREQUENCY_HZ 1000 +#define WRITE_READY_UNBLOCK (1 << 0) +#define READ_READY_UNBLOCK (1 << 1) + +class USBAudio::AsyncWrite: public AsyncOp { +public: + AsyncWrite(USBAudio *audio, uint8_t *buf, uint32_t size): + audio(audio), tx_buf(buf), tx_size(size), result(false) + { + + } + + virtual ~AsyncWrite() + { + + } + + virtual bool process() + { + if (audio->_tx_state != Opened) { + result = false; + return true; + } + + uint32_t actual_size = 0; + audio->write_nb(tx_buf, tx_size, &actual_size); + tx_size -= actual_size; + tx_buf += actual_size; + if (tx_size == 0) { + result = true; + return true; + } + + return false; + } + + USBAudio *audio; + uint8_t *tx_buf; + uint32_t tx_size; + bool result; +}; + +class USBAudio::AsyncRead: public AsyncOp { +public: + AsyncRead(USBAudio *audio, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all) + : audio(audio), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false) + { + + } + + virtual ~AsyncRead() + { + + } + + virtual bool process() + { + if (audio->_rx_state != Opened) { + result = false; + return true; + } + + uint32_t actual_size = 0; + audio->read_nb(rx_buf, rx_size, &actual_size); + rx_buf += actual_size; + *rx_actual += actual_size; + rx_size -= actual_size; + if ((!all && *rx_actual > 0) || (rx_size == 0)) { + // Wake thread if request is done + result = true; + return true; + } + + return false; + } + + USBAudio *audio; + uint8_t *rx_buf; + uint32_t rx_size; + uint32_t *rx_actual; + bool all; + bool result; +}; + +static void stub_volume() +{ + +} + +static void stub_handler(USBAudio::AudioEvent event) +{ + (void)event; +} + +USBAudio::USBAudio(bool connect, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBDevice(get_usb_phy(), vendor_id, product_id, product_release) +{ + _init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms); + + // connect or init device + if (connect) { + USBDevice::connect(); + } else { + USBDevice::init(); + } +} + +USBAudio::USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBDevice(phy, vendor_id, product_id, product_release) +{ + _init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms); +} + +void USBAudio::_init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms) +{ + _connected = false; + + _volume = 0; + _mute = 0; + _vol_cur = 0x0080; + _vol_min = 0x0000; + _vol_max = 0x0100; + _vol_res = 0x0004; + + _update_vol = mbed::callback(stub_volume); + _tx_done = mbed::callback(stub_handler); + _rx_done = mbed::callback(stub_handler); + + _rx_overflow = 0; + _tx_underflow = 0; + + _tx_freq = frequency_tx; + _rx_freq = frequency_rx; + + _tx_channel_count = channel_count_tx; + _rx_channel_count = channel_count_rx; + + _tx_idle = true; + _tx_frame_fract = 0; + _tx_whole_frames_per_xfer = _tx_freq / XFER_FREQUENCY_HZ; + _tx_fract_frames_per_xfer = _tx_freq % XFER_FREQUENCY_HZ; + + uint32_t max_frames = _tx_whole_frames_per_xfer + (_tx_fract_frames_per_xfer ? 1 : 0); + _tx_packet_size_max = max_frames * SAMPLE_SIZE * _tx_channel_count; + _rx_packet_size_max = (_rx_freq + 1000 - 1) / 1000 * _rx_channel_count * 2; + + _tx_packet_buf = new uint8_t[_tx_packet_size_max](); + _rx_packet_buf = new uint8_t[_rx_packet_size_max](); + + _tx_queue.resize(buffer_ms * _tx_channel_count * SAMPLE_SIZE * _tx_freq / XFER_FREQUENCY_HZ); + _rx_queue.resize(buffer_ms * _rx_channel_count * SAMPLE_SIZE * _rx_freq / XFER_FREQUENCY_HZ); + + _tx_state = Closed; + _rx_state = Closed; + + EndpointResolver resolver(endpoint_table()); + resolver.endpoint_ctrl(64); + _episo_out = resolver.endpoint_out(USB_EP_TYPE_ISO, _tx_packet_size_max); + _episo_in = resolver.endpoint_in(USB_EP_TYPE_ISO, _rx_packet_size_max); + MBED_ASSERT(resolver.valid()); + + _channel_config_rx = (_rx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R; + _channel_config_tx = (_tx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R; + + _build_configuration_desc(); +} + +USBAudio::~USBAudio() +{ + disconnect(); + deinit(); + + delete[] _tx_packet_buf; + delete[] _rx_packet_buf; +} + +void USBAudio::connect() +{ + lock(); + + if (!_connected) { + USBDevice::connect(); + _connected = true; + _receive_change(Closed); + _send_change(Closed); + } + + unlock(); +} + +void USBAudio::disconnect() +{ + lock(); + + if (_connected) { + _connected = false; + USBDevice::disconnect(); + _receive_change(Powerdown); + _send_change(Powerdown); + } + + unlock(); +} + +bool USBAudio::read(uint8_t *buf, uint32_t size) +{ + lock(); + + uint32_t actual; + AsyncRead read(this, buf, size, &actual, true); + _read_list.add(&read); + + unlock(); + + read.wait(NULL); + return read.result; +} + +void USBAudio::read_nb(uint8_t *buf, uint32_t size, uint32_t *actual) +{ + + lock(); + + uint32_t available = _rx_queue.size(); + uint32_t copy_size = available > size ? size : available; + _rx_queue.read(buf, copy_size); + *actual = copy_size; + + unlock(); +} + +uint32_t USBAudio::read_overflows(bool clear) +{ + lock(); + + uint32_t overflows = _rx_overflow; + if (clear) { + _rx_overflow = 0; + } + + unlock(); + return overflows; +} + + +bool USBAudio::read_ready() +{ + lock(); + + bool ready = _rx_state == Opened; + + unlock(); + + return ready; +} + +void USBAudio::read_wait_ready() +{ + _flags.wait_any(READ_READY_UNBLOCK, osWaitForever, false); +} + +bool USBAudio::write(uint8_t *buf, uint32_t size) +{ + lock(); + + AsyncWrite write(this, buf, size); + _write_list.add(&write); + + unlock(); + + write.wait(NULL); + return write.result; +} + +void USBAudio::write_nb(uint8_t *buf, uint32_t size, uint32_t *actual) +{ + lock(); + + uint32_t available = _tx_queue.free(); + uint32_t copy_size = available > size ? size : available; + _tx_queue.write(buf, copy_size); + *actual = copy_size; + _send_isr_start(); + + unlock(); +} + +uint32_t USBAudio::write_underflows(bool clear) +{ + lock(); + + uint32_t underflows = _tx_underflow; + if (clear) { + _tx_underflow = 0; + } + + unlock(); + return underflows; +} + +bool USBAudio::write_ready() +{ + lock(); + + bool ready = _tx_state == Opened; + + unlock(); + + return ready; +} + +void USBAudio::write_wait_ready() +{ + _flags.wait_any(WRITE_READY_UNBLOCK, osWaitForever, false); +} + + +float USBAudio::get_volume() +{ + lock(); + + float ret = _mute ? 0.0f : _volume; + + unlock(); + return ret; +} + +void USBAudio::attach(mbed::Callback &cb) +{ + lock(); + + _update_vol = cb; + if (!_update_vol) { + _update_vol = stub_volume; + } + + unlock(); +} + +void USBAudio::attach_tx(mbed::Callback &cb) +{ + lock(); + + _tx_done = cb; + if (!_tx_done) { + _tx_done = mbed::callback(stub_handler); + } + + unlock(); +} + +void USBAudio::attach_rx(mbed::Callback &cb) +{ + lock(); + + _rx_done = cb; + if (!_rx_done) { + _rx_done = mbed::callback(stub_handler); + } + + unlock(); +} + +void USBAudio::callback_state_change(DeviceState new_state) +{ + assert_locked(); + + if (_connected && (new_state != Configured)) { + _receive_change(Closed); + _send_change(Closed); + } +} + +void USBAudio::callback_request(const setup_packet_t *setup) +{ + assert_locked(); + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + // Process class-specific requests + if (setup->bmRequestType.Type == CLASS_TYPE) { + + // Feature Unit: Interface = 0, ID = 2 + if (setup->wIndex == 0x0200) { + + // Master Channel + if ((setup->wValue & 0xff) == 0) { + + switch (setup->wValue >> 8) { + case MUTE_CONTROL: + switch (setup->bRequest) { + case REQUEST_GET_CUR: + size = 1; + data = &_mute; + result = Send; + break; + + case REQUEST_SET_CUR: + size = 1; + data = _control_receive; + result = Receive; + break; + default: + break; + } + break; + case VOLUME_CONTROL: + switch (setup->bRequest) { + case REQUEST_GET_CUR: + size = 2; + data = (uint8_t *)&_vol_cur; + result = Send; + break; + case REQUEST_GET_MIN: + size = 2; + data = (uint8_t *)&_vol_min; + result = Send; + break; + case REQUEST_GET_MAX: + size = 2; + data = (uint8_t *)&_vol_max; + result = Send; + break; + case REQUEST_GET_RES: + size = 2; + data = (uint8_t *)&_vol_res; + result = Send; + break; + + case REQUEST_SET_CUR: + size = 2; + data = _control_receive; + result = Receive; + break; + case REQUEST_SET_MIN: + size = 2; + data = _control_receive; + result = Receive; + break; + case REQUEST_SET_MAX: + size = 2; + data = _control_receive; + result = Receive; + break; + case REQUEST_SET_RES: + size = 2; + data = _control_receive; + result = Receive; + break; + } + break; + default: + break; + } + } + } + } + complete_request(result, data, size); +} + +void USBAudio::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + assert_locked(); + + if (aborted) { + complete_request_xfer_done(false); + return; + } + + if (setup->bmRequestType.dataTransferDirection == DEVICE_TO_HOST) { + complete_request_xfer_done(true); + return; + } + + if ((setup->wLength == 1) || (setup->wLength == 2)) { + uint16_t data = (_control_receive[0] << 0) | (_control_receive[1] << 8); + data &= ((setup->wLength == 1) ? 0xFF : 0xFFFF); + switch (setup->wValue >> 8) { + case MUTE_CONTROL: + switch (setup->bRequest) { + case REQUEST_SET_CUR: + _mute = data & 0xff; + _update_vol.call(); + break; + default: + break; + } + break; + case VOLUME_CONTROL: + switch (setup->bRequest) { + case REQUEST_SET_CUR: + _vol_cur = data; + _volume = (float)_vol_cur / (float)_vol_max; + _update_vol.call(); + break; + default: + break; + } + break; + default: + break; + } + complete_request_xfer_done(true); + return; + } + + complete_request_xfer_done(false); +} + +void USBAudio::callback_set_configuration(uint8_t configuration) +{ + assert_locked(); + + bool ret = false; + if (configuration == DEFAULT_CONFIGURATION) { + endpoint_remove_all(); + + // Configure isochronous endpoint + endpoint_add(_episo_out, _rx_packet_size_max, USB_EP_TYPE_ISO, static_cast(&USBAudio::_receive_isr)); + endpoint_add(_episo_in, _tx_packet_size_max, USB_EP_TYPE_ISO, static_cast(&USBAudio::_send_isr)); + + // activate readings on this endpoint + read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max); + ret = true; + } + complete_set_configuration(ret); +} + +void USBAudio::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + + bool ret = false; + if (interface == 0 && alternate == 0) { + ret = true; + } + if (interface == 1 && (alternate == 0 || alternate == 1)) { + _receive_change(alternate == 1 ? Opened : Closed); + ret = true; + } + if (interface == 2 && (alternate == 0 || alternate == 1)) { + _send_change(alternate == 1 ? Opened : Closed); + ret = true; + } + complete_set_interface(ret); +} + +const uint8_t *USBAudio::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + return _config_descriptor; +} + +const uint8_t *USBAudio::string_iinterface_desc() +{ + static const uint8_t stringIinterfaceDescriptor[] = { + 0x0c, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio + }; + return stringIinterfaceDescriptor; +} + +const uint8_t *USBAudio::string_iproduct_desc() +{ + static const uint8_t stringIproductDescriptor[] = { + 0x16, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio + }; + return stringIproductDescriptor; +} + +#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ + + (5 * INTERFACE_DESCRIPTOR_LENGTH) \ + + (1 * CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1) \ + + (2 * INPUT_TERMINAL_DESCRIPTOR_LENGTH) \ + + (1 * FEATURE_UNIT_DESCRIPTOR_LENGTH) \ + + (2 * OUTPUT_TERMINAL_DESCRIPTOR_LENGTH) \ + + (2 * STREAMING_INTERFACE_DESCRIPTOR_LENGTH) \ + + (2 * FORMAT_TYPE_I_DESCRIPTOR_LENGTH) \ + + (2 * (ENDPOINT_DESCRIPTOR_LENGTH + 2)) \ + + (2 * STREAMING_ENDPOINT_DESCRIPTOR_LENGTH) ) + +#define TOTAL_CONTROL_INTF_LENGTH (CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1 + \ + 2*INPUT_TERMINAL_DESCRIPTOR_LENGTH + \ + FEATURE_UNIT_DESCRIPTOR_LENGTH + \ + 2*OUTPUT_TERMINAL_DESCRIPTOR_LENGTH) + +void USBAudio::_build_configuration_desc() +{ + uint8_t config_descriptor_temp[] = { + // Configuration 1 + CONFIGURATION_DESCRIPTOR_LENGTH, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) + MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) + 0x03, // bNumInterfaces + DEFAULT_CONFIGURATION, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // Interface 0, Alternate Setting 0, Audio Control + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints + AUDIO_CLASS, // bInterfaceClass + SUBCLASS_AUDIOCONTROL, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + + // Audio Control Interface + CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1,// bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_HEADER, // bDescriptorSubtype + LSB(0x0100), // bcdADC (LSB) + MSB(0x0100), // bcdADC (MSB) + LSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength + MSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength + 0x02, // bInCollection + 0x01, // baInterfaceNr + 0x02, // baInterfaceNr + + // Audio Input Terminal (Speaker) + INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_INPUT_TERMINAL, // bDescriptorSubtype + 0x01, // bTerminalID + LSB(TERMINAL_USB_STREAMING), // wTerminalType + MSB(TERMINAL_USB_STREAMING), // wTerminalType + 0x00, // bAssocTerminal + _rx_channel_count, // bNrChannels + (uint8_t)(LSB(_channel_config_rx)), // wChannelConfig + (uint8_t)(MSB(_channel_config_rx)), // wChannelConfig + 0x00, // iChannelNames + 0x00, // iTerminal + + // Audio Feature Unit (Speaker) + FEATURE_UNIT_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_FEATURE_UNIT, // bDescriptorSubtype + 0x02, // bUnitID + 0x01, // bSourceID + 0x01, // bControlSize + CONTROL_MUTE | + CONTROL_VOLUME, // bmaControls(0) + 0x00, // bmaControls(1) + 0x00, // iTerminal + + // Audio Output Terminal (Speaker) + OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype + 0x03, // bTerminalID + LSB(TERMINAL_SPEAKER), // wTerminalType + MSB(TERMINAL_SPEAKER), // wTerminalType + 0x00, // bAssocTerminal + 0x02, // bSourceID + 0x00, // iTerminal + + + // Audio Input Terminal (Microphone) + INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_INPUT_TERMINAL, // bDescriptorSubtype + 0x04, // bTerminalID + LSB(TERMINAL_MICROPHONE), // wTerminalType + MSB(TERMINAL_MICROPHONE), // wTerminalType + 0x00, // bAssocTerminal + _tx_channel_count, // bNrChannels + (uint8_t)(LSB(_channel_config_tx)), // wChannelConfig + (uint8_t)(MSB(_channel_config_tx)), // wChannelConfig + 0x00, // iChannelNames + 0x00, // iTerminal + + // Audio Output Terminal (Microphone) + OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype + 0x05, // bTerminalID + LSB(TERMINAL_USB_STREAMING), // wTerminalType + MSB(TERMINAL_USB_STREAMING), // wTerminalType + 0x00, // bAssocTerminal + 0x04, // bSourceID + 0x00, // iTerminal + + + + + + + // Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints + AUDIO_CLASS, // bInterfaceClass + SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // Interface 1, Alternate Setting 1, Audio Streaming - Operational + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x01, // bInterfaceNumber + 0x01, // bAlternateSetting + 0x01, // bNumEndpoints + AUDIO_CLASS, // bInterfaceClass + SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // Audio Streaming Interface + STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + STREAMING_GENERAL, // bDescriptorSubtype + 0x01, // bTerminalLink + 0x00, // bDelay + LSB(FORMAT_PCM), // wFormatTag + MSB(FORMAT_PCM), // wFormatTag + + // Audio Type I Format + FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + STREAMING_FORMAT_TYPE, // bDescriptorSubtype + FORMAT_TYPE_I, // bFormatType + _rx_channel_count, // bNrChannels + 0x02, // bSubFrameSize + 16, // bBitResolution + 0x01, // bSamFreqType + (uint8_t)(LSB(_rx_freq)), // tSamFreq + (uint8_t)((_rx_freq >> 8) & 0xff), // tSamFreq + (uint8_t)((_rx_freq >> 16) & 0xff), // tSamFreq + + // Endpoint - Standard Descriptor + ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _episo_out, // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_rx_packet_size_max)), // wMaxPacketSize + (uint8_t)(MSB(_rx_packet_size_max)), // wMaxPacketSize + 0x01, // bInterval + 0x00, // bRefresh + 0x00, // bSynchAddress + + // Endpoint - Audio Streaming + STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType + ENDPOINT_GENERAL, // bDescriptor + 0x00, // bmAttributes + 0x00, // bLockDelayUnits + LSB(0x0000), // wLockDelay + MSB(0x0000), // wLockDelay + + + // Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x02, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints + AUDIO_CLASS, // bInterfaceClass + SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // Interface 1, Alternate Setting 1, Audio Streaming - Operational + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x02, // bInterfaceNumber + 0x01, // bAlternateSetting + 0x01, // bNumEndpoints + AUDIO_CLASS, // bInterfaceClass + SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // Audio Streaming Interface + STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + SUBCLASS_AUDIOCONTROL, // bDescriptorSubtype + 0x05, // bTerminalLink (output terminal microphone) + 0x01, // bDelay + 0x01, // wFormatTag + 0x00, // wFormatTag + + // Audio Type I Format + FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType + SUBCLASS_AUDIOSTREAMING, // bDescriptorSubtype + FORMAT_TYPE_I, // bFormatType + _tx_channel_count, // bNrChannels + 0x02, // bSubFrameSize + 0x10, // bBitResolution + 0x01, // bSamFreqType + (uint8_t)(LSB(_tx_freq)), // tSamFreq + (uint8_t)((_tx_freq >> 8) & 0xff), // tSamFreq + (uint8_t)((_tx_freq >> 16) & 0xff), // tSamFreq + + // Endpoint - Standard Descriptor + ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _episo_in, // bEndpointAddress + E_ISOCHRONOUS, // bmAttributes + (uint8_t)(LSB(_tx_packet_size_max)), // wMaxPacketSize + (uint8_t)(MSB(_tx_packet_size_max)), // wMaxPacketSize + 0x01, // bInterval + 0x00, // bRefresh + 0x00, // bSynchAddress + + // Endpoint - Audio Streaming + STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType + ENDPOINT_GENERAL, // bDescriptor + 0x00, // bmAttributes + 0x00, // bLockDelayUnits + LSB(0x0000), // wLockDelay + MSB(0x0000), // wLockDelay + }; + + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); + memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor)); +} + +void USBAudio::_receive_change(ChannelState new_state) +{ + assert_locked(); + + ChannelState prev_state = _rx_state; + _rx_state = new_state; + if (prev_state == new_state) { + // no change + return; + } + + if (prev_state == Opened) { + // Leaving the opened state + _read_list.process(); + _rx_done.call(End); + } + if (new_state == Opened) { + // Entering the opened state + _read_list.process(); + _rx_done.call(Start); + } + if (new_state == Closed) { + // Only block if the channel is closed + _flags.clear(READ_READY_UNBLOCK); + } else { + _flags.set(READ_READY_UNBLOCK); + } +} + +void USBAudio::_receive_isr() +{ + assert_locked(); + + uint32_t size = read_finish(_episo_out); + + if (size > _rx_queue.free()) { + _rx_overflow++; + } else { + + // Copy data over + _rx_queue.write(_rx_packet_buf, size); + + // Signal that there is more data available + _read_list.process(); + if (_rx_done) { + _rx_done.call(Transfer); + } + } + + read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max); +} + +void USBAudio::_send_change(ChannelState new_state) +{ + assert_locked(); + + ChannelState prev_state = _tx_state; + _tx_state = new_state; + if (prev_state == new_state) { + // no change + return; + } + + if (prev_state == Opened) { + // Leaving the opened state + _write_list.process(); + _tx_done.call(End); + } + if (new_state == Opened) { + // Entering the opened state + _write_list.process(); + _tx_done.call(Start); + } + if (new_state == Closed) { + // Only block if the channel is closed + _flags.clear(WRITE_READY_UNBLOCK); + } else { + _flags.set(WRITE_READY_UNBLOCK); + } +} +void USBAudio::_send_isr_start() +{ + assert_locked(); + + if (!_tx_idle) { + return; + } + + _send_isr_next_sync(); +} + +void USBAudio::_send_isr_next_sync() +{ + // Compute size to send + uint32_t fames = _tx_whole_frames_per_xfer; + if (_tx_frame_fract >= XFER_FREQUENCY_HZ) { + _tx_frame_fract -= XFER_FREQUENCY_HZ; + fames += 1; + } + uint32_t send_size = fames * _tx_channel_count * 2; + + // Check if this is the initial TX packet + if (_tx_idle && !_tx_queue.full()) { + // Don't start until the TX buffer is full + return; + } + + // Check if this stream was closed + if (_tx_state != Opened) { + _tx_idle = true; + return; + } + + // Check for enough data to send + if (_tx_queue.size() < send_size) { + _tx_underflow++; + _tx_idle = true; + return; + } + + // Copy data over + _tx_queue.read(_tx_packet_buf, send_size); + + // Start the write + write_start(_episo_in, _tx_packet_buf, send_size); + _tx_idle = false; + _tx_frame_fract += _tx_fract_frames_per_xfer; +} + +void USBAudio::_send_isr() +{ + assert_locked(); + + write_finish(_episo_in); + + _send_isr_next_sync(); + + // Signal that there is space for more data + _write_list.process(); + if (_tx_done) { + _tx_done.call(Transfer); + } +} diff --git a/drivers/usb/source/USBCDC.cpp b/drivers/usb/source/USBCDC.cpp new file mode 100644 index 0000000..9cf1ed0 --- /dev/null +++ b/drivers/usb/source/USBCDC.cpp @@ -0,0 +1,644 @@ +/* + * 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 "USBCDC.h" +#include "EndpointResolver.h" +#include "AsyncOp.h" +#include "usb_phy_api.h" + +static const uint8_t cdc_line_coding_default[7] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08}; + +#define DEFAULT_CONFIGURATION (1) + +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 + +// Control Line State bits +#define CLS_DTR (1 << 0) +#define CLS_RTS (1 << 1) + +class USBCDC::AsyncWrite: public AsyncOp { +public: + AsyncWrite(USBCDC *serial, uint8_t *buf, uint32_t size): + serial(serial), tx_buf(buf), tx_size(size), result(false) + { + + } + + virtual ~AsyncWrite() + { + + } + + virtual bool process() + { + if (!serial->_terminal_connected) { + result = false; + return true; + } + + uint32_t actual_size = 0; + serial->send_nb(tx_buf, tx_size, &actual_size, true); + tx_size -= actual_size; + tx_buf += actual_size; + if (tx_size == 0) { + result = true; + return true; + } + + // Start transfer if it hasn't been + serial->_send_isr_start(); + return false; + } + + USBCDC *serial; + uint8_t *tx_buf; + uint32_t tx_size; + bool result; +}; + +class USBCDC::AsyncRead: public AsyncOp { +public: + AsyncRead(USBCDC *serial, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all) + : serial(serial), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false) + { + + } + + virtual ~AsyncRead() + { + + } + + virtual bool process() + { + if (!serial->_terminal_connected) { + result = false; + return true; + } + + uint32_t actual_size = 0; + serial->receive_nb(rx_buf, rx_size, &actual_size); + rx_buf += actual_size; + *rx_actual += actual_size; + rx_size -= actual_size; + if ((!all && *rx_actual > 0) || (rx_size == 0)) { + // Wake thread if request is done + result = true; + return true; + } + + serial->_receive_isr_start(); + return false; + } + + USBCDC *serial; + uint8_t *rx_buf; + uint32_t rx_size; + uint32_t *rx_actual; + bool all; + bool result; +}; + +class USBCDC::AsyncWait: public AsyncOp { +public: + AsyncWait(USBCDC *serial) + : serial(serial) + { + + } + + virtual ~AsyncWait() + { + + } + + virtual bool process() + { + if (serial->_terminal_connected) { + return true; + } + + return false; + } + + USBCDC *serial; +}; + +USBCDC::USBCDC(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) + +{ + _init(); + if (connect_blocking) { + connect(); + wait_ready(); + } else { + init(); + } +} + +USBCDC::USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release) +{ + _init(); +} + +USBCDC::~USBCDC() +{ + deinit(); +} + +void USBCDC::_init() +{ + memcpy(_cdc_line_coding, cdc_line_coding_default, sizeof(_cdc_line_coding)); + + EndpointResolver resolver(endpoint_table()); + resolver.endpoint_ctrl(CDC_MAX_PACKET_SIZE); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); + _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, CDC_MAX_PACKET_SIZE); + MBED_ASSERT(resolver.valid()); + + _terminal_connected = false; + + _tx_in_progress = false; + _tx_buf = _tx_buffer; + _tx_size = 0; + + _rx_in_progress = false; + _rx_buf = _rx_buffer; + _rx_size = 0; +} + +void USBCDC::callback_reset() +{ + assert_locked(); + /* Called in ISR context */ + + _change_terminal_connected(false); +}; + +void USBCDC::callback_state_change(DeviceState new_state) +{ + assert_locked(); + /* Called in ISR context */ + + if (new_state != Configured) { + _change_terminal_connected(false); + } +} + +void USBCDC::callback_request(const setup_packet_t *setup) +{ + assert_locked(); + /* Called in ISR context */ + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + /* Only process class-specific requests */ + if (setup->bmRequestType.Type == CLASS_TYPE) { + switch (setup->bRequest) { + case CDC_GET_LINE_CODING: + result = Send; + data = _cdc_line_coding; + size = 7; + break; + case CDC_SET_LINE_CODING: + result = Receive; + data = _cdc_new_line_coding; + size = 7; + break; + case CDC_SET_CONTROL_LINE_STATE: + if (setup->wValue & CLS_DTR) { + _change_terminal_connected(true); + } else { + _change_terminal_connected(false); + } + result = Success; + break; + default: + result = Failure; + break; + } + } + complete_request(result, data, size); +} + + +void USBCDC::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + assert_locked(); + /* Called in ISR context */ + + if (aborted) { + complete_request_xfer_done(false); + return; + } + + bool success = false; + + /* Process class-specific requests */ + if (setup->bmRequestType.Type == CLASS_TYPE) { + if ((setup->bRequest == CDC_SET_LINE_CODING) && (setup->wLength == 7)) { + if (memcmp(_cdc_line_coding, _cdc_new_line_coding, 7)) { + memcpy(_cdc_line_coding, _cdc_new_line_coding, 7); + + const uint8_t *buf = _cdc_line_coding; + int baud = buf[0] + (buf[1] << 8) + + (buf[2] << 16) + (buf[3] << 24); + int stop = buf[4]; + int bits = buf[6]; + int parity = buf[5]; + + line_coding_changed(baud, bits, parity, stop); + } + success = true; + } + if (setup->bRequest == CDC_GET_LINE_CODING) { + success = true; + } + } + + complete_request_xfer_done(success); +} + +void USBCDC::callback_set_configuration(uint8_t configuration) +{ + assert_locked(); + /* Called in ISR context */ + + bool ret = false; + if (configuration == DEFAULT_CONFIGURATION) { + // Configure endpoints > 0 + endpoint_add(_int_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_INT); + endpoint_add(_bulk_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_send_isr); + endpoint_add(_bulk_out, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_receive_isr); + + read_start(_bulk_out, _rx_buf, sizeof(_rx_buffer)); + _rx_in_progress = true; + + ret = true; + } + + complete_set_configuration(ret); +} + +void USBCDC::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + complete_set_interface(true); +} + +void USBCDC::_change_terminal_connected(bool connected) +{ + assert_locked(); + + _terminal_connected = connected; + if (!_terminal_connected) { + // Abort TX + if (_tx_in_progress) { + endpoint_abort(_bulk_in); + _tx_in_progress = false; + } + _tx_buf = _tx_buffer; + _tx_size = 0; + _tx_list.process(); + MBED_ASSERT(_tx_list.empty()); + + // Abort RX + if (_rx_in_progress) { + endpoint_abort(_bulk_in); + _rx_in_progress = false; + } + _rx_buf = _rx_buffer; + _rx_size = 0; + _rx_list.process(); + MBED_ASSERT(_rx_list.empty()); + + } + _connected_list.process(); +} + +bool USBCDC::ready() +{ + lock(); + + bool ready = _terminal_connected; + + unlock(); + return ready; +} + +void USBCDC::wait_ready() +{ + lock(); + + AsyncWait wait_op(this); + _connected_list.add(&wait_op); + + unlock(); + + wait_op.wait(NULL); +} + +bool USBCDC::send(uint8_t *buffer, uint32_t size) +{ + lock(); + + AsyncWrite write_op(this, buffer, size); + _tx_list.add(&write_op); + + unlock(); + + write_op.wait(NULL); + return write_op.result; +} + +void USBCDC::send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now) +{ + lock(); + + *actual = 0; + if (_terminal_connected && !_tx_in_progress) { + uint32_t free = sizeof(_tx_buffer) - _tx_size; + uint32_t write_size = free > size ? size : free; + if (size > 0) { + memcpy(_tx_buf, buffer, write_size); + } + _tx_size += write_size; + *actual = write_size; + if (now) { + _send_isr_start(); + } + } + + unlock(); +} + +void USBCDC::_send_isr_start() +{ + assert_locked(); + + if (!_tx_in_progress && _tx_size) { + if (USBDevice::write_start(_bulk_in, _tx_buffer, _tx_size)) { + _tx_in_progress = true; + } + } +} + +/* +* Called by when CDC data is sent +* Warning: Called in ISR +*/ +void USBCDC::_send_isr() +{ + assert_locked(); + + write_finish(_bulk_in); + _tx_buf = _tx_buffer; + _tx_size = 0; + _tx_in_progress = false; + + _tx_list.process(); + if (!_tx_in_progress) { + data_tx(); + } +} + +bool USBCDC::receive(uint8_t *buffer, uint32_t size, uint32_t *size_read) +{ + lock(); + + bool read_all = size_read == NULL; + uint32_t size_read_dummy; + uint32_t *size_read_ptr = read_all ? &size_read_dummy : size_read; + *size_read_ptr = 0; + AsyncRead read_op(this, buffer, size, size_read_ptr, read_all); + _rx_list.add(&read_op); + + unlock(); + + read_op.wait(NULL); + return read_op.result; +} + +void USBCDC::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *size_read) +{ + + *size_read = 0; + if (_terminal_connected && !_rx_in_progress) { + // Copy data over + uint32_t copy_size = _rx_size > size ? size : _rx_size; + memcpy(buffer, _rx_buf, copy_size); + *size_read = copy_size; + _rx_buf += copy_size; + _rx_size -= copy_size; + if (_rx_size == 0) { + _receive_isr_start(); + } + } +} + +void USBCDC::_receive_isr_start() +{ + if ((_rx_size == 0) && !_rx_in_progress) { + // Refill the buffer + _rx_in_progress = true; + read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer)); + } +} + +/* +* Called by when CDC data is received +* Warning: Called in ISR +*/ +void USBCDC::_receive_isr() +{ + assert_locked(); + + MBED_ASSERT(_rx_size == 0); + _rx_buf = _rx_buffer; + _rx_size = read_finish(_bulk_out); + _rx_in_progress = false; + _rx_list.process(); + if (!_rx_in_progress) { + data_rx(); + } + +} + +const uint8_t *USBCDC::device_desc() +{ + uint8_t ep0_size = endpoint_max_packet_size(0x00); + uint8_t device_descriptor_temp[] = { + 18, // bLength + 1, // bDescriptorType + 0x10, 0x01, // bcdUSB + 2, // bDeviceClass + 0, // bDeviceSubClass + 0, // 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 + 0x00, 0x01, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1 // 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::string_iinterface_desc() +{ + static const uint8_t stringIinterfaceDescriptor[] = { + 0x08, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, + }; + return stringIinterfaceDescriptor; +} + +const uint8_t *USBCDC::string_iproduct_desc() +{ + static const uint8_t stringIproductDescriptor[] = { + 0x16, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 + }; + return stringIproductDescriptor; +} + + +#define CONFIG1_DESC_SIZE (9+8+9+5+5+4+5+7+9+7+7) + +const uint8_t *USBCDC::configuration_desc(uint8_t index) +{ + uint8_t config_descriptor_temp[] = { + // configuration descriptor + 9, // bLength + 2, // bDescriptorType + LSB(CONFIG1_DESC_SIZE), // wTotalLength + MSB(CONFIG1_DESC_SIZE), + 2, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // IAD to associate the two CDC interfaces + 0x08, // bLength + 0x0b, // bDescriptorType + 0x00, // bFirstInterface + 0x02, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0, // bFunctionProtocol + 0, // iFunction + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x03, // bmCapabilities + 1, // bDataInterface + + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + 0, // bMasterInterface + 1, // bSlaveInterface0 + + // 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) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) + 16, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 1, // bInterfaceNumber + 0, // 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) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // 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) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) + 0 // bInterval + }; + + if (index == 0) { + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); + memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor)); + return _config_descriptor; + } else { + return NULL; + } +} diff --git a/drivers/usb/source/USBCDC_ECM.cpp b/drivers/usb/source/USBCDC_ECM.cpp new file mode 100644 index 0000000..45e97ad --- /dev/null +++ b/drivers/usb/source/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/USBDevice.cpp b/drivers/usb/source/USBDevice.cpp new file mode 100644 index 0000000..6d3cfc8 --- /dev/null +++ b/drivers/usb/source/USBDevice.cpp @@ -0,0 +1,1773 @@ +/* + * 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 "USBDevice.h" +#include "USBDescriptor.h" +#include "usb_phy_api.h" +#include "mbed_assert.h" +#include "platform/mbed_error.h" + +//#define DEBUG + +/* Device status */ +#define DEVICE_STATUS_SELF_POWERED (1U<<0) +#define DEVICE_STATUS_REMOTE_WAKEUP (1U<<1) + +/* Endpoint status */ +#define ENDPOINT_STATUS_HALT (1U<<0) + +/* Standard feature selectors */ +#define DEVICE_REMOTE_WAKEUP (1) +#define ENDPOINT_HALT (0) + +/* Endpoint macros */ +#define EP_INDEXABLE(endpoint) (EP_VALID(endpoint) && !EP_CONTROL(endpoint)) +#define EP_TO_INDEX(endpoint) ((((endpoint & 0xf) << 1) | (endpoint & 0x80 ? 1 : 0)) - 2) +#define INDEX_TO_EP(index) ((usb_ep_t)((((index) >> 1) | (index & 1 ? 0x80 : 0)) + 1)) +#define EP_VALID(endpoint) (((endpoint) & ~0x8F) == 0) +#define EP_CONTROL(endpoint) (((endpoint) & 0xF) == 0) +#define EP_RX(endpoint) ((endpoint) & 0x80) + +/* Other defines */ +#define ENDPOINT_ENABLED (1 << 0) +#define ENDPOINT_STALLED (1 << 1) + +/* The maximum wMaxPacketSize for endpoint 0 */ +#if defined(MAX_PACKET_SIZE_EP0) +#undef MAX_PACKET_SIZE_EP0 +#endif +#define MAX_PACKET_SIZE_EP0 64 + +#define USB_MIN(a, b) ((a) > (b) ? (b) : (a)) + + +bool USBDevice::_request_get_descriptor() +{ + assert_locked(); + + bool success = false; +#ifdef DEBUG + printf("get descr: type: %d\r\n", DESCRIPTOR_TYPE(_transfer.setup.wValue)); +#endif + switch (DESCRIPTOR_TYPE(_transfer.setup.wValue)) { + case DEVICE_DESCRIPTOR: { + if (device_desc() != NULL) { + if ((device_desc()[0] == DEVICE_DESCRIPTOR_LENGTH) \ + && (device_desc()[1] == DEVICE_DESCRIPTOR)) { +#ifdef DEBUG + printf("device descr\r\n"); +#endif + _transfer.remaining = DEVICE_DESCRIPTOR_LENGTH; + _transfer.ptr = (uint8_t *)device_desc(); + _transfer.direction = Send; + success = true; + } + } + break; + } + case CONFIGURATION_DESCRIPTOR: { + const uint8_t idx = DESCRIPTOR_INDEX(_transfer.setup.wValue); + if (configuration_desc(idx) != NULL) { + if ((configuration_desc(idx)[0] == CONFIGURATION_DESCRIPTOR_LENGTH) \ + && (configuration_desc(idx)[1] == CONFIGURATION_DESCRIPTOR)) { +#ifdef DEBUG + printf("conf descr request\r\n"); +#endif + /* Get wTotalLength */ + _transfer.remaining = configuration_desc(idx)[2] \ + | (configuration_desc(idx)[3] << 8); + + _transfer.ptr = (uint8_t *)configuration_desc(idx); + _transfer.direction = Send; + success = true; + } + } + break; + } + case STRING_DESCRIPTOR: { +#ifdef DEBUG + printf("str descriptor\r\n"); +#endif + switch (DESCRIPTOR_INDEX(_transfer.setup.wValue)) { + case STRING_OFFSET_LANGID: +#ifdef DEBUG + printf("1\r\n"); +#endif + _transfer.remaining = string_langid_desc()[0]; + _transfer.ptr = (uint8_t *)string_langid_desc(); + _transfer.direction = Send; + success = true; + break; + case STRING_OFFSET_IMANUFACTURER: +#ifdef DEBUG + printf("2\r\n"); +#endif + _transfer.remaining = string_imanufacturer_desc()[0]; + _transfer.ptr = (uint8_t *)string_imanufacturer_desc(); + _transfer.direction = Send; + success = true; + break; + case STRING_OFFSET_IPRODUCT: +#ifdef DEBUG + printf("3\r\n"); +#endif + _transfer.remaining = string_iproduct_desc()[0]; + _transfer.ptr = (uint8_t *)string_iproduct_desc(); + _transfer.direction = Send; + success = true; + break; + case STRING_OFFSET_ISERIAL: +#ifdef DEBUG + printf("4\r\n"); +#endif + _transfer.remaining = string_iserial_desc()[0]; + _transfer.ptr = (uint8_t *)string_iserial_desc(); + _transfer.direction = Send; + success = true; + break; + case STRING_OFFSET_ICONFIGURATION: +#ifdef DEBUG + printf("5\r\n"); +#endif + _transfer.remaining = string_iconfiguration_desc()[0]; + _transfer.ptr = (uint8_t *)string_iconfiguration_desc(); + _transfer.direction = Send; + success = true; + break; + case STRING_OFFSET_IINTERFACE: +#ifdef DEBUG + printf("6\r\n"); +#endif + _transfer.remaining = string_iinterface_desc()[0]; + _transfer.ptr = (uint8_t *)string_iinterface_desc(); + _transfer.direction = Send; + success = true; + break; + } + break; + } + case INTERFACE_DESCRIPTOR: { +#ifdef DEBUG + printf("interface descr\r\n"); +#endif + break; + } + case ENDPOINT_DESCRIPTOR: { +#ifdef DEBUG + printf("endpoint descr\r\n"); +#endif + /* TODO: Support is optional, not implemented here */ + break; + } + default: { +#ifdef DEBUG + printf("ERROR\r\n"); +#endif + break; + } + } + + return success; +} + +void USBDevice::_decode_setup_packet(uint8_t *data, setup_packet_t *packet) +{ + // No lock needed - stateless function + + /* Fill in the elements of a setup_packet_t structure from raw data */ + packet->bmRequestType.dataTransferDirection = (data[0] & 0x80) >> 7; + packet->bmRequestType.Type = (data[0] & 0x60) >> 5; + packet->bmRequestType.Recipient = data[0] & 0x1f; + packet->bRequest = data[1]; + packet->wValue = (data[2] | (uint16_t)data[3] << 8); + packet->wIndex = (data[4] | (uint16_t)data[5] << 8); + packet->wLength = (data[6] | (uint16_t)data[7] << 8); +} + + +bool USBDevice::_control_out() +{ + assert_locked(); + + /* Control transfer data OUT stage */ + uint32_t packetSize; + + /* Check we should be transferring data OUT */ + if (_transfer.direction != Receive) { + /* for other platforms, count on the HAL to handle this case */ + return false; + } + + /* Read from endpoint */ + packetSize = _phy->ep0_read_result(); + + /* Check if transfer size is valid */ + if (packetSize > _transfer.remaining) { + /* Too big */ + return false; + } + + /* Update transfer */ + _transfer.ptr += packetSize; + _transfer.remaining -= packetSize; + + /* Check if transfer has completed */ + if (_transfer.remaining == 0) { + /* Transfer completed */ + _transfer.user_callback = RequestXferDone; + if (_transfer.notify) { + /* Notify class layer. */ + _transfer.notify = false; + callback_request_xfer_done(&_transfer.setup, false); + } else { + complete_request_xfer_done(true); + } + } else { + _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); + } + + return true; +} + +bool USBDevice::_control_in() +{ + assert_locked(); + + /* Control transfer data IN stage */ + uint32_t packetSize; + + + /* Check we should be transferring data IN */ + if (_transfer.direction != Send) { + return false; + } + + if (_transfer.remaining == 0) { + if (!_transfer.zlp) { + /* Status as already been sent so ignore this IN. */ + return true; + } + /* ZLP will be sent below */ + _transfer.zlp = false; + } + + packetSize = _transfer.remaining; + + if (packetSize > _max_packet_size_ep0) { + packetSize = _max_packet_size_ep0; + } + + /* Write to endpoint */ + _phy->ep0_write(_transfer.ptr, packetSize); + + /* Update transfer */ + _transfer.ptr += packetSize; + _transfer.remaining -= packetSize; + + /* Send status if all the data has been sent + * NOTE - Start the status stage immediately + * after writing the last packet. Do not wait + * for the next IN event, as this can be dropped + * if the ACK by the host is corrupted. + * + * For more info on this see section + * 8.5.3.2 of the USB2.0 specification. + */ + if ((_transfer.remaining == 0) && !_transfer.zlp) { + /* Transfer completed */ + _transfer.user_callback = RequestXferDone; + if (_transfer.notify) { + /* Notify class layer. */ + _transfer.notify = false; + callback_request_xfer_done(&_transfer.setup, false); + } else { + complete_request_xfer_done(true); + } + + + /* Completed */ + return true; + } + + + return true; +} + +void USBDevice::complete_request_xfer_done(bool success) +{ + lock(); + + MBED_ASSERT(_transfer.user_callback == RequestXferDone); + _transfer.args.status = success; + _run_later(&USBDevice::_complete_request_xfer_done); + + unlock(); +} + +void USBDevice::_complete_request_xfer_done() +{ + assert_locked(); + + bool success = _transfer.args.status; + + _transfer.user_callback = None; + if (_abort_control) { + _control_abort(); + return; + } + + if (!success) { + _phy->ep0_stall(); + return; + } + + /* Status stage */ + if (_transfer.stage == DataOut) { + _transfer.stage = Status; + _phy->ep0_write(NULL, 0); + } else if (_transfer.stage == DataIn) { + _transfer.stage = Status; + _phy->ep0_read(NULL, 0); + } +} + +bool USBDevice::_request_set_address() +{ + assert_locked(); + + /* Set the device address */ + _phy->set_address(_transfer.setup.wValue); + + if (_transfer.setup.wValue == 0) { + _change_state(Default); + } else { + _change_state(Address); + } + + return true; +} + +bool USBDevice::_request_set_configuration() +{ + assert_locked(); + + _device.configuration = _transfer.setup.wValue; + /* Set the device configuration */ + if (_device.configuration == 0) { + /* Not configured */ + _phy->unconfigure(); + _change_state(Address); + } else { + _endpoint_add_remove_allowed = true; + _transfer.user_callback = SetConfiguration; + callback_set_configuration(_device.configuration); + } + + return true; +} + +void USBDevice::complete_set_configuration(bool success) +{ + lock(); + + MBED_ASSERT(_transfer.user_callback == SetConfiguration); + _transfer.args.status = success; + _run_later(&USBDevice::_complete_set_configuration); + + unlock(); +} + +void USBDevice::_complete_set_configuration() +{ + assert_locked(); + + bool success = _transfer.args.status; + if ((_abort_control || !success) && !configured()) { + // The set configuration request was aborted or failed so + // reset any endpoints which may have been added. + memset(_endpoint_info, 0, sizeof(_endpoint_info)); + _device.configuration = 0; + _endpoint_add_remove_allowed = false; + } + + + _transfer.user_callback = None; + if (_abort_control) { + _control_abort(); + return; + } + + if (success) { + /* Valid configuration */ + _phy->configure(); + _change_state(Configured); + _control_setup_continue(); + } else { + _phy->ep0_stall(); + return; + } +} + +bool USBDevice::_request_get_configuration() +{ + assert_locked(); + + /* Send the device configuration */ + _transfer.ptr = &_device.configuration; + _transfer.remaining = sizeof(_device.configuration); + _transfer.direction = Send; + return true; +} + +bool USBDevice::_request_get_interface() +{ + assert_locked(); + + /* Return the selected alternate setting for an interface */ + + if (_device.state != Configured) { + return false; + } + + /* Send the alternate setting */ + _transfer.setup.wIndex = _current_interface; + _transfer.ptr = &_current_alternate; + _transfer.remaining = sizeof(_current_alternate); + _transfer.direction = Send; + return true; +} + +bool USBDevice::_request_set_interface() +{ + assert_locked(); + + _transfer.user_callback = SetInterface; + callback_set_interface(_transfer.setup.wIndex, _transfer.setup.wValue); + return true; +} + +void USBDevice::complete_set_interface(bool success) +{ + lock(); + + MBED_ASSERT(_transfer.user_callback == SetInterface); + _transfer.args.status = success; + _run_later(&USBDevice::_complete_set_interface); + + unlock(); +} + +void USBDevice::_complete_set_interface() +{ + assert_locked(); + + bool success = _transfer.args.status; + + _transfer.user_callback = None; + if (_abort_control) { + _control_abort(); + return; + } + + if (success) { + _current_interface = _transfer.setup.wIndex; + _current_alternate = _transfer.setup.wValue; + _control_setup_continue(); + } else { + _phy->ep0_stall(); + return; + } +} + +bool USBDevice::_request_set_feature() +{ + assert_locked(); + bool success = false; + + if (_device.state != Configured) { + /* Endpoint or interface must be zero */ + if (_transfer.setup.wIndex != 0) { + return false; + } + } + + switch (_transfer.setup.bmRequestType.Recipient) { + case DEVICE_RECIPIENT: + /* TODO: Remote wakeup feature not supported */ + break; + case ENDPOINT_RECIPIENT: + if (!EP_INDEXABLE(_transfer.setup.wIndex)) { + break; + } else if (_transfer.setup.wValue == ENDPOINT_HALT) { + endpoint_stall(_transfer.setup.wIndex); + success = true; + } + break; + default: + break; + } + + return success; +} + +bool USBDevice::_request_clear_feature() +{ + assert_locked(); + + bool success = false; + + if (_device.state != Configured) { + /* Endpoint or interface must be zero */ + if (_transfer.setup.wIndex != 0) { + return false; + } + } + + switch (_transfer.setup.bmRequestType.Recipient) { + case DEVICE_RECIPIENT: + /* TODO: Remote wakeup feature not supported */ + break; + case ENDPOINT_RECIPIENT: + if (!EP_INDEXABLE(_transfer.setup.wIndex)) { + break; + } else if (_transfer.setup.wValue == ENDPOINT_HALT) { + endpoint_unstall(_transfer.setup.wIndex); + success = true; + } + break; + default: + break; + } + + return success; +} + +bool USBDevice::_request_get_status() +{ + assert_locked(); + + static uint16_t status; + bool success = false; + + if (_device.state != Configured) { + /* Endpoint or interface must be zero */ + if (_transfer.setup.wIndex != 0) { + return false; + } + } + + switch (_transfer.setup.bmRequestType.Recipient) { + case DEVICE_RECIPIENT: + /* TODO: Currently only supports self powered devices */ + status = DEVICE_STATUS_SELF_POWERED; + success = true; + break; + case INTERFACE_RECIPIENT: + status = 0; + success = true; + break; + case ENDPOINT_RECIPIENT: + if (!EP_VALID(_transfer.setup.wIndex)) { + break; + } else if (EP_CONTROL(_transfer.setup.wIndex)) { + /* Control endpoint can't be halted */ + status = 0; + } else if (_endpoint_info[EP_TO_INDEX(_transfer.setup.wIndex & 0xFF)].flags & ENDPOINT_STALLED) { + status = ENDPOINT_STATUS_HALT; + } else { + status = 0; + } + success = true; + break; + default: + break; + } + + if (success) { + /* Send the status */ + _transfer.ptr = (uint8_t *)&status; /* Assumes little endian */ + _transfer.remaining = sizeof(status); + _transfer.direction = Send; + } + + return success; +} + +bool USBDevice::_request_setup() +{ + assert_locked(); + + bool success = false; + + /* Process standard requests */ + if (_transfer.setup.bmRequestType.Type == STANDARD_TYPE) { + switch (_transfer.setup.bRequest) { + case GET_STATUS: + success = _request_get_status(); + break; + case CLEAR_FEATURE: + success = _request_clear_feature(); + break; + case SET_FEATURE: + success = _request_set_feature(); + break; + case SET_ADDRESS: + success = _request_set_address(); + break; + case GET_DESCRIPTOR: + success = _request_get_descriptor(); + break; + case SET_DESCRIPTOR: + /* TODO: Support is optional, not implemented here */ + success = false; + break; + case GET_CONFIGURATION: + success = _request_get_configuration(); + break; + case SET_CONFIGURATION: + success = _request_set_configuration(); + break; + case GET_INTERFACE: + success = _request_get_interface(); + break; + case SET_INTERFACE: + success = _request_set_interface(); + break; + default: + break; + } + } + + return success; +} + +void USBDevice::_control_setup() +{ + assert_locked(); + + /* Control transfer setup stage */ + uint8_t buffer[MAX_PACKET_SIZE_EP0]; + + _phy->ep0_setup_read_result(buffer, _max_packet_size_ep0); + + /* Initialise control transfer state */ + _decode_setup_packet(buffer, &_transfer.setup); + _transfer.ptr = NULL; + _transfer.remaining = 0; + _transfer.direction = 0; + _transfer.zlp = false; + _transfer.notify = false; + _transfer.stage = Setup; + _transfer.user_callback = Request; + +#ifdef DEBUG + printf("dataTransferDirection: %d\r\nType: %d\r\nRecipient: %d\r\nbRequest: %d\r\nwValue: %d\r\nwIndex: %d\r\nwLength: %d\r\n", _transfer.setup.bmRequestType.dataTransferDirection, + _transfer.setup.bmRequestType.Type, + _transfer.setup.bmRequestType.Recipient, + _transfer.setup.bRequest, + _transfer.setup.wValue, + _transfer.setup.wIndex, + _transfer.setup.wLength); +#endif + + /* Class / vendor specific */ + callback_request(&_transfer.setup); +} + +void USBDevice::complete_request(RequestResult direction, uint8_t *data, uint32_t size) +{ + lock(); + + MBED_ASSERT(_transfer.user_callback == Request); + _transfer.args.request.result = direction; + _transfer.args.request.data = data; + _transfer.args.request.size = size; + _run_later(&USBDevice::_complete_request); + + unlock(); +} + +void USBDevice::_complete_request() +{ + assert_locked(); + + RequestResult direction = _transfer.args.request.result; + uint8_t *data = _transfer.args.request.data; + uint32_t size = _transfer.args.request.size; + + _transfer.user_callback = None; + if (_abort_control) { + if ((direction == Receive) || (direction == Send)) { + _transfer.user_callback = RequestXferDone; + callback_request_xfer_done(&_transfer.setup, true); + } else { + _control_abort(); + } + return; + } + + if (direction == PassThrough) { + /* Standard requests */ + if (!_request_setup()) { + _phy->ep0_stall(); + return; + } + + /* user_callback may be set by _request_setup() */ + if (_transfer.user_callback == None) { + _control_setup_continue(); + } + } else if (direction == Failure) { + _phy->ep0_stall(); + return; + } else { + _transfer.notify = true; + _transfer.remaining = size; + _transfer.ptr = data; + _transfer.direction = direction; + _control_setup_continue(); + } +} + +void USBDevice::_control_abort_start() +{ + assert_locked(); + + _setup_ready = false; + if (_transfer.user_callback == None) { + _control_abort(); + } else { + _abort_control = true; + } +} + +void USBDevice::_control_setup_continue() +{ + assert_locked(); + + /* Check transfer size and direction */ + if (_transfer.setup.wLength > 0) { + if (_transfer.setup.bmRequestType.dataTransferDirection \ + == Send) { + /* IN data stage is required */ + if (_transfer.direction != Send) { + _phy->ep0_stall(); + return; + } + + /* Transfer must be less than or equal to the size */ + /* requested by the host */ + if (_transfer.remaining > _transfer.setup.wLength) { + _transfer.remaining = _transfer.setup.wLength; + } + } else { + + /* OUT data stage is required */ + if (_transfer.direction != Receive) { + _phy->ep0_stall(); + return; + } + + /* Transfer must be equal to the size requested by the host */ + if (_transfer.remaining != _transfer.setup.wLength) { + _phy->ep0_stall(); + return; + } + } + } else { + /* No data stage; transfer size must be zero */ + if (_transfer.remaining != 0) { + _phy->ep0_stall(); + return; + } + } + + /* Data or status stage if applicable */ + if (_transfer.setup.wLength > 0) { + if (_transfer.setup.bmRequestType.dataTransferDirection \ + == Send) { + /* Check if we'll need to send a zero length packet at */ + /* the end of this transfer */ + if (_transfer.setup.wLength > _transfer.remaining) { + /* Device wishes to transfer less than host requested */ + if ((_transfer.remaining % _max_packet_size_ep0) == 0) { + /* Transfer is a multiple of EP0 max packet size */ + _transfer.zlp = true; + } + } + + /* IN stage */ + _transfer.stage = DataIn; + _control_in(); + } else { + /* OUT stage */ + _transfer.stage = DataOut; + _phy->ep0_read(_transfer.ptr, USB_MIN(_transfer.remaining, _max_packet_size_ep0)); + } + } else { + /* Status stage */ + _transfer.stage = Status; + _phy->ep0_write(NULL, 0); + } +} + +void USBDevice::_control_abort() +{ + assert_locked(); + + _abort_control = false; + _transfer.stage = Status; +} + +void USBDevice::reset() +{ + assert_locked(); + + _change_state(Default); + _device.suspended = false; + _control_abort_start(); + + /* Call class / vendor specific busReset function */ + callback_reset(); +} + +void USBDevice::ep0_setup() +{ + assert_locked(); + + if (_device.state < Default) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_NOT_READY + ), + "Device state is \"Powered\" or \"Detached\"" + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + _setup_ready = true; + + /* Endpoint 0 setup event */ + if (_transfer.user_callback == None) { + _control_setup(); + } else { + /* A new setup packet has arrived so abort the + current control transfer */ + _abort_control = true; + } + +} + +void USBDevice::ep0_out() +{ + assert_locked(); + + if (_device.state < Default) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_NOT_READY + ), + "Device state is \"Powered\" or \"Detached\"" + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (_transfer.user_callback != None) { + /* EP0 OUT should not receive data if the stack is waiting + on a user callback for the buffer to fill or status */ +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_NOT_READY + ), + "The stack is waiting on a user callback for the buffer to fill or status." + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (_transfer.stage == Status) { + // No action needed on status stage + return; + } + + /* Endpoint 0 OUT data event */ + if (!_control_out()) { + /* Protocol stall; this will stall both endpoints */ + _phy->ep0_stall(); + return; + } +} + +void USBDevice::ep0_in() +{ + assert_locked(); + + if (_device.state < Default) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_NOT_READY + ), + "Device state is \"Powered\" or \"Detached\"" + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + +#ifdef DEBUG + printf("ep0_in\r\n"); +#endif + if (_transfer.stage == Status) { + // No action needed on status stage + return; + } + + /* Endpoint 0 IN data event */ + if (!_control_in()) { + /* Protocol stall; this will stall both endpoints */ + _phy->ep0_stall(); + return; + } +} + +void USBDevice::out(usb_ep_t endpoint) +{ + assert_locked(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + + MBED_ASSERT(info->pending >= 1); + info->pending -= 1; + if (info->callback) { + info->callback(); + } +} + +void USBDevice::in(usb_ep_t endpoint) +{ + assert_locked(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + + MBED_ASSERT(info->pending >= 1); + info->pending -= 1; + if (info->callback) { + info->callback(); + } +} + +void USBDevice::init() +{ + lock(); + + if (!_initialized) { + this->_phy->init(this); + _max_packet_size_ep0 = this->_phy->ep0_set_max_packet(MAX_PACKET_SIZE_EP0); + _initialized = true; + } + + unlock(); +} + +void USBDevice::deinit() +{ + lock(); + + if (_initialized) { + disconnect(); + this->_phy->deinit(); + _initialized = false; + } + + unlock(); +} + +bool USBDevice::configured() +{ + lock(); + + /* Returns true if device is in the Configured state */ + bool ret = (_device.state == Configured); + + unlock(); + return ret; +} + +void USBDevice::connect() +{ + lock(); + + /* Ensure device has been initialized */ + init(); + + /* Connect device */ + if (!_connected) { + _phy->connect(); + _connected = true; + } + + unlock(); +} + +void USBDevice::disconnect() +{ + lock(); + + /* Disconnect device */ + if (_connected) { + _phy->disconnect(); + _connected = false; + } + + /* Set initial device state */ + if (_device.state > Powered) { + _change_state(Powered); + } + //TODO - remove these? + _device.configuration = 0; + _device.suspended = false; + + unlock(); +} + +void USBDevice::sof_enable() +{ + lock(); + + _phy->sof_enable(); + + unlock(); +} + +void USBDevice::sof_disable() +{ + lock(); + + _phy->sof_disable(); + + unlock(); +} + +bool USBDevice::endpoint_add(usb_ep_t endpoint, uint32_t max_packet_size, usb_ep_type_t type, mbed::Callback callback) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return false; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (!_endpoint_add_remove_allowed) { + unlock(); + return false; + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + MBED_ASSERT(!(info->flags & ENDPOINT_ENABLED)); + MBED_ASSERT(max_packet_size <= 1024); + + bool ret = _phy->endpoint_add(endpoint, max_packet_size, type); + if (ret) { + info->callback = callback; + info->flags |= ENDPOINT_ENABLED; + info->pending = 0; + info->max_packet_size = max_packet_size; + } + + unlock(); + return ret; +} + +void USBDevice::endpoint_remove(usb_ep_t endpoint) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (!_endpoint_add_remove_allowed) { + unlock(); + return; + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + MBED_ASSERT(info->flags & ENDPOINT_ENABLED); + + if (info->pending) { + _phy->endpoint_abort(endpoint); + } + + info->callback = nullptr; + info->flags = 0; + info->pending = 0; + info->max_packet_size = 0; + + _phy->endpoint_remove(endpoint); + + unlock(); +} + +void USBDevice::endpoint_remove_all() +{ + lock(); + + for (uint32_t i = 0; i < sizeof(_endpoint_info) / sizeof(_endpoint_info[0]); i++) { + endpoint_info_t *info = _endpoint_info + i; + if (info->flags & ENDPOINT_ENABLED) { + endpoint_remove(INDEX_TO_EP(i)); + } + } + + unlock(); +} + +void USBDevice::endpoint_stall(usb_ep_t endpoint) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Invalid endpoint is being used + MBED_ASSERT(!configured()); + unlock(); + return; + } + + info->flags |= ENDPOINT_STALLED; + _phy->endpoint_stall(endpoint); + + if (info->pending) { + endpoint_abort(endpoint); + } + + unlock(); +} + +void USBDevice::endpoint_unstall(usb_ep_t endpoint) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Invalid endpoint is being used + MBED_ASSERT(!configured()); + unlock(); + return; + } + + if (info->pending) { + endpoint_abort(endpoint); + } + + info->flags &= ~ENDPOINT_STALLED; + _phy->endpoint_unstall(endpoint); + + unlock(); +} + +uint8_t *USBDevice::find_descriptor(uint8_t descriptorType, uint8_t index) +{ + /* Find a descriptor within the list of descriptors */ + /* following a configuration descriptor. */ + uint16_t wTotalLength; + uint8_t *ptr; + + if (configuration_desc(index) == NULL) { + return NULL; + } + + /* Check this is a configuration descriptor */ + if ((configuration_desc(index)[0] != CONFIGURATION_DESCRIPTOR_LENGTH) \ + || (configuration_desc(index)[1] != CONFIGURATION_DESCRIPTOR)) { + return NULL; + } + + wTotalLength = configuration_desc(index)[2] | (configuration_desc(index)[3] << 8); + + /* Check there are some more descriptors to follow */ + if (wTotalLength <= (CONFIGURATION_DESCRIPTOR_LENGTH + 2)) + /* +2 is for bLength and bDescriptorType of next descriptor */ + { + return NULL; + } + + /* Start at first descriptor after the configuration descriptor */ + ptr = &(((uint8_t *)configuration_desc(index))[CONFIGURATION_DESCRIPTOR_LENGTH]); + + do { + if (ptr[1] /* bDescriptorType */ == descriptorType) { + /* Found */ + return ptr; + } + + /* Skip to next descriptor */ + ptr += ptr[0]; /* bLength */ + } while (ptr < (configuration_desc(index) + wTotalLength)); + + /* Reached end of the descriptors - not found */ + return NULL; +} + +const usb_ep_table_t *USBDevice::endpoint_table() +{ + return _phy->endpoint_table(); +} + +void USBDevice::power(bool powered) +{ + assert_locked(); + + if (!powered && _device.state > Attached) { + _change_state(Attached); + } +} + +void USBDevice::suspend(bool suspended) +{ +} + +void USBDevice::sof(int frame_number) +{ + callback_sof(frame_number); +} + + +USBDevice::USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) +{ + this->vendor_id = vendor_id; + this->product_id = product_id; + this->product_release = product_release; + + memset(_endpoint_info, 0, sizeof(_endpoint_info)); + memset(&_transfer, 0, sizeof(_transfer)); + _transfer.user_callback = None; + + _setup_ready = false; + _abort_control = false; + + _phy = phy; + _initialized = false; + _connected = false; + _endpoint_add_remove_allowed = false; + _current_interface = 0; + _current_alternate = 0; + _locked = 0; + _post_process = NULL; + _max_packet_size_ep0 = 0; + + /* Set initial device state */ + _device.state = Powered; + _device.configuration = 0; + _device.suspended = false; +} + +USBDevice::~USBDevice() +{ + MBED_ASSERT(!_initialized); + deinit(); +} + +uint32_t USBDevice::endpoint_max_packet_size(usb_ep_t endpoint) +{ + lock(); + + uint32_t size = 0; + if (EP_CONTROL(endpoint)) { + size = _max_packet_size_ep0; + } else { + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + size = info->max_packet_size; + } + + unlock(); + return size; +} + +void USBDevice::endpoint_abort(usb_ep_t endpoint) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Assert that only valid endpoints are used when in the configured state + MBED_ASSERT(!configured()); + unlock(); + return; + } + + if (info->pending) { + _phy->endpoint_abort(endpoint); + info->pending = 0; + } + + unlock(); +} + +bool USBDevice::read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t max_size) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return false; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Assert that only valid endpoints are used when in the configured state + MBED_ASSERT(!configured()); + unlock(); + return false; + } + + if (max_size < info->max_packet_size) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_SIZE + ), + "The size of the data to read is less than the max packet size for this endpoint." + ); +#else + unlock(); + return false; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (info->pending) { + // Only allow 1 packet + unlock(); + return false; + } + + bool ret = _phy->endpoint_read(endpoint, buffer, info->max_packet_size); + if (ret) { + info->pending += 1; + } + + unlock(); + + return ret; +} + +uint32_t USBDevice::read_finish(usb_ep_t endpoint) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return 0; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Assert that only valid endpoints are used when in the configured state + MBED_ASSERT(!configured()); + unlock(); + return 0; + } + + uint32_t size = 0; + size = _phy->endpoint_read_result(endpoint); + unlock(); + return size; +} + +bool USBDevice::write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size) +{ + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable" + ); +#else + unlock(); + return false; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Assert that only valid endpoints are used when in the configured state + MBED_ASSERT(!configured()); + unlock(); + return false; + } + + if (size > info->max_packet_size) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_SIZE + ), + "Size being written is too large." + ); +#else + unlock(); + return false; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + if (info->pending) { + // Only allow 1 packet + unlock(); + return false; + } + + /* Send report */ + bool ret = _phy->endpoint_write(endpoint, buffer, size); + if (ret) { + info->transfer_size = size; + info->pending += 1; + } else { + info->transfer_size = 0; + } + + unlock(); + return ret; +} + +uint32_t USBDevice::write_finish(usb_ep_t endpoint) +{ + uint32_t ret = 0; + + lock(); + + if (!EP_INDEXABLE(endpoint)) { +#if MBED_TRAP_ERRORS_ENABLED + MBED_ERROR( + MBED_MAKE_ERROR( + MBED_MODULE_DRIVER_USB, + MBED_ERROR_CODE_INVALID_INDEX + ), + "The endpoint is not indexable." + ); +#else + unlock(); + return 0; +#endif // MBED_TRAP_ERRORS_ENABLED + } + + endpoint_info_t *info = &_endpoint_info[EP_TO_INDEX(endpoint)]; + if (!(info->flags & ENDPOINT_ENABLED)) { + // Assert that only valid endpoints are used when in the configured state + MBED_ASSERT(!configured()); + unlock(); + return 0; + } + + ret = info->transfer_size; + + unlock(); + return ret; +} + +const uint8_t *USBDevice::device_desc() +{ + uint8_t device_descriptor_temp[] = { + DEVICE_DESCRIPTOR_LENGTH, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */ + MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */ + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceprotocol */ + (uint8_t)_max_packet_size_ep0, /* bMaxPacketSize0 */ + (uint8_t)(LSB(vendor_id)), /* idVendor (LSB) */ + (uint8_t)(MSB(vendor_id)), /* idVendor (MSB) */ + (uint8_t)(LSB(product_id)), /* idProduct (LSB) */ + (uint8_t)(MSB(product_id)), /* idProduct (MSB) */ + (uint8_t)(LSB(product_release)), /* bcdDevice (LSB) */ + (uint8_t)(MSB(product_release)), /* bcdDevice (MSB) */ + 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 *USBDevice::string_langid_desc() +{ + static const uint8_t string_langid_descriptor[] = { + 0x04, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 0x09, 0x04, /*bString Lang ID - 0x0409 - English*/ + }; + return string_langid_descriptor; +} + +const uint8_t *USBDevice::string_imanufacturer_desc() +{ + static const uint8_t string_imanufacturer_descriptor[] = { + 0x12, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 'm', 0, 'b', 0, 'e', 0, 'd', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0, /*bString iManufacturer - mbed.org*/ + }; + return string_imanufacturer_descriptor; +} + +const uint8_t *USBDevice::string_iserial_desc() +{ + static const uint8_t string_iserial_descriptor[] = { + 0x16, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + '0', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0, /*bString iSerial - 0123456789*/ + }; + return string_iserial_descriptor; +} + +const uint8_t *USBDevice::string_iconfiguration_desc() +{ + static const uint8_t string_iconfiguration_descriptor[] = { + 0x06, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + '0', 0, '1', 0, /*bString iConfiguration - 01*/ + }; + return string_iconfiguration_descriptor; +} + +const uint8_t *USBDevice::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x08, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 'U', 0, 'S', 0, 'B', 0, /*bString iInterface - USB*/ + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBDevice::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 0x16, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 /*bString iProduct - USB DEVICE*/ + }; + return string_iproduct_descriptor; +} + +void USBDevice::start_process() +{ + lock(); + + _phy->process(); + + unlock(); +} + +void USBDevice::lock() +{ + core_util_critical_section_enter(); + _locked++; + MBED_ASSERT(_locked > 0); +} + +void USBDevice::unlock() +{ + if (_locked == 1) { + // Perform post processing before fully unlocking + while (_post_process != NULL) { + void (USBDevice::*call)() = _post_process; + _post_process = NULL; + (this->*call)(); + } + } + + MBED_ASSERT(_locked > 0); + _locked--; + core_util_critical_section_exit(); +} + +void USBDevice::assert_locked() +{ + MBED_ASSERT(_locked > 0); +} + +void USBDevice::_change_state(DeviceState new_state) +{ + assert_locked(); + + DeviceState old_state = _device.state; + _device.state = new_state; + + if (old_state == new_state) { + return; + } + + bool leaving_configured_state = (old_state >= Configured) && (new_state < Configured); + bool leaving_default_state = (old_state >= Default) && (new_state < Default); + + if (leaving_configured_state) { + memset(_endpoint_info, 0, sizeof(_endpoint_info)); + _device.configuration = 0; + _endpoint_add_remove_allowed = false; + } + + if (leaving_default_state) { + /* Abort any pending control transfers */ + _control_abort_start(); + } + + callback_state_change(new_state); +} + +void USBDevice::_run_later(void (USBDevice::*function)()) +{ + _post_process = function; +} diff --git a/drivers/usb/source/USBHID.cpp b/drivers/usb/source/USBHID.cpp new file mode 100644 index 0000000..cb2cb67 --- /dev/null +++ b/drivers/usb/source/USBHID.cpp @@ -0,0 +1,512 @@ +/* + * 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 "USBHID.h" +#include "EndpointResolver.h" +#include "usb_phy_api.h" + +class USBHID::AsyncSend: public AsyncOp { +public: + AsyncSend(USBHID *hid, const HID_REPORT *report): hid(hid), report(report), result(false) + { + + } + + virtual ~AsyncSend() + { + + } + + virtual bool process() + { + if (!hid->configured()) { + result = false; + return true; + } + + if (hid->send_nb(report)) { + result = true; + return true; + } + + return false; + } + + USBHID *hid; + const HID_REPORT *report; + bool result; +}; + +class USBHID::AsyncRead: public AsyncOp { +public: + AsyncRead(USBHID *hid, HID_REPORT *report): hid(hid), report(report), result(false) + { + + } + + virtual ~AsyncRead() + { + + } + + virtual bool process() + { + if (!hid->configured()) { + result = false; + return true; + } + + if (hid->read_nb(report)) { + result = true; + return true; + } + + return false; + } + + USBHID *hid; + HID_REPORT *report; + bool result; +}; + +class USBHID::AsyncWait: public AsyncOp { +public: + AsyncWait(USBHID *hid): hid(hid) + { + + } + + virtual ~AsyncWait() + { + + } + + virtual bool process() + { + if (hid->configured()) { + return true; + } + + return false; + } + + USBHID *hid; +}; + +USBHID::USBHID(bool connect_blocking, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(get_usb_phy(), vendor_id, product_id, product_release) +{ + _init(output_report_length, input_report_length); + if (connect_blocking) { + connect(); + wait_ready(); + } else { + init(); + } + +} + +USBHID::USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release) +{ + _init(output_report_length, input_report_length); +} + +USBHID::~USBHID() +{ + deinit(); +} + +void USBHID::_init(uint8_t output_report_length, uint8_t input_report_length) +{ + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE); + _int_out = resolver.endpoint_out(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE); + MBED_ASSERT(resolver.valid()); + + _send_idle = true; + _read_idle = true; + _output_length = output_report_length; + _input_length = input_report_length; + reportLength = 0; + _input_report.length = 0; + _output_report.length = 0; +} + +bool USBHID::ready() +{ + return configured(); +} + +void USBHID::wait_ready() +{ + lock(); + + AsyncWait wait_op(this); + _connect_list.add(&wait_op); + + unlock(); + + wait_op.wait(NULL); +} + + +bool USBHID::send(const HID_REPORT *report) +{ + lock(); + + AsyncSend send_op(this, report); + _send_list.add(&send_op); + + unlock(); + + send_op.wait(NULL); + return send_op.result; +} + +bool USBHID::send_nb(const HID_REPORT *report) +{ + lock(); + + if (!configured()) { + unlock(); + return false; + } + + bool success = false; + if (_send_idle) { + memcpy(&_input_report, report, sizeof(_input_report)); + write_start(_int_in, _input_report.data, _input_report.length); + _send_idle = false; + success = true; + } + + unlock(); + return success; +} + +bool USBHID::read(HID_REPORT *report) +{ + lock(); + + AsyncRead read_op(this, report); + _read_list.add(&read_op); + + unlock(); + + read_op.wait(NULL); + return read_op.result; +} + + +bool USBHID::read_nb(HID_REPORT *report) +{ + lock(); + + if (!configured()) { + unlock(); + return false; + } + + bool success = false; + if (_read_idle) { + memcpy(report, &_output_report, sizeof(_output_report)); + read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE); + _read_idle = false; + success = true; + } + + unlock(); + return success; +} + +void USBHID::_send_isr() +{ + assert_locked(); + + write_finish(_int_in); + _send_idle = true; + + _send_list.process(); + if (_send_idle) { + report_tx(); + } + +} + +void USBHID::_read_isr() +{ + assert_locked(); + + _output_report.length = read_finish(_int_out); + _read_idle = true; + + _read_list.process(); + if (_read_idle) { + report_rx(); + } +} + +uint16_t USBHID::report_desc_length() +{ + report_desc(); + return reportLength; +} + + +void USBHID::callback_state_change(DeviceState new_state) +{ + if (new_state != Configured) { + if (!_send_idle) { + endpoint_abort(_int_in); + _send_idle = true; + } + if (!_read_idle) { + endpoint_abort(_int_out); + _read_idle = true; + } + } + _send_list.process(); + _read_list.process(); + _connect_list.process(); +} + +// +// Route callbacks from lower layers to class(es) +// + + +// Called in ISR context +// Called by USBDevice on Endpoint0 request +// This is used to handle extensions to standard requests +// and class specific requests +// Return true if class handles this request +void USBHID::callback_request(const setup_packet_t *setup) +{ + uint8_t *hidDescriptor; + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + // Process additional standard requests + + if ((setup->bmRequestType.Type == STANDARD_TYPE)) { + switch (setup->bRequest) { + case GET_DESCRIPTOR: + switch (DESCRIPTOR_TYPE(setup->wValue)) { + case REPORT_DESCRIPTOR: + if ((report_desc() != NULL) \ + && (report_desc_length() != 0)) { + size = report_desc_length(); + data = (uint8_t *)report_desc(); + result = Send; + } + break; + case HID_DESCRIPTOR: + // Find the HID descriptor, after the configuration descriptor + hidDescriptor = find_descriptor(HID_DESCRIPTOR); + if (hidDescriptor != NULL) { + size = HID_DESCRIPTOR_LENGTH; + data = hidDescriptor; + result = Send; + } + break; + + default: + break; + } + break; + default: + break; + } + } + + // Process class-specific requests + + if (setup->bmRequestType.Type == CLASS_TYPE) { + switch (setup->bRequest) { + case SET_REPORT: + // First byte will be used for report ID + _output_report.data[0] = setup->wValue & 0xff; + _output_report.length = setup->wLength + 1; + + size = sizeof(_output_report.data) - 1; + data = &_output_report.data[1]; + result = Send; + break; + default: + break; + } + } + + complete_request(result, data, size); +} + +void USBHID::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + (void)aborted; + complete_request_xfer_done(true); +} + + +#define DEFAULT_CONFIGURATION (1) + + +// Called in ISR context +// Set configuration. Return false if the +// configuration is not supported +void USBHID::callback_set_configuration(uint8_t configuration) +{ + if (configuration == DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + } + + // Configure endpoints > 0 + endpoint_add(_int_in, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_send_isr); + endpoint_add(_int_out, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_read_isr); + + // We activate the endpoint to be able to recceive data + read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE); + _read_idle = false; + + + complete_set_configuration(true); +} + +void USBHID::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + complete_set_interface(true); +} + + +const uint8_t *USBHID::string_iinterface_desc() +{ + static const uint8_t stringIinterfaceDescriptor[] = { + 0x08, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'H', 0, 'I', 0, 'D', 0, //bString iInterface - HID + }; + return stringIinterfaceDescriptor; +} + +const uint8_t *USBHID::string_iproduct_desc() +{ + static const uint8_t stringIproductDescriptor[] = { + 0x16, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'H', 0, 'I', 0, 'D', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 //bString iProduct - HID device + }; + return stringIproductDescriptor; +} + + + +const uint8_t *USBHID::report_desc() +{ + uint8_t reportDescriptorTemp[] = { + USAGE_PAGE(2), LSB(0xFFAB), MSB(0xFFAB), + USAGE(2), LSB(0x0200), MSB(0x0200), + COLLECTION(1), 0x01, // Collection (Application) + + REPORT_SIZE(1), 0x08, // 8 bits + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0xFF, + + REPORT_COUNT(1), _input_length, + USAGE(1), 0x01, + INPUT(1), 0x02, // Data, Var, Abs + + REPORT_COUNT(1), _output_length, + USAGE(1), 0x02, + OUTPUT(1), 0x02, // Data, Var, Abs + + END_COLLECTION(0), + }; + reportLength = sizeof(reportDescriptor); + MBED_ASSERT(sizeof(reportDescriptorTemp) == sizeof(reportDescriptor)); + memcpy(reportDescriptor, reportDescriptorTemp, sizeof(reportDescriptor)); + return reportDescriptor; +} + +#define DEFAULT_CONFIGURATION (1) +#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ + + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ + + (1 * HID_DESCRIPTOR_LENGTH) \ + + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) + +const uint8_t *USBHID::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + + uint8_t configurationDescriptorTemp[] = { + CONFIGURATION_DESCRIPTOR_LENGTH, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) + MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) + 0x01, // bNumInterfaces + DEFAULT_CONFIGURATION, // bConfigurationValue + 0x00, // iConfiguration + C_RESERVED | C_SELF_POWERED, // bmAttributes + C_POWER(0), // bMaxPower + + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + HID_CLASS, // bInterfaceClass + HID_SUBCLASS_NONE, // bInterfaceSubClass + HID_PROTOCOL_NONE, // bInterfaceProtocol + 0x00, // iInterface + + HID_DESCRIPTOR_LENGTH, // bLength + HID_DESCRIPTOR, // bDescriptorType + LSB(HID_VERSION_1_11), // bcdHID (LSB) + MSB(HID_VERSION_1_11), // bcdHID (MSB) + 0x00, // bCountryCode + 0x01, // bNumDescriptors + REPORT_DESCRIPTOR, // bDescriptorType + (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) + (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + }; + MBED_ASSERT(sizeof(configurationDescriptorTemp) == sizeof(_configuration_descriptor)); + memcpy(_configuration_descriptor, configurationDescriptorTemp, sizeof(_configuration_descriptor)); + return _configuration_descriptor; +} diff --git a/drivers/usb/source/USBKeyboard.cpp b/drivers/usb/source/USBKeyboard.cpp new file mode 100644 index 0000000..5598b36 --- /dev/null +++ b/drivers/usb/source/USBKeyboard.cpp @@ -0,0 +1,605 @@ +/* + * 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 "stdint.h" + +#include "USBKeyboard.h" +#include "usb_phy_api.h" + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0, 0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0, 0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + + +USBKeyboard::USBKeyboard(bool connect, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) +{ + _lock_status = 0; + if (connect) { + USBDevice::connect(); + wait_ready(); + } else { + init(); + } +} + +USBKeyboard::USBKeyboard(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(phy, 0, 0, vendor_id, product_id, product_release) +{ + _lock_status = 0; + + // User or child responsible for calling connect or init +} + +USBKeyboard::~USBKeyboard() +{ + deinit(); +} + +const uint8_t *USBKeyboard::report_desc() +{ + static const uint8_t reportDescriptor[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + REPORT_ID(1), REPORT_ID_KEYBOARD, + + USAGE_PAGE(1), 0x07, // Key Codes + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Constant + + + REPORT_COUNT(1), 0x05, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x05, + OUTPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Constant + + + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, + USAGE_PAGE(1), 0x07, // Key Codes + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data, Array + END_COLLECTION(0), + + // Media Control + USAGE_PAGE(1), 0x0C, + USAGE(1), 0x01, + COLLECTION(1), 0x01, + REPORT_ID(1), REPORT_ID_VOLUME, + USAGE_PAGE(1), 0x0C, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x07, + USAGE(1), 0xB5, // Next Track + USAGE(1), 0xB6, // Previous Track + USAGE(1), 0xB7, // Stop + USAGE(1), 0xCD, // Play / Pause + USAGE(1), 0xE2, // Mute + USAGE(1), 0xE9, // Volume Up + USAGE(1), 0xEA, // Volume Down + INPUT(1), 0x02, // Input (Data, Variable, Absolute) + REPORT_COUNT(1), 0x01, + INPUT(1), 0x01, + END_COLLECTION(0), + }; + reportLength = sizeof(reportDescriptor); + return reportDescriptor; +} + + +void USBKeyboard::report_rx() +{ + assert_locked(); + + HID_REPORT report; + read_nb(&report); + + // we take [1] because [0] is the report ID + _lock_status = report.data[1] & 0x07; +} + +uint8_t USBKeyboard::lock_status() +{ + return _lock_status; +} + +int USBKeyboard::_putc(int c) +{ + return key_code(c, keymap[c].modifier); +} + +bool USBKeyboard::key_code(uint8_t key, uint8_t modifier) +{ + _mutex.lock(); + + // Send a simulated keyboard keypress. Returns true if successful. + HID_REPORT report; + + report.data[0] = REPORT_ID_KEYBOARD; + report.data[1] = modifier; + report.data[2] = 0; + report.data[3] = keymap[key].usage; + report.data[4] = 0; + report.data[5] = 0; + report.data[6] = 0; + report.data[7] = 0; + report.data[8] = 0; + + report.length = 9; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + report.data[1] = 0; + report.data[3] = 0; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + _mutex.unlock(); + return true; + +} + + +bool USBKeyboard::media_control(MEDIA_KEY key) +{ + _mutex.lock(); + + HID_REPORT report; + + report.data[0] = REPORT_ID_VOLUME; + report.data[1] = (1 << key) & 0x7f; + + report.length = 2; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + report.data[0] = REPORT_ID_VOLUME; + report.data[1] = 0; + + report.length = 2; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + _mutex.unlock(); + return true; +} + +#define DEFAULT_CONFIGURATION (1) +#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ + + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ + + (1 * HID_DESCRIPTOR_LENGTH) \ + + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) + +const uint8_t *USBKeyboard::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + uint8_t configuration_descriptor_temp[] = { + CONFIGURATION_DESCRIPTOR_LENGTH, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) + MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) + 0x01, // bNumInterfaces + DEFAULT_CONFIGURATION, // bConfigurationValue + 0x00, // iConfiguration + C_RESERVED | C_SELF_POWERED, // bmAttributes + C_POWER(0), // bMaxPower + + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + HID_CLASS, // bInterfaceClass + HID_SUBCLASS_BOOT, // bInterfaceSubClass + HID_PROTOCOL_KEYBOARD, // bInterfaceProtocol + 0x00, // iInterface + + HID_DESCRIPTOR_LENGTH, // bLength + HID_DESCRIPTOR, // bDescriptorType + LSB(HID_VERSION_1_11), // bcdHID (LSB) + MSB(HID_VERSION_1_11), // bcdHID (MSB) + 0x00, // bCountryCode + 0x01, // bNumDescriptors + REPORT_DESCRIPTOR, // bDescriptorType + (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) + (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + }; + MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor)); + memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor)); + return _configuration_descriptor; +} + +int USBKeyboard::_getc() +{ + return -1; +} diff --git a/drivers/usb/source/USBMIDI.cpp b/drivers/usb/source/USBMIDI.cpp new file mode 100644 index 0000000..594b1b9 --- /dev/null +++ b/drivers/usb/source/USBMIDI.cpp @@ -0,0 +1,399 @@ +/* + * 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 "USBMIDI.h" +#include "EndpointResolver.h" +#include "usb_phy_api.h" + +#define FLAG_WRITE_DONE (1 << 0) +#define FLAG_DISCONNECT (1 << 1) +#define FLAG_CONNECT (1 << 2) + +USBMIDI::USBMIDI(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) +{ + _init(); + + if (connect_blocking) { + USBDevice::connect(); + wait_ready(); + } else { + init(); + } +} + +USBMIDI::USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBDevice(phy, vendor_id, product_id, product_release) +{ + _init(); + + // User or child responsible for calling connect or init +} + +USBMIDI::~USBMIDI() +{ + deinit(); +} + +void USBMIDI::_init() +{ + _bulk_buf_pos = 0; + _bulk_buf_size = 0; + + _data_ready = false; + _cur_data = 0; + + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MaxSize); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MaxSize); + MBED_ASSERT(resolver.valid()); +} + +bool USBMIDI::ready() +{ + return _flags.get() & FLAG_CONNECT ? true : false; +} + +void USBMIDI::wait_ready() +{ + _flags.wait_any(FLAG_CONNECT, osWaitForever, false); +} + +// write plain MIDIMessage that will be converted to USBMidi event packet +bool USBMIDI::write(MIDIMessage m) +{ + _write_mutex.lock(); + + bool ret = true; + // first byte keeped for retro-compatibility + for (int p = 1; p < m.length; p += 3) { + uint8_t buf[4]; + // Midi message to USBMidi event packet + buf[0] = m.data[1] >> 4; + // SysEx + if (buf[0] == 0xF) { + if ((m.length - p) > 3) { + // SysEx start or continue + buf[0] = 0x4; + } else { + switch (m.length - p) { + case 1: + // SysEx end with one byte + buf[0] = 0x5; + break; + case 2: + // SysEx end with two bytes + buf[0] = 0x6; + break; + case 3: + // SysEx end with three bytes + buf[0] = 0x7; + break; + } + } + } + buf[1] = m.data[p]; + + if (p + 1 < m.length) { + buf[2] = m.data[p + 1]; + } else { + buf[2] = 0; + } + + if (p + 2 < m.length) { + buf[3] = m.data[p + 2]; + } else { + buf[3] = 0; + } + + _flags.clear(FLAG_WRITE_DONE); + USBDevice::write_start(_bulk_in, buf, 4); + uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false); + if (flags & FLAG_DISCONNECT) { + ret = false; + break; + } + USBDevice::write_finish(_bulk_in); + } + + _write_mutex.unlock(); + return ret; +} + +bool USBMIDI::readable() +{ + lock(); + + bool ret = _data_ready; + + unlock(); + + return ret; +} + +bool USBMIDI::read(MIDIMessage *m) +{ + lock(); + + // Invalidate message + m->length = 0; + + if (!_data_ready) { + unlock(); + return false; + } + + m->from_raw(_data, _cur_data); + _cur_data = 0; + _next_message(); + + if (!_data_ready) { + read_start(_bulk_out, _bulk_buf, MaxSize); + } + + unlock(); + + return true; +} + +void USBMIDI::attach(mbed::Callback callback) +{ + lock(); + + _callback = callback; + + unlock(); +} + +void USBMIDI::callback_state_change(DeviceState new_state) +{ + assert_locked(); + + if (new_state == Configured) { + _flags.set(FLAG_CONNECT); + _flags.clear(FLAG_DISCONNECT); + } else { + _flags.set(FLAG_DISCONNECT); + _flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE); + } +} + +void USBMIDI::callback_request(const setup_packet_t *setup) +{ + assert_locked(); + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + complete_request(result, data, size); +} + +void USBMIDI::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + assert_locked(); + + complete_request_xfer_done(false); +} + +void USBMIDI::callback_set_configuration(uint8_t configuration) +{ + assert_locked(); + + if (configuration == DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + } + + endpoint_remove_all(); + endpoint_add(_bulk_in, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_in_callback); + endpoint_add(_bulk_out, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_out_callback); + + read_start(_bulk_out, _bulk_buf, MaxSize); + + complete_set_configuration(true); +} + +void USBMIDI::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + + complete_set_interface(true); +} + +const uint8_t *USBMIDI::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x0c, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBMIDI::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 0x16, //bLength + STRING_DESCRIPTOR, //bDescriptorType 0x03 + 'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio + }; + return string_iproduct_descriptor; +} + +const uint8_t *USBMIDI::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + + uint8_t config_descriptor_temp[] = { + // configuration descriptor + 0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50, + + // The Audio Interface Collection + 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor + 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor + 0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors + 0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor + + // MIDI IN JACKS + 0x06, 0x24, 0x02, 0x01, 0x01, 0x00, + 0x06, 0x24, 0x02, 0x02, 0x02, 0x00, + + // MIDI OUT JACKS + 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, + 0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00, + + // OUT endpoint - Standard MS Bulk Data Endpoint Descriptor + 0x09, // bLength + 0x05, // bDescriptorType + _bulk_out, // bEndpointAddress + 0x02, // bmAttributes + 0x40, // wMaxPacketSize (LSB) + 0x00, // wMaxPacketSize (MSB) + 0x00, // bInterval (milliseconds) + 0x00, // bRefresh + 0x00, // bSynchAddress + + 0x05, 0x25, 0x01, 0x01, 0x01, + + // IN endpoint - Standard MS Bulk Data Endpoint Descriptor + 0x09, // bLength + 0x05, // bDescriptorType + _bulk_in, // bEndpointAddress + 0x02, // bmAttributes + 0x40, // wMaxPacketSize (LSB) + 0x00, // wMaxPacketSize (MSB) + 0x00, // bInterval (milliseconds) + 0x00, // bRefresh + 0x00, // bSynchAddress + + 0x05, 0x25, 0x01, 0x01, 0x03, + }; + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); + memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp)); + return _config_descriptor; +} + +void USBMIDI::_in_callback() +{ + assert_locked(); + + _flags.set(FLAG_WRITE_DONE); +} + +void USBMIDI::_out_callback() +{ + assert_locked(); + + _bulk_buf_size = read_finish(_bulk_out); + _bulk_buf_pos = 0; + + if (_callback && _next_message()) { + _callback(); + return; + } + + read_start(_bulk_out, _bulk_buf, MaxSize); +} + +bool USBMIDI::_next_message() +{ + assert_locked(); + + bool data_ready = false; + while (_bulk_buf_pos < _bulk_buf_size) { + uint8_t data_read; + bool data_end = true; + switch (_bulk_buf[_bulk_buf_pos]) { + case 0x2: + // Two-bytes System Common Message - undefined in USBMidi 1.0 + data_read = 2; + break; + case 0x4: + // SysEx start or continue + data_end = false; + data_read = 3; + break; + case 0x5: + // Single-byte System Common Message or SysEx end with one byte + data_read = 1; + break; + case 0x6: + // SysEx end with two bytes + data_read = 2; + break; + case 0xC: + // Program change + data_read = 2; + break; + case 0xD: + // Channel pressure + data_read = 2; + break; + case 0xF: + // Single byte + data_read = 1; + break; + default: + // Others three-bytes messages + data_read = 3; + break; + } + + for (uint8_t j = 1; j < data_read + 1; j++) { + if (_cur_data < sizeof(_data)) { + _data[_cur_data] = _bulk_buf[_bulk_buf_pos + j]; + } + _cur_data++; + } + _bulk_buf_pos += 4; + + if (data_end) { + // Message is ready to be read + data_ready = true; + break; + } + } + + _data_ready = data_ready; + return data_ready; +} diff --git a/drivers/usb/source/USBMSD.cpp b/drivers/usb/source/USBMSD.cpp new file mode 100644 index 0000000..6845c02 --- /dev/null +++ b/drivers/usb/source/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(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, 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) +{ + bd_addr_t addr = block * _bd->get_erase_size(); + 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) +{ + bd_addr_t addr = block * _bd->get_erase_size(); + 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/USBMouse.cpp b/drivers/usb/source/USBMouse.cpp new file mode 100644 index 0000000..fc22522 --- /dev/null +++ b/drivers/usb/source/USBMouse.cpp @@ -0,0 +1,351 @@ +/* + * 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 "USBMouse.h" +#include "ThisThread.h" +#include "usb_phy_api.h" + +using namespace std::chrono_literals; + +USBMouse::USBMouse(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) +{ + _button = 0; + _mouse_type = mouse_type; + + if (connect_blocking) { + USBDevice::connect(); + wait_ready(); + } else { + init(); + } +} + +USBMouse::USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) +{ + _button = 0; + _mouse_type = mouse_type; +} + +USBMouse::~USBMouse() +{ + deinit(); +} + +bool USBMouse::update(int16_t x, int16_t y, uint8_t button, int8_t z) +{ + bool ret; + switch (_mouse_type) { + case REL_MOUSE: + _mutex.lock(); + + while (x > 127) { + if (!mouse_send(127, 0, button, z)) { + _mutex.unlock(); + return false; + } + x = x - 127; + } + while (x < -128) { + if (!mouse_send(-128, 0, button, z)) { + _mutex.unlock(); + return false; + } + x = x + 128; + } + while (y > 127) { + if (!mouse_send(0, 127, button, z)) { + _mutex.unlock(); + return false; + } + y = y - 127; + } + while (y < -128) { + if (!mouse_send(0, -128, button, z)) { + _mutex.unlock(); + return false; + } + y = y + 128; + } + ret = mouse_send(x, y, button, z); + + _mutex.unlock(); + return ret; + case ABS_MOUSE: + _mutex.lock(); + + HID_REPORT report; + + report.data[0] = x & 0xff; + report.data[1] = (x >> 8) & 0xff; + report.data[2] = y & 0xff; + report.data[3] = (y >> 8) & 0xff; + report.data[4] = -z; + report.data[5] = button & 0x07; + + report.length = 6; + + ret = send(&report); + + _mutex.unlock(); + return ret; + default: + return false; + } +} + +bool USBMouse::mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z) +{ + _mutex.lock(); + + HID_REPORT report; + report.data[0] = buttons & 0x07; + report.data[1] = x; + report.data[2] = y; + report.data[3] = -z; // >0 to scroll down, <0 to scroll up + + report.length = 4; + + bool ret = send(&report); + + _mutex.unlock(); + return ret; +} + +bool USBMouse::move(int16_t x, int16_t y) +{ + _mutex.lock(); + + bool ret = update(x, y, _button, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouse::scroll(int8_t z) +{ + _mutex.lock(); + + bool ret = update(0, 0, _button, z); + + _mutex.unlock(); + return ret; +} + + +bool USBMouse::double_click() +{ + _mutex.lock(); + + if (!click(MOUSE_LEFT)) { + _mutex.unlock(); + return false; + } + rtos::ThisThread::sleep_for(100ms); + bool ret = click(MOUSE_LEFT); + + _mutex.unlock(); + return ret; +} + +bool USBMouse::click(uint8_t button) +{ + _mutex.lock(); + + if (!update(0, 0, button, 0)) { + _mutex.unlock(); + return false; + } + rtos::ThisThread::sleep_for(10ms); + bool ret = update(0, 0, 0, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouse::press(uint8_t button) +{ + _mutex.lock(); + + _button = button & 0x07; + bool ret = update(0, 0, _button, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouse::release(uint8_t button) +{ + _mutex.lock(); + + _button = (_button & (~button)) & 0x07; + bool ret = update(0, 0, _button, 0); + + _mutex.unlock(); + return ret; +} + + +const uint8_t *USBMouse::report_desc() +{ + + if (_mouse_type == REL_MOUSE) { + static const uint8_t report_descriptor[] = { + USAGE_PAGE(1), 0x01, // Genric Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x1, + USAGE_MAXIMUM(1), 0x3, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, + + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x08, + USAGE_PAGE(1), 0x01, + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + USAGE(1), 0x38, // scroll + LOGICAL_MINIMUM(1), 0x81, + LOGICAL_MAXIMUM(1), 0x7f, + INPUT(1), 0x06, // Relative data + + END_COLLECTION(0), + END_COLLECTION(0), + }; + reportLength = sizeof(report_descriptor); + return report_descriptor; + } else if (_mouse_type == ABS_MOUSE) { + static const uint8_t report_descriptor[] = { + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + LOGICAL_MINIMUM(1), 0x00, // 0 + LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767 + REPORT_SIZE(1), 0x10, + REPORT_COUNT(1), 0x02, + INPUT(1), 0x02, // Data, Variable, Absolute + + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x38, // scroll + LOGICAL_MINIMUM(1), 0x81, // -127 + LOGICAL_MAXIMUM(1), 0x7f, // 127 + REPORT_SIZE(1), 0x08, + REPORT_COUNT(1), 0x01, + INPUT(1), 0x06, // Data, Variable, Relative + + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x03, + LOGICAL_MINIMUM(1), 0x00, // 0 + LOGICAL_MAXIMUM(1), 0x01, // 1 + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x01, + INPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, // Constant + + END_COLLECTION(0), + END_COLLECTION(0) + }; + reportLength = sizeof(report_descriptor); + return report_descriptor; + } + return NULL; +} + +#define DEFAULT_CONFIGURATION (1) +#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \ + + (1 * INTERFACE_DESCRIPTOR_LENGTH) \ + + (1 * HID_DESCRIPTOR_LENGTH) \ + + (2 * ENDPOINT_DESCRIPTOR_LENGTH)) + +const uint8_t *USBMouse::configuration_desc(uint8_t index) +{ + if (index != 0) { + return NULL; + } + uint8_t configuration_descriptor_temp[] = { + CONFIGURATION_DESCRIPTOR_LENGTH, // bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB) + MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB) + 0x01, // bNumInterfaces + DEFAULT_CONFIGURATION, // bConfigurationValue + 0x00, // iConfiguration + C_RESERVED | C_SELF_POWERED, // bmAttributes + C_POWER(0), // bMaxPower + + INTERFACE_DESCRIPTOR_LENGTH, // bLength + INTERFACE_DESCRIPTOR, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + HID_CLASS, // bInterfaceClass + HID_SUBCLASS_BOOT, // bInterfaceSubClass + HID_PROTOCOL_MOUSE, // bInterfaceProtocol + 0x00, // iInterface + + HID_DESCRIPTOR_LENGTH, // bLength + HID_DESCRIPTOR, // bDescriptorType + LSB(HID_VERSION_1_11), // bcdHID (LSB) + MSB(HID_VERSION_1_11), // bcdHID (MSB) + 0x00, // bCountryCode + 0x01, // bNumDescriptors + REPORT_DESCRIPTOR, // bDescriptorType + (uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB) + (uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes + LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval (milliseconds) + }; + MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor)); + memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor)); + return _configuration_descriptor; +} diff --git a/drivers/usb/source/USBMouseKeyboard.cpp b/drivers/usb/source/USBMouseKeyboard.cpp new file mode 100644 index 0000000..f6da5c6 --- /dev/null +++ b/drivers/usb/source/USBMouseKeyboard.cpp @@ -0,0 +1,832 @@ +/* + * 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 "stdint.h" +#include "USBMouseKeyboard.h" +#include "usb_phy_api.h" +#include "ThisThread.h" + +using namespace std::chrono_literals; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0, 0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0, 0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +USBMouseKeyboard::USBMouseKeyboard(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release) +{ + _lock_status = 0; + _button = 0; + _mouse_type = mouse_type; + + if (connect_blocking) { + USBDevice::connect(); + wait_ready(); + } else { + init(); + } +}; + +USBMouseKeyboard::USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBHID(phy, 0, 0, vendor_id, product_id, product_release) +{ + _lock_status = 0; + _button = 0; + _mouse_type = mouse_type; +}; + +/** + * Destroy this object + * + * Any classes which inherit from this class must call deinit + * before this destructor runs. + */ +USBMouseKeyboard::~USBMouseKeyboard() +{ + deinit(); +} + +const uint8_t *USBMouseKeyboard::report_desc() +{ + if (_mouse_type == REL_MOUSE) { + static const uint8_t reportDescriptor[] = { + // Keyboard + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), REPORT_ID_KEYBOARD, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + REPORT_COUNT(1), 0x05, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x05, + OUTPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(2), 0xff, 0x00, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(2), 0xff, 0x00, + INPUT(1), 0x00, + END_COLLECTION(0), + + // Mouse + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + REPORT_ID(1), REPORT_ID_MOUSE, + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x1, + USAGE_MAXIMUM(1), 0x3, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x08, + USAGE_PAGE(1), 0x01, + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + USAGE(1), 0x38, // scroll + LOGICAL_MINIMUM(1), 0x81, + LOGICAL_MAXIMUM(1), 0x7f, + INPUT(1), 0x06, + END_COLLECTION(0), + END_COLLECTION(0), + + + // Media Control + USAGE_PAGE(1), 0x0C, + USAGE(1), 0x01, + COLLECTION(1), 0x01, + REPORT_ID(1), REPORT_ID_VOLUME, + USAGE_PAGE(1), 0x0C, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x07, + USAGE(1), 0xB5, // Next Track + USAGE(1), 0xB6, // Previous Track + USAGE(1), 0xB7, // Stop + USAGE(1), 0xCD, // Play / Pause + USAGE(1), 0xE2, // Mute + USAGE(1), 0xE9, // Volume Up + USAGE(1), 0xEA, // Volume Down + INPUT(1), 0x02, // Input (Data, Variable, Absolute) + REPORT_COUNT(1), 0x01, + INPUT(1), 0x01, + END_COLLECTION(0), + }; + reportLength = sizeof(reportDescriptor); + return reportDescriptor; + } else if (_mouse_type == ABS_MOUSE) { + static const uint8_t reportDescriptor[] = { + + // Keyboard + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), REPORT_ID_KEYBOARD, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + REPORT_COUNT(1), 0x05, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x05, + OUTPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(2), 0xff, 0x00, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(2), 0xff, 0x00, + INPUT(1), 0x00, + END_COLLECTION(0), + + // Mouse + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + REPORT_ID(1), REPORT_ID_MOUSE, + + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + LOGICAL_MINIMUM(1), 0x00, // 0 + LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767 + REPORT_SIZE(1), 0x10, + REPORT_COUNT(1), 0x02, + INPUT(1), 0x02, // Data, Variable, Absolute + + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x38, // scroll + LOGICAL_MINIMUM(1), 0x81, // -127 + LOGICAL_MAXIMUM(1), 0x7f, // 127 + REPORT_SIZE(1), 0x08, + REPORT_COUNT(1), 0x01, + INPUT(1), 0x06, // Data, Variable, Relative + + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x03, + LOGICAL_MINIMUM(1), 0x00, // 0 + LOGICAL_MAXIMUM(1), 0x01, // 1 + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x01, + INPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, // Constant + + END_COLLECTION(0), + END_COLLECTION(0), + + // Media Control + USAGE_PAGE(1), 0x0C, + USAGE(1), 0x01, + COLLECTION(1), 0x01, + REPORT_ID(1), REPORT_ID_VOLUME, + USAGE_PAGE(1), 0x0C, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x07, + USAGE(1), 0xB5, // Next Track + USAGE(1), 0xB6, // Previous Track + USAGE(1), 0xB7, // Stop + USAGE(1), 0xCD, // Play / Pause + USAGE(1), 0xE2, // Mute + USAGE(1), 0xE9, // Volume Up + USAGE(1), 0xEA, // Volume Down + INPUT(1), 0x02, // Input (Data, Variable, Absolute) + REPORT_COUNT(1), 0x01, + INPUT(1), 0x01, + END_COLLECTION(0), + }; + reportLength = sizeof(reportDescriptor); + return reportDescriptor; + } + + return NULL; +} + +void USBMouseKeyboard::report_rx() +{ + assert_locked(); + + HID_REPORT report; + read_nb(&report); + + // we take [1] because [0] is the report ID + _lock_status = report.data[1] & 0x07; +} + +uint8_t USBMouseKeyboard::lock_status() +{ + return _lock_status; +} + +bool USBMouseKeyboard::update(int16_t x, int16_t y, uint8_t button, int8_t z) +{ + bool ret; + switch (_mouse_type) { + case REL_MOUSE: + _mutex.lock(); + + while (x > 127) { + if (!_mouse_send(127, 0, button, z)) { + _mutex.unlock(); + return false; + } + x = x - 127; + } + while (x < -128) { + if (!_mouse_send(-128, 0, button, z)) { + _mutex.unlock(); + return false; + } + x = x + 128; + } + while (y > 127) { + if (!_mouse_send(0, 127, button, z)) { + _mutex.unlock(); + return false; + } + y = y - 127; + } + while (y < -128) { + if (!_mouse_send(0, -128, button, z)) { + _mutex.unlock(); + return false; + } + y = y + 128; + } + ret = _mouse_send(x, y, button, z); + + _mutex.unlock(); + return ret; + case ABS_MOUSE: + _mutex.lock(); + + HID_REPORT report; + + report.data[0] = REPORT_ID_MOUSE; + report.data[1] = x & 0xff; + report.data[2] = (x >> 8) & 0xff; + report.data[3] = y & 0xff; + report.data[4] = (y >> 8) & 0xff; + report.data[5] = -z; + report.data[6] = button & 0x07; + + report.length = 7; + + ret = send(&report); + + _mutex.unlock(); + return ret; + default: + return false; + } +} + +bool USBMouseKeyboard::_mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z) +{ + _mutex.lock(); + + HID_REPORT report; + report.data[0] = REPORT_ID_MOUSE; + report.data[1] = buttons & 0x07; + report.data[2] = x; + report.data[3] = y; + report.data[4] = -z; // >0 to scroll down, <0 to scroll up + + report.length = 5; + + bool ret = send(&report); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::move(int16_t x, int16_t y) +{ + _mutex.lock(); + + bool ret = update(x, y, _button, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::scroll(int8_t z) +{ + _mutex.lock(); + + bool ret = update(0, 0, _button, z); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::doubleClick() +{ + _mutex.lock(); + + if (!click(MOUSE_LEFT)) { + _mutex.unlock(); + return false; + } + rtos::ThisThread::sleep_for(100ms); + bool ret = click(MOUSE_LEFT); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::click(uint8_t button) +{ + _mutex.lock(); + + if (!update(0, 0, button, 0)) { + _mutex.unlock(); + return false; + } + rtos::ThisThread::sleep_for(10ms); + bool ret = update(0, 0, 0, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::press(uint8_t button) +{ + _mutex.lock(); + + _button = button & 0x07; + bool ret = update(0, 0, button, 0); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::release(uint8_t button) +{ + _mutex.lock(); + + _button = (_button & (~button)) & 0x07; + bool ret = update(0, 0, _button, 0); + + _mutex.unlock(); + return ret; +} + +int USBMouseKeyboard::_putc(int c) +{ + _mutex.lock(); + + bool ret = key_code(c, keymap[c].modifier); + + _mutex.unlock(); + return ret; +} + +bool USBMouseKeyboard::key_code(uint8_t key, uint8_t modifier) +{ + // Send a simulated keyboard keypress. Returns true if successful. + _mutex.lock(); + + HID_REPORT report; + + report.data[0] = REPORT_ID_KEYBOARD; + report.data[1] = modifier; + report.data[2] = 0; + report.data[3] = keymap[key].usage; + report.data[4] = 0; + report.data[5] = 0; + report.data[6] = 0; + report.data[7] = 0; + report.data[8] = 0; + + report.length = 9; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + report.data[1] = 0; + report.data[3] = 0; + + if (!send(&report)) { + _mutex.unlock(); + return false; + } + + _mutex.unlock(); + return true; +} + + +bool USBMouseKeyboard::media_control(MEDIA_KEY key) +{ + _mutex.lock(); + + HID_REPORT report; + + report.data[0] = REPORT_ID_VOLUME; + report.data[1] = (1 << key) & 0x7f; + + report.length = 2; + + send(&report); + + report.data[0] = REPORT_ID_VOLUME; + report.data[1] = 0; + + report.length = 2; + + bool ret = send(&report); + + _mutex.unlock(); + return ret; +} + +int USBMouseKeyboard::_getc() +{ + return -1; +} diff --git a/drivers/usb/source/USBSerial.cpp b/drivers/usb/source/USBSerial.cpp new file mode 100644 index 0000000..b0907d1 --- /dev/null +++ b/drivers/usb/source/USBSerial.cpp @@ -0,0 +1,92 @@ +/* + * 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 "stdint.h" +#include "USBSerial.h" +#include "usb_phy_api.h" + + +USBSerial::USBSerial(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBCDC(get_usb_phy(), vendor_id, product_id, product_release) +{ + _settings_changed_callback = 0; + + if (connect_blocking) { + connect(); + wait_ready(); + } else { + init(); + } +} + +USBSerial::USBSerial(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): + USBCDC(phy, vendor_id, product_id, product_release) +{ + _settings_changed_callback = 0; +} + +USBSerial::~USBSerial() +{ + deinit(); +} + +int USBSerial::_putc(int c) +{ + if (send((uint8_t *)&c, 1)) { + return c; + } else { + return -1; + } +} + +int USBSerial::_getc() +{ + uint8_t c = 0; + if (receive(&c, sizeof(c))) { + return c; + } else { + return -1; + } +} + +void USBSerial::data_rx() +{ + assert_locked(); + + //call a potential handler + if (rx) { + rx.call(); + } +} + +uint8_t USBSerial::available() +{ + USBCDC::lock(); + + uint8_t size = 0; + if (!_rx_in_progress) { + size = _rx_size > 0xFF ? 0xFF : _rx_size; + } + + USBCDC::unlock(); + return size; +} + +bool USBSerial::connected() +{ + return _terminal_connected; +}