mbed-os/drivers/internal/USBDevice.h

624 lines
17 KiB
C++

/*
* 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 <stddef.h>
#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<void()> 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<typename T>
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<ep_cb_t>(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<void()> 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