mbed-os/usb/device/USBHID/USBHID.cpp

494 lines
14 KiB
C++

/* 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 "stdint.h"
#include "USBHID.h"
#include "EndpointResolver.h"
class USBHID::AsyncSend: public AsyncOp {
public:
AsyncSend(const HID_REPORT *report): AsyncOp(NULL), report(report), result(false)
{
}
const HID_REPORT *report;
bool result;
};
class USBHID::AsyncRead: public AsyncOp {
public:
AsyncRead(HID_REPORT *report): AsyncOp(NULL), report(report), result(false)
{
}
HID_REPORT *report;
bool result;
};
USBHID::USBHID(uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect)
: USBDevice(vendor_id, product_id, product_release)
{
_init(output_report_length, input_report_length, connect);
}
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, bool connect)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init(output_report_length, input_report_length, connect);
}
void USBHID::_init(uint8_t output_report_length, uint8_t input_report_length, bool connect)
{
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;
init();
if (connect) {
USBDevice::connect();
}
}
void USBHID::wait_connected()
{
lock();
AsyncOp wait_op(NULL);
wait_op.start(&_connect_list);
if (configured()) {
wait_op.complete();
}
unlock();
wait_op.wait();
}
bool USBHID::send(const HID_REPORT *report)
{
lock();
if (!configured()) {
unlock();
return false;
}
if (send_nb(report)) {
unlock();
return true;
}
AsyncSend send_op(report);
send_op.start(&_send_list);
unlock();
send_op.wait();
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();
if (!configured()) {
unlock();
return false;
}
if (read_nb(report)) {
unlock();
return true;
}
AsyncRead read_op(report);
read_op.start(&_read_list);
unlock();
read_op.wait();
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(usb_ep_t endpoint)
{
assert_locked();
write_finish(_int_in);
_send_idle = true;
AsyncSend *send_op = _send_list.head();
if (send_op != NULL) {
if (send_nb(send_op->report)) {
send_op->result = true;
send_op->complete();
}
} else {
report_tx();
}
}
void USBHID::_read_isr(usb_ep_t endpoint)
{
assert_locked();
_output_report.length = read_finish(_int_out);
_read_idle = true;
AsyncRead *read_op = _read_list.head();
if (read_op != NULL) {
if (read_nb(read_op->report)) {
read_op->result = true;
read_op->complete();
}
} else {
report_rx();
}
}
void USBHID::_connect_wake_all()
{
assert_locked();
AsyncOp *wait_op = _connect_list.head();
while (wait_op != NULL) {
wait_op->complete();
wait_op = _connect_list.head();
}
}
void USBHID::_send_abort_all()
{
assert_locked();
if (!_send_idle) {
endpoint_abort(_int_in);
_send_idle = true;
}
AsyncSend *tx_cur = _send_list.head();
while (tx_cur != NULL) {
tx_cur->result = false;
tx_cur->complete();
tx_cur = _send_list.head();
}
}
void USBHID::_read_abort_all()
{
assert_locked();
if (!_read_idle) {
endpoint_abort(_int_out);
_read_idle = true;
}
AsyncRead *rx_cur = _read_list.head();
while (rx_cur != NULL) {
rx_cur->result = false;
rx_cur->complete();
rx_cur = _read_list.head();
}
}
uint16_t USBHID::report_desc_length()
{
report_desc();
return reportLength;
}
void USBHID::callback_state_change(DeviceState new_state)
{
if (new_state == Configured) {
_connect_wake_all();
} else {
_send_abort_all();
_read_abort_all();
}
}
//
// 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, (uint8_t*)&_output_report, 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 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;
}