Update USBDevice and configure to use a USBPhy

Update the USBDevice class API so it matches mbed-os's naming
conventions, has a more robust API and uses USBPhy as its backend.
pull/9768/head
Russ Butler 2018-02-27 10:23:14 -06:00
parent f7cb2cd280
commit caace4ac61
4 changed files with 1332 additions and 550 deletions

View File

@ -14,6 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef USBDESCRIPTOR_H
#define USBDESCRIPTOR_H
/* Standard descriptor types */ /* Standard descriptor types */
#define DEVICE_DESCRIPTOR (1) #define DEVICE_DESCRIPTOR (1)
#define CONFIGURATION_DESCRIPTOR (2) #define CONFIGURATION_DESCRIPTOR (2)
@ -44,9 +47,6 @@
#define LSB(n) ((n)&0xff) #define LSB(n) ((n)&0xff)
#define MSB(n) (((n)&0xff00)>>8) #define MSB(n) (((n)&0xff00)>>8)
/* Convert physical endpoint number to descriptor endpoint number */
#define PHY_TO_DESC(endpoint) (((endpoint)>>1) | (((endpoint) & 1) ? 0x80:0))
/* bmAttributes in configuration descriptor */ /* bmAttributes in configuration descriptor */
/* C_RESERVED must always be set */ /* C_RESERVED must always be set */
#define C_RESERVED (1U<<7) #define C_RESERVED (1U<<7)
@ -70,3 +70,5 @@
#define E_DATA (0x00) #define E_DATA (0x00)
#define E_FEEDBACK (0x10) #define E_FEEDBACK (0x10)
#define E_IMPLICIT_FEEDBACK (0x20) #define E_IMPLICIT_FEEDBACK (0x20)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -19,170 +19,241 @@
#include "mbed.h" #include "mbed.h"
#include "USBDevice_Types.h" #include "USBDevice_Types.h"
#include "USBHAL.h" #include "USBPhy.h"
#include "mbed_critical.h"
class USBDevice: public USBHAL { /**
* \defgroup usb_device USB Device
*
*/
/**
* \defgroup usb_device_core Core
*
* @ingroup usb_device
*/
/**
* Core USB Device driver
*
* USB driver which wraps and provides synchronization for a USBPhy object.
*
* @ingroup usb_device_core
*/
class USBDevice: public USBPhyEvents {
public: public:
typedef void (USBDevice::*ep_cb_t)(usb_ep_t endpoint);
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
*
* This function uses a target's built in USBPhy.
*
* @param vendor_id The USB vendor ID
* @param product_id The USB product ID
* @param product_release The device release number
*/
USBDevice(uint16_t vendor_id, uint16_t product_id, uint16_t product_release); USBDevice(uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/* /**
* 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);
/**
* 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.
*/
void deinit();
/**
* Check if the device is configured * Check if the device is configured
* *
* @returns true if configured, false otherwise * @returns true if configured, false otherwise
*/ */
bool configured(void); bool configured();
/* /**
* Connect a device * Connect a device
* *
* @param blocking: block if not configured * @param blocking: block if not configured
*/ */
void connect(bool blocking = true); void connect(bool blocking = true);
/* /**
* Disconnect a device * Disconnect a device
*/ */
void disconnect(void); 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 * Add an endpoint
* *
* @param endpoint endpoint which will be added * @param endpoint Endpoint to enable
* @param maxPacket Maximum size of a packet which can be sent for this endpoint * @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 * @returns true if successful, false otherwise
*/ */
bool addEndpoint(uint8_t endpoint, uint32_t maxPacket); bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, ep_cb_t callback = NULL);
/* /**
* Start a reading on a certain endpoint. * Add an endpoint
* You can access the result of the reading by USBDevice_read
* *
* @param endpoint endpoint which will be read * @param endpoint Endpoint to enable
* @param maxSize the maximum length that can be read * @param max_packet Maximum size of a packet which can be sent or received on this endpoint
* @return true if successful * @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 readStart(uint8_t endpoint, uint32_t maxSize); template<typename T>
bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, void (T::*callback)(usb_ep_t endpoint))
/*
* Read a certain endpoint. Before calling this function, USBUSBDevice_readStart
* must be called.
*
* Warning: blocking
*
* @param endpoint endpoint which will be read
* @param buffer buffer will be filled with the data received
* @param size the number of bytes read will be stored in *size
* @param maxSize the maximum length that can be read
* @returns true if successful
*/
bool readEP(uint8_t endpoint, uint8_t *buffer, uint32_t *size, uint32_t maxSize);
/*
* Read a certain endpoint.
*
* Warning: non blocking
*
* @param endpoint endpoint which will be read
* @param buffer buffer will be filled with the data received (if data are available)
* @param size the number of bytes read will be stored in *size
* @param maxSize the maximum length that can be read
* @returns true if successful
*/
bool readEP_NB(uint8_t endpoint, uint8_t *buffer, uint32_t *size, uint32_t maxSize);
/*
* Write a certain endpoint.
*
* Warning: blocking
*
* @param endpoint endpoint to write
* @param buffer data contained in buffer will be write
* @param size the number of bytes to write
* @param maxSize the maximum length that can be written on this endpoint
*/
bool write(uint8_t endpoint, uint8_t *buffer, uint32_t size, uint32_t maxSize);
/*
* Write a certain endpoint.
*
* Warning: non blocking
*
* @param endpoint endpoint to write
* @param buffer data contained in buffer will be write
* @param size the number of bytes to write
* @param maxSize the maximum length that can be written on this endpoint
*/
bool writeNB(uint8_t endpoint, uint8_t *buffer, uint32_t size, uint32_t maxSize);
/*
* Called by USBDevice layer on bus reset. Warning: Called in ISR context
*
* May be used to reset state
*/
virtual void USBCallback_busReset(void) {};
/*
* Called by USBDevice on Endpoint0 request. Warning: Called in ISR context
* This is used to handle extensions to standard requests
* and class specific requests
*
* @returns true if class handles this request
*/
virtual bool USBCallback_request()
{ {
return false; return endpoint_add(endpoint, max_packet, type, static_cast<ep_cb_t>(callback));
}; }
/* /**
* Called by USBDevice on Endpoint0 request completion * Remove an endpoint
* if the 'notify' flag has been set to true. Warning: Called in ISR context
* *
* In this case it is used to indicate that a HID report has * @param endpoint Endpoint to disable
* been received from the host on endpoint 0 * @note This endpoint must already have been setup with endpoint_add
*
* @param buf buffer received on endpoint 0
* @param length length of this buffer
*/ */
virtual void USBCallback_requestCompleted(uint8_t *buf, uint32_t length) {}; void endpoint_remove(usb_ep_t endpoint);
/* /**
* Called by USBDevice layer. Set configuration of the device. * Stall an endpoint
* For instance, you can add all endpoints that you need on this function.
* *
* @param configuration Number of the configuration * @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
*/ */
virtual bool USBCallback_setConfiguration(uint8_t configuration) void endpoint_stall(usb_ep_t endpoint);
{
return false;
};
/* /**
* Called by USBDevice layer. Set interface/alternate of the device. * Unstall an endpoint
*
* @param endpoint Endpoint to unstall
* @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
* *
* @param interface Number of the interface to be configured * Return the currently configured maximum packet size, wMaxPacketSize,
* @param alternate Number of the alternate to be configured * for this endpoint.
* @returns true if class handles this request * @note This endpoint must already have been setup with endpoint_add
*/ */
virtual bool USBCallback_setInterface(uint16_t interface, uint8_t alternate) uint32_t endpoint_max_packet_size(usb_ep_t endpoint);
{
return false; /** Start a read on the given endpoint
}; *
* After the read is finished call read_start to get the result.
*
* @param endpoint endpoint to perform the read on
* @return true if the read was started, false if no more reads can be started
* @note This endpoint must already have been setup with endpoint_add
*/
bool read_start(usb_ep_t endpoint);
/**
* Finish a read on the given endpoint
*
* Get the contents of a read started with read_start. To ensure all
* the data from this endpoint is read make sure the buffer and size
* passed is at least as big as the maximum packet for this endpoint.
*
* @param endpoint endpoint to read data from
* @param buffer buffer to fill with read data
* @param max_size the total size of the data buffer. This must be at least
* the max packet size of this endpoint
* @param size The size of data that was read
* @return true if the read was completed, otherwise false
* @note This endpoint must already have been setup with endpoint_add
*/
bool read_finish(usb_ep_t endpoint, uint8_t *buffer, uint32_t max_size, uint32_t *size);
/**
* Write a data to the given endpoint
*
* Write data to an endpoint.
*
* @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(usb_ep_t endpoint, uint8_t *buffer, uint32_t size);
/* /*
* Get device descriptor. * Get device descriptor.
* *
* @returns pointer to the device descriptor * @returns pointer to the device descriptor
*/ */
virtual const uint8_t *deviceDesc(); virtual const uint8_t *device_desc();
/* /*
* Get configuration descriptor * Get configuration descriptor
* *
* @returns pointer to the configuration descriptor * @returns pointer to the configuration descriptor
*/ */
virtual const uint8_t *configurationDesc() virtual const uint8_t *configuration_desc()
{ {
return NULL; return NULL;
}; };
@ -192,93 +263,308 @@ public:
* *
* @return pointer to the string lang id descriptor * @return pointer to the string lang id descriptor
*/ */
virtual const uint8_t *stringLangidDesc(); virtual const uint8_t *string_langid_desc();
/* /*
* Get string manufacturer descriptor * Get string manufacturer descriptor
* *
* @returns pointer to the string manufacturer descriptor * @returns pointer to the string manufacturer descriptor
*/ */
virtual const uint8_t *stringImanufacturerDesc(); virtual const uint8_t *string_imanufacturer_desc();
/* /*
* Get string product descriptor * Get string product descriptor
* *
* @returns pointer to the string product descriptor * @returns pointer to the string product descriptor
*/ */
virtual const uint8_t *stringIproductDesc(); virtual const uint8_t *string_iproduct_desc();
/* /*
* Get string serial descriptor * Get string serial descriptor
* *
* @returns pointer to the string serial descriptor * @returns pointer to the string serial descriptor
*/ */
virtual const uint8_t *stringIserialDesc(); virtual const uint8_t *string_iserial_desc();
/* /*
* Get string configuration descriptor * Get string configuration descriptor
* *
* @returns pointer to the string configuration descriptor * @returns pointer to the string configuration descriptor
*/ */
virtual const uint8_t *stringIConfigurationDesc(); virtual const uint8_t *string_iconfiguration_desc();
/* /*
* Get string interface descriptor * Get string interface descriptor
* *
* @returns pointer to the string interface descriptor * @returns pointer to the string interface descriptor
*/ */
virtual const uint8_t *stringIinterfaceDesc(); virtual const uint8_t *string_iinterface_desc();
/* /*
* Get the length of the report descriptor * Get the length of the report descriptor
* *
* @returns length of the report descriptor * @returns length of the report descriptor
*/ */
virtual uint16_t reportDescLength() virtual uint16_t report_desc_dength()
{ {
return 0; return 0;
}; };
protected: protected:
virtual void busReset(void);
virtual void EP0setupCallback(void);
virtual void EP0out(void);
virtual void EP0in(void);
virtual void connectStateChanged(unsigned int connected);
virtual void suspendStateChanged(unsigned int suspended);
uint8_t *findDescriptor(uint8_t descriptorType);
CONTROL_TRANSFER *getTransferPtr(void);
uint16_t VENDOR_ID; /**
uint16_t PRODUCT_ID; * Called by USBDevice layer on power state change.
uint16_t PRODUCT_RELEASE; *
uint8_t deviceDescriptor[18]; * @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
* @return A descriptor of the given type or NULL if none were found
*/
uint8_t *find_descriptor(uint8_t descriptor_type);
/**
* 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: private:
bool addRateFeedbackEndpoint(uint8_t endpoint, uint32_t maxPacket); // USBPhyEvents
bool requestGetDescriptor(void); virtual void power(bool powered);
bool controlOut(void); virtual void suspend(bool suspended);
bool controlIn(void); virtual void sof(int frame_number);
bool requestSetAddress(void); virtual void reset();
bool requestSetConfiguration(void); virtual void ep0_setup();
bool requestSetFeature(void); virtual void ep0_out();
bool requestClearFeature(void); virtual void ep0_in();
bool requestGetStatus(void); virtual void out(usb_ep_t endpoint);
bool requestSetup(void); virtual void in(usb_ep_t endpoint);
bool controlSetup(void);
void decodeSetupPacket(uint8_t *data, SETUP_PACKET *packet);
bool requestGetConfiguration(void);
bool requestGetInterface(void);
bool requestSetInterface(void);
CONTROL_TRANSFER transfer; bool _request_get_descriptor();
USB_DEVICE device; 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);
uint16_t currentInterface; struct endpoint_info_t {
uint8_t currentAlternate; void (USBDevice::*callback)(usb_ep_t endpoint);
uint16_t max_packet_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
};
struct control_transfer_t {
setup_packet_t setup;
uint8_t *ptr;
uint32_t remaining;
uint8_t direction;
bool zlp;
bool notify;
ControlState stage;
bool user_callback;
};
endpoint_info_t _endpoint_info[32 - 2];
USBPhy *_phy;
bool _initialized;
control_transfer_t _transfer;
usb_device_t _device;
uint32_t _max_packet_size_ep0;
bool _setup_ready;
bool _abort_control;
uint16_t _current_interface;
uint8_t _current_alternate;
uint32_t _locked;
}; };
#endif #endif

View File

@ -49,33 +49,4 @@
#define DESCRIPTOR_TYPE(wValue) (wValue >> 8) #define DESCRIPTOR_TYPE(wValue) (wValue >> 8)
#define DESCRIPTOR_INDEX(wValue) (wValue & 0xff) #define DESCRIPTOR_INDEX(wValue) (wValue & 0xff)
typedef struct {
struct {
uint8_t dataTransferDirection;
uint8_t Type;
uint8_t Recipient;
} bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} SETUP_PACKET;
typedef struct {
SETUP_PACKET setup;
uint8_t *ptr;
uint32_t remaining;
uint8_t direction;
bool zlp;
bool notify;
} CONTROL_TRANSFER;
typedef enum {ATTACHED, POWERED, DEFAULT, ADDRESS, CONFIGURED} DEVICE_STATE;
typedef struct {
volatile DEVICE_STATE state;
uint8_t configuration;
bool suspended;
} USB_DEVICE;
#endif #endif