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

508 lines
13 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"
#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;
}
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, (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 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;
}