Add USBPhy, USB HAL and Utility class

Add the USBPhy and USBPhyEvents abstract classes, the HAL header using
this class and the EndpointResolver utility class.
pull/9768/head
Russ Butler 2018-02-07 16:32:43 -06:00
parent e03b3b68c1
commit 8ae123f2d4
7 changed files with 757 additions and 0 deletions

29
hal/mbed_usb_phy.cpp Normal file
View File

@ -0,0 +1,29 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 <stddef.h>
#include "usb_phy_api.h"
#include "mbed_error.h"
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
USBPhy *get_usb_phy()
{
error("This board does not have a hardware USB driver");
return NULL;
}
#endif

36
hal/usb_phy_api.h Normal file
View File

@ -0,0 +1,36 @@
/** \addtogroup hal */
/** @{*/
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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_USB_PHY_API_H
#define MBED_USB_PHY_API_H
#include "USBPhy.h"
/** Return a the USBPhy instance for this hardware
*
* For details on adding support for a USBPhy see the specification in USBPhy.h.
*
* @return A pointer to a USBPhy instance
* @note Calling this function on platforms without a USBPhy will result in an
* error
*/
USBPhy *get_usb_phy();
#endif
/** @}*/

302
platform/USBPhy.h Normal file
View File

@ -0,0 +1,302 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 USBPHY_H
#define USBPHY_H
#include "USBPhyTypes.h"
#include "USBPhyEvents.h"
/** Abstract interface to physical USB hardware
*
* # Defined behavior
* * Any endpoint configurations which fit in the parameters of the table returned
* by USBPhy::endpoint_table can be used.
* * All endpoints in any valid endpoint configuration can be used concurrently
* * Device supports use of at least one control, bulk, interrupt and
* isochronous in each direction at the same time - at least 8 endpoints.
* * Device supports all standard endpoint sizes (wMaxPacketSize)
* * Device can handle an interrupt latency of at least 100ms if reset is not being performed and address is not being set
* * USBPhyEvents events are only sent when USBPhy is in the initialized state
* * When unpowered only the USBPhyEvents::power event can be sent
* * On USB reset all endpoints are removed except for endpoint 0
* * USBPhyEvents::out and USBPhyEvents::in events only occur for endpoints which have been added
* * A call to USBPhy::ep0_write results in USBPhyEvents::in getting called if not
* interrupted by a power loss or reset
* * A call to endpoint_read followed by endpoint_read_result results in USBPhyEvents::out getting called if not
* interrupted by a power loss or reset
* * Endpoint 0 naks all transactions aside from setup packets until one
* of ep0_read, ep0_write or ep0_stall has been called
* * Endpoint 0 stall is automatically cleared on reception of a setup packet
*
* # Undefined behavior
* * Calling USBPhy::endpoint_add or USBPhy::endpoint_remove outside of the control requests SetInterface or SetConfiguration
* * Devices behavior is undefined if latency is greater than 2ms when address is being set - see USB spec 9.2.6.3
* * Devices behavior is undefined if latency is greater than 10ms when a reset occurs - see USB spec 7.1.7.5
* * Calling any of the USBPhy::endpoint_* functions on endpoint 0
*
* # Notes
* * Make sure USB packets are processed in the correct order when multiple packets are present.
* Typically IN endpoints should be handled before OUT endpoints if both are pending.
* * Setup packets may be resent if there is noise on the USB line. The USBPhy should be able
* to gracefully handle this scenario and respond to the setup packet with an ACK.
* * Bi-directional protocols making use of alternating IN and OUT phases should not rely
* on the last ACK an IN transfer to indicate that the OUT phase should start. Instead,
* the OUT phase should be started at the same time the last IN transfer is started. This
* is because the ACK to the last in transfer may be dropped if there is noise on the USB
* line. If dropped it will only get re-sent on the next IN phase. More info on this can be
* found in section 8.5.3.3 of the USB spec.
*
* @ingroup usb_device_core
*/
class USBPhy {
public:
USBPhy() {};
virtual ~USBPhy() {};
/**
* Initialize this USBPhy instance
*
* This function must be called before calling
* any other functions of this class, unless specifically
* noted.
*
* @param events Callback class to handle USB events
*/
virtual void init(USBPhyEvents *events) = 0;
/**
* Power down this USBPhy instance
*
* Disable interrupts and stop sending events.
*/
virtual void deinit() = 0;
/**
* Check if USB power is present
*
* Devices which don't support checking the USB power state
* must always return true.
*
* @return true if USB power is present, false otherwise
*/
virtual bool powered() = 0;
/**
* Make the USB phy visible to the USB host
*
* Enable either the D+ or D- pullup so the host can detect
* the presence of this device.
*/
virtual void connect() = 0;
/**
* Detach the USB phy
*
* Disable the D+ and D- pullup and stop responding to
* USB traffic.
*/
virtual void disconnect() = 0;
/**
* Set this device to the configured state
*
* Enable added endpoints if they are not enabled
* already.
*/
virtual void configure() = 0;
/**
* Leave the configured state
*
* This is a notification to the USBPhy indicating that the device
* is leaving the configured state. The USBPhy can disable all
* endpoints other than endpoint 0.
*
*/
virtual void unconfigure() = 0;
/**
* Enable the start of frame interrupt
*
* Call USBPhyEvents::sof on every frame.
*/
virtual void sof_enable() = 0;
/**
* Disable the start of frame interrupt
*
* Stop calling USBPhyEvents::sof.
*/
virtual void sof_disable() = 0;
/**
* Set the USBPhy's address
*
* @param address This device's USB address
*/
virtual void set_address(uint8_t address) = 0;
/**
* Wake upstream devices
*/
virtual void remote_wakeup() = 0;
/**
* Get the endpoint table
*
* This function returns a table which describes the endpoints
* can be used, the functionality of those endpoints and the
* resource cost.
*/
virtual const usb_ep_table_t* endpoint_table() = 0;
/**
* Set wMaxPacketSize of endpoint 0
*
* @param max_packet The wMaxPacketSize value for endpoint 0
* @return The actual size of endpoint 0
*/
virtual uint32_t ep0_set_max_packet(uint32_t max_packet) = 0;
/**
* Read the contents of the SETUP packet
*
* @param buffer Buffer to fill with data
* @param size Size of buffer passed in
*/
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size) = 0;
/**
* Start receiving a packet of up to wMaxPacketSize on endpoint 0
*/
virtual void ep0_read() = 0;
/**
* Read the contents of a received packet
*
* @param buffer Buffer to fill with the data read
* @param size Size of buffer
*/
virtual uint32_t ep0_read_result(uint8_t *buffer, uint32_t size) = 0;
/**
* Write a packet on endpoint 0
*
* @param buffer Buffer fill with data to send
* @param size Size of data to send
*/
virtual void ep0_write(uint8_t *buffer, uint32_t size) = 0;
/**
* Protocol stall on endpoint 0
*
* Stall all IN and OUT packets on endpoint 0 until a setup packet
* is received.
* @note The stall is cleared automatically when a setup packet is received
*/
virtual void ep0_stall() = 0;
/**
* Configure and enable an endpoint
*
* @param endpoint Endpoint to configure and enable
* @param max_packet The maximum packet size that can be sent or received
* @param type The type of endpoint this should be configured as -
* USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO
* @note This function cannot be used to configure endpoint 0. That must be done
* with ep0_set_max_packet
*/
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type) = 0;
/**
* Disable an endpoint
*
* @param endpoint Endpoint to disable
*/
virtual void endpoint_remove(usb_ep_t endpoint) = 0;
/**
* Perform a functional stall on the given endpoint
*
* Set the HALT feature for this endpoint so that all further
* communication is aborted.
*
* @param endpoint Endpoint to stall
*/
virtual void endpoint_stall(usb_ep_t endpoint) = 0;
/**
* Unstall the endpoint
*
* Clear the HALT feature on this endpoint so communication can
* resume.
*
* @param endpoint Endpoint to stall
*/
virtual void endpoint_unstall(usb_ep_t endpoint) = 0;
/**
* Start a read on the given endpoint
*
* @param endpoint Endpoint to start the read on
* @param max_packet A hint as to the wMaxPacketSize of this endpoint.
* This must match the size in endpoint_add.
* @return true if the read was successfully started, false otherwise
*/
virtual bool endpoint_read(usb_ep_t endpoint, uint32_t max_packet) = 0;
/**
* Finish a read on the given endpoint
*
* @param endpoint Endpoint to read data from
* @param data Buffer to fill with data
* @param size Size of buffer
* @param bytes_read The number of bytes in the current packet. This can be larger than
* the size parameter if the buffer passed in was too small.
* @return true if data was read false otherwise
*/
virtual bool endpoint_read_result(usb_ep_t endpoint, uint8_t *data, uint32_t size, uint32_t *bytes_read) = 0;
/**
* Start a write on the given endpoint
*
* @param endpoint Endpoint to write to
* @param data Buffer to write
* @param size Size of data to write
* @return true if the data was prepared for transmit, false otherwise
*/
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size) = 0;
/**
* Abort the current transfer if it has not yet been sent
*
* @param endpoint Endpoint to abort the transfer on. It is implementation defined
* if this function has an effect on receive endpoints.
*/
virtual void endpoint_abort(usb_ep_t endpoint) = 0;
/**
* Callback used for performing USB processing
*
* USBPhy processing should be triggered by calling USBPhyEvents::start_process
* and done inside process. All USBPhyEvents callbacks aside from
* USBPhyEvents::start_process must be called in the context of process
*/
virtual void process() = 0;
};
#endif

107
platform/USBPhyEvents.h Normal file
View File

@ -0,0 +1,107 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 USBPHY_EVENTS_H
#define USBPHY_EVENTS_H
#include "USBPhyTypes.h"
/** Event handler for USBPhy
*
* This class is the event handler for the USBPhy class. Any events generated
* by USBPhy are passed to this class via the virtual functions.
*
* @ingroup usb_device_core
*
*/
class USBPhyEvents {
public:
USBPhyEvents() {};
virtual ~USBPhyEvents() {};
/**
* Callback called when a bus reset occurs
* @note called in the contex of USBPhy::process
*/
virtual void reset() = 0;
/**
* Callback called when an endpoint 0 setup packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_setup() = 0;
/**
* Callback called when an endpoint 0 out packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_out() = 0;
/**
* Callback called when an endpoint 0 in packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_in() = 0;
/**
* Callback called USB power is applied or removed
*
* @param powered true if USB power is present, false otherwise
* @note called in the contex of USBPhy::process
*/
virtual void power(bool powered) = 0;
/**
* Callback called when entering or leaving suspend mode
*
* @param suspended true if entering suspend mode false otherwise
* @note called in the contex of USBPhy::process
*/
virtual void suspend(bool suspended) = 0;
/**
* Callback called on start of frame
*
* @param frame_number The current frame number
* @note This callback is enabled/disabled by
* calling USBPhy::sof_enable / USBPhy::sof_disable
* @note called in the contex of USBPhy::process
*/
virtual void sof(int frame_number) = 0;
/**
* Callback called on the reception of an OUT packet
*
* @param endpoint Endpoint which received the OUT packet
* @note called in the contex of USBPhy::process
*/
virtual void out(usb_ep_t endpoint) = 0;
/**
* Callback called on the transmission of an IN packet
*
* @param endpoint Endpoint which sent the IN packet
* @note called in the contex of USBPhy::process
*/
virtual void in(usb_ep_t endpoint) = 0;
/**
* Callback called to indicate the USB processing needs to be done
*/
virtual void start_process() = 0;
};
#endif

58
platform/USBPhyTypes.h Normal file
View File

@ -0,0 +1,58 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 USBPHY_TYPES_H
#define USBPHY_TYPES_H
#include <stdint.h>
typedef uint8_t usb_ep_t;
typedef enum {
USB_EP_TYPE_CTRL = 0,
USB_EP_TYPE_ISO = 1,
USB_EP_TYPE_BULK = 2,
USB_EP_TYPE_INT = 3
} usb_ep_type_t;
enum {
USB_EP_ATTR_ALLOW_CTRL = 1 << USB_EP_TYPE_CTRL,
USB_EP_ATTR_ALLOW_BULK = 1 << USB_EP_TYPE_BULK,
USB_EP_ATTR_ALLOW_INT = 1 << USB_EP_TYPE_INT,
USB_EP_ATTR_ALLOW_ISO = 1 << USB_EP_TYPE_ISO,
USB_EP_ATTR_ALLOW_ALL = USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_ALLOW_BULK |
USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_ALLOW_ISO,
USB_EP_ATTR_DIR_IN = 0 << 4,
USB_EP_ATTR_DIR_OUT = 1 << 4,
USB_EP_ATTR_DIR_IN_OR_OUT = 2 << 4,
USB_EP_ATTR_DIR_IN_AND_OUT = 3 << 4,
USB_EP_ATTR_DIR_MASK = 3 << 4
};
typedef uint8_t usb_ep_attr_t;
struct usb_ep_entry_t {
usb_ep_attr_t attributes;
uint8_t byte_cost;
uint16_t base_cost;
};
struct usb_ep_table_t {
uint32_t resources;
usb_ep_entry_t table[16];
};
#endif

View File

@ -0,0 +1,136 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 "mbed.h"
#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::endpoint_in(usb_ep_type_t type, uint32_t size)
{
int index = next_index(type, true);
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_out(usb_ep_type_t type, uint32_t size)
{
int index = next_index(type, false);
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);
}
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 && (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;
}

View File

@ -0,0 +1,89 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* 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 "mbed.h"
#include "USBPhy.h"
/**
* 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.
*
* @ingroup usb_device_core
*/
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);
/**
* 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