/* * 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. */ #include <stdint.h> #include <string.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; reportLength = 0; _input_report.length = 0; _output_report.length = 0; } 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, _output_report.data, 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; }