From 3d68ea280790351d0320fc4e712c51b820a5522a Mon Sep 17 00:00:00 2001 From: Amir Hammad Date: Fri, 9 Sep 2016 18:37:22 +0200 Subject: make hid_mouse driver generic HID driver + keyboard support + SET_REPORT commands - usually leds on keyboards (WIP) - missing parsing of HID report descriptor Signed-off-by: Amir Hammad --- src/CMakeLists.txt | 4 +- src/demo.c | 26 ++- src/usbh_driver_hid.c | 537 ++++++++++++++++++++++++++++++++++++++++++++ src/usbh_driver_hid_mouse.c | 298 ------------------------ 4 files changed, 558 insertions(+), 307 deletions(-) create mode 100644 src/usbh_driver_hid.c delete mode 100644 src/usbh_driver_hid_mouse.c (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3165b51..f8478f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,7 @@ add_library (usbhost ${inc}/usbh_core.h ${inc}/usbh_driver_ac_midi.h ${inc}/usbh_driver_gp_xbox.h - ${inc}/usbh_driver_hid_mouse.h + ${inc}/usbh_driver_hid.h ${inc}/usbh_driver_hub.h ${inc}/usbh_lld_stm32f4.h ${inc}/driver/usbh_device_driver.h @@ -23,7 +23,7 @@ add_library (usbhost usbh_driver_ac_midi.c usbh_driver_ac_midi_private.h usbh_driver_gp_xbox.c - usbh_driver_hid_mouse.c + usbh_driver_hid.c usbh_driver_hub.c usbh_driver_hub_private.h usbh_lld_stm32f4.c diff --git a/src/demo.c b/src/demo.c index 3fedbd9..995e68c 100644 --- a/src/demo.c +++ b/src/demo.c @@ -23,7 +23,7 @@ #include "usart_helpers.h" /// provides LOG_PRINTF macros used for debugging #include "usbh_core.h" /// provides usbh_init() and usbh_poll() #include "usbh_lld_stm32f4.h" /// provides low level usb host driver for stm32f4 platform -#include "usbh_driver_hid_mouse.h" /// provides usb device driver Human Interface Device - type mouse +#include "usbh_driver_hid.h" /// provides generic usb device driver for Human Interface Device (HID) #include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported) #include "usbh_driver_gp_xbox.h" /// provides usb device driver for Gamepad: Microsoft XBOX compatible Controller #include "usbh_driver_ac_midi.h" /// provides usb device driver for midi class devices @@ -116,7 +116,7 @@ static void gpio_setup(void) static const usbh_dev_driver_t *device_drivers[] = { &usbh_hub_driver, - &usbh_hid_mouse_driver, + &usbh_hid_driver, &usbh_gp_xbox_driver, &usbh_midi_driver, 0 @@ -148,17 +148,29 @@ static const gp_xbox_config_t gp_xbox_config = { .notify_disconnected = &gp_xbox_disconnected }; -static void mouse_in_message_handler(uint8_t device_id, const uint8_t *data) +static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length) { (void)device_id; (void)data; + if (length < 4) { + LOG_PRINTF("data too short, type=%d\n", hid_get_type(device_id)); + return; + } + // print only first 4 bytes, since every mouse should have at least these four set. // Report descriptors are not read by driver for now, so we do not know what each byte means - LOG_PRINTF("MOUSE EVENT %02X %02X %02X %02X \n", data[0], data[1], data[2], data[3]); + LOG_PRINTF("HID EVENT %02X %02X %02X %02X \n", data[0], data[1], data[2], data[3]); + if (hid_get_type(device_id) == HID_TYPE_KEYBOARD) { + static int x = 0; + if (x != data[2]) { + x = data[2]; + hid_set_report(device_id, x); + } + } } -static const hid_mouse_config_t mouse_config = { - .mouse_in_message_handler = &mouse_in_message_handler +static const hid_config_t hid_config = { + .hid_in_message_handler = &hid_in_message_handler }; static void midi_in_message_handler(int device_id, uint8_t *data) @@ -200,7 +212,7 @@ int main(void) * * Pass configuration struct where the callbacks are defined */ - hid_mouse_driver_init(&mouse_config); + hid_driver_init(&hid_config); hub_driver_init(); gp_xbox_driver_init(&gp_xbox_config); midi_driver_init(&midi_config); diff --git a/src/usbh_driver_hid.c b/src/usbh_driver_hid.c new file mode 100644 index 0000000..21fa262 --- /dev/null +++ b/src/usbh_driver_hid.c @@ -0,0 +1,537 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2016 Amir Hammad + * + * + * libusbhost is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "usbh_core.h" +#include "driver/usbh_device_driver.h" +#include "usbh_driver_hid.h" +#include "usart_helpers.h" + +#include +#include +#include + +#define USB_HID_SET_REPORT 0x09 +#define USB_HID_SET_IDLE 0x0A + +enum STATES { + STATE_INACTIVE, + STATE_READING_REQUEST, + STATE_READING_COMPLETE_AND_CHECK_REPORT, + STATE_SET_REPORT_EMPTY_READ, + STATE_SET_CONFIGURATION_REQUEST, + STATE_SET_CONFIGURATION_EMPTY_READ, + STATE_GET_REPORT_DESCRIPTOR_READ_SETUP,// configuration is complete at this point. We write request + STATE_GET_REPORT_DESCRIPTOR_READ, + STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE,// after the read finishes, we parse that descriptor + STATE_SET_IDLE, + STATE_SET_IDLE_COMPLETE, +}; + +enum REPORT_STATE { + REPORT_STATE_NULL, + REPORT_STATE_READY, + REPORT_STATE_WRITE_PENDING, + REPORT_STATE_WRITE_PENDING_DATA, + REPORT_STATE_EMPTY_READ, +}; + +struct _hid_device { + usbh_device_t *usbh_device; + uint8_t buffer[USBH_HID_BUFFER]; + uint16_t endpoint_in_maxpacketsize; + uint8_t endpoint_in_address; + enum STATES state_next; + uint8_t endpoint_in_toggle; + uint8_t device_id; + uint8_t configuration_value; + uint16_t report0_length; + enum REPORT_STATE report_state; + uint8_t report_data[USBH_HID_REPORT_BUFFER]; + uint8_t report_data_length; + enum HID_TYPE hid_type; + uint8_t interface_number; +}; +typedef struct _hid_device hid_device_t; + +struct hid_report_decriptor { + struct usb_hid_descriptor header; + struct _report_descriptor_info { + uint8_t bDescriptorType; + uint16_t wDescriptorLength; + } __attribute__((packed)) report_descriptors_info[]; +} __attribute__((packed)); + +static hid_device_t hid_device[USBH_HID_MAX_DEVICES]; +static hid_config_t hid_config; + +static bool initialized = false; + +void hid_driver_init(const hid_config_t *config) +{ + uint32_t i; + + initialized = true; + + hid_config = *config; + for (i = 0; i < USBH_HID_MAX_DEVICES; i++) { + hid_device[i].state_next = STATE_INACTIVE; + } +} + +static void *init(void *usbh_dev) +{ + if (!initialized) { + LOG_PRINTF("\n%s/%d : driver not initialized\r\n", __FILE__, __LINE__); + return 0; + } + + uint32_t i; + hid_device_t *drvdata = 0; + + // find free data space for HID device + for (i = 0; i < USBH_HID_MAX_DEVICES; i++) { + if (hid_device[i].state_next == STATE_INACTIVE) { + drvdata = &hid_device[i]; + drvdata->device_id = i; + drvdata->endpoint_in_address = 0; + drvdata->endpoint_in_toggle = 0; + drvdata->report0_length = 0; + drvdata->usbh_device = (usbh_device_t *)usbh_dev; + drvdata->report_state = REPORT_STATE_NULL; + drvdata->hid_type = HID_TYPE_NONE; + break; + } + } + + return drvdata; +} + +static void parse_report_descriptor(hid_device_t *hid, const uint8_t *buffer, uint32_t length) +{ + // TODO + // Do some parsing! + // add some checks + hid->report_state = REPORT_STATE_READY; + + // TODO: parse this from buffer! + hid->report_data_length = 1; + (void)buffer; + (void)length; +} + +/** + * Returns true if all needed data are parsed + */ +static bool analyze_descriptor(void *drvdata, void *descriptor) +{ + hid_device_t *hid = (hid_device_t *)drvdata; + uint8_t desc_type = ((uint8_t *)descriptor)[1]; + switch (desc_type) { + case USB_DT_CONFIGURATION: + { + const struct usb_config_descriptor * cfg = (const struct usb_config_descriptor*)descriptor; + hid->configuration_value = cfg->bConfigurationValue; + } + break; + + case USB_DT_DEVICE: + { + const struct usb_device_descriptor *devDesc = (const struct usb_device_descriptor *)descriptor; + (void)devDesc; + } + break; + + case USB_DT_INTERFACE: + { + const struct usb_interface_descriptor *ifDesc = (const struct usb_interface_descriptor *)descriptor; + if (ifDesc->bInterfaceProtocol == 0x01) { + hid->hid_type = HID_TYPE_KEYBOARD; + hid->interface_number = ifDesc->bInterfaceNumber; + } else if (ifDesc->bInterfaceProtocol == 0x02) { + hid->hid_type = HID_TYPE_MOUSE; + hid->interface_number = ifDesc->bInterfaceNumber; + } + } + break; + + case USB_DT_ENDPOINT: + { + const struct usb_endpoint_descriptor *ep = (const struct usb_endpoint_descriptor *)descriptor; + if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) { + uint8_t epaddr = ep->bEndpointAddress; + if (epaddr & (1<<7)) { + hid->endpoint_in_address = epaddr&0x7f; + if (ep->wMaxPacketSize < USBH_HID_BUFFER) { + hid->endpoint_in_maxpacketsize = ep->wMaxPacketSize; + } else { + hid->endpoint_in_maxpacketsize = USBH_HID_BUFFER; + } + } + } + } + break; + + case USB_DT_HID: + { + const struct hid_report_decriptor *desc = (const struct hid_report_decriptor *)descriptor; + if (desc->header.bNumDescriptors > 0 && desc->report_descriptors_info[0].bDescriptorType == USB_DT_REPORT) { + hid->report0_length = desc->report_descriptors_info[0].wDescriptorLength; + } + } + break; + + default: + break; + } + + if (hid->endpoint_in_address && hid->report0_length) { + hid->state_next = STATE_SET_CONFIGURATION_REQUEST; + return true; + } + + return false; +} + +static void report_event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) +{ + hid_device_t *hid = (hid_device_t *)dev->drvdata; + switch (hid->report_state) { + case REPORT_STATE_WRITE_PENDING: + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hid->report_state = REPORT_STATE_WRITE_PENDING_DATA; + + device_xfer_control_write_data(hid->report_data, hid->report_data_length, report_event, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + break; + + case REPORT_STATE_WRITE_PENDING_DATA: + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hid->report_state = REPORT_STATE_EMPTY_READ; + device_xfer_control_read(0, 0, report_event, dev); + LOG_PRINTF("reading empty\n"); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + break; + + + case REPORT_STATE_EMPTY_READ: + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hid->report_state = REPORT_STATE_READY; + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + break; + default: + break; + } +} + +static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) +{ + hid_device_t *hid = (hid_device_t *)dev->drvdata; + + switch (hid->state_next) { + case STATE_READING_COMPLETE_AND_CHECK_REPORT: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + if (hid_config.hid_in_message_handler) { + hid_config.hid_in_message_handler(hid->device_id, hid->buffer, cb_data.transferred_length); + } + hid->state_next = STATE_READING_REQUEST; + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + } + break; + + case STATE_SET_CONFIGURATION_EMPTY_READ: + { + LOG_PRINTF("|empty packet read|"); + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_SETUP; + device_xfer_control_read(0, 0, event, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + } + break; + + case STATE_GET_REPORT_DESCRIPTOR_READ_SETUP: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + { + hid->endpoint_in_toggle = 0; + // We support only the first report descriptor with index 0 + + // limit the size of the report descriptor! + if (hid->report0_length > USBH_HID_BUFFER) { + hid->report0_length = USBH_HID_BUFFER; + } + + struct usb_setup_data setup_data; + + setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_INTERFACE; + setup_data.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_data.wValue = USB_DT_REPORT << 8; + setup_data.wIndex = 0; + setup_data.wLength = hid->report0_length; + + hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ; + device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev); + + } + break; + + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + } + break; + + case STATE_GET_REPORT_DESCRIPTOR_READ: + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE; + device_xfer_control_read(hid->buffer, hid->report0_length, event, dev); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + } + break; + + + case STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE: // read complete, SET_IDLE to 0 + { + switch (cb_data.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + LOG_PRINTF("READ REPORT COMPLETE \n"); + hid->state_next = STATE_READING_REQUEST; + hid->endpoint_in_toggle = 0; + + parse_report_descriptor(hid, hid->buffer, cb_data.transferred_length); + break; + + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + ERROR(cb_data.status); + hid->state_next = STATE_INACTIVE; + break; + } + } + break; + + default: + break; + } +} + + +static void read_hid_in_endpoint(void *drvdata) +{ + hid_device_t *hid = (hid_device_t *)drvdata; + usbh_packet_t packet; + + packet.address = hid->usbh_device->address; + packet.data.in = &hid->buffer[0]; + packet.datalen = hid->endpoint_in_maxpacketsize; + packet.endpoint_address = hid->endpoint_in_address; + packet.endpoint_size_max = hid->endpoint_in_maxpacketsize; + packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT; + packet.speed = hid->usbh_device->speed; + packet.callback = event; + packet.callback_arg = hid->usbh_device; + packet.toggle = &hid->endpoint_in_toggle; + + hid->state_next = STATE_READING_COMPLETE_AND_CHECK_REPORT; + usbh_read(hid->usbh_device, &packet); +} + +/** + * @param time_curr_us - monotically rising time + * unit is microseconds + * @see usbh_poll() + */ +static void poll(void *drvdata, uint32_t time_curr_us) +{ + (void)time_curr_us; + + hid_device_t *hid = (hid_device_t *)drvdata; + usbh_device_t *dev = hid->usbh_device; + switch (hid->state_next) { + case STATE_READING_REQUEST: + { + read_hid_in_endpoint(drvdata); + } + break; + + case STATE_SET_CONFIGURATION_REQUEST: + { + struct usb_setup_data setup_data; + + setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE; + setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.wValue = hid->configuration_value; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + hid->state_next = STATE_SET_CONFIGURATION_EMPTY_READ; + + device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev); + } + break; + + default: + // do nothing - probably transfer is in progress + break; + } +} + +static void remove(void *drvdata) +{ + hid_device_t *hid = (hid_device_t *)drvdata; + hid->state_next = STATE_INACTIVE; + hid->endpoint_in_address = 0; +} + +bool hid_set_report(uint8_t device_id, uint8_t val) +{ + if (device_id >= USBH_HID_MAX_DEVICES) { + LOG_PRINTF("invalid device id"); + return false; + } + + hid_device_t *hid = &hid_device[device_id]; + if (hid->report_state != REPORT_STATE_READY) { + LOG_PRINTF("reporting is not ready\n"); + // store and update afterwards + return false; + } + + if (hid->report_data_length == 0) { + LOG_PRINTF("reporting is not available (report len=0)\n"); + return false; + } + + struct usb_setup_data setup_data; + setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE; + setup_data.bRequest = USB_HID_SET_REPORT; + setup_data.wValue = 0x02 << 8; + setup_data.wIndex = hid->interface_number; + setup_data.wLength = hid->report_data_length; + + hid->report_data[0] = val; + + hid->report_state = REPORT_STATE_WRITE_PENDING; + device_xfer_control_write_setup(&setup_data, sizeof(setup_data), report_event, hid->usbh_device); + return true; +} + +bool hid_is_connected(uint8_t device_id) +{ + if (device_id >= USBH_HID_MAX_DEVICES) { + LOG_PRINTF("is connected: invalid device id"); + return false; + } + return hid_device[device_id].state_next == STATE_INACTIVE; +} + + +enum HID_TYPE hid_get_type(uint8_t device_id) +{ + if (hid_is_connected(device_id)) { + return HID_TYPE_NONE; + } + return hid_device[device_id].hid_type; +} + +static const usbh_dev_driver_info_t driver_info = { + .deviceClass = -1, + .deviceSubClass = -1, + .deviceProtocol = -1, + .idVendor = -1, + .idProduct = -1, + .ifaceClass = 0x03, // HID class + .ifaceSubClass = -1, + .ifaceProtocol = -1, // Do not care +}; + +const usbh_dev_driver_t usbh_hid_driver = { + .init = init, + .analyze_descriptor = analyze_descriptor, + .poll = poll, + .remove = remove, + .info = &driver_info +}; + + + diff --git a/src/usbh_driver_hid_mouse.c b/src/usbh_driver_hid_mouse.c deleted file mode 100644 index b52392f..0000000 --- a/src/usbh_driver_hid_mouse.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * This file is part of the libusbhost library - * hosted at http://github.com/libusbhost/libusbhost - * - * Copyright (C) 2015 Amir Hammad - * - * - * libusbhost is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#include "usbh_core.h" -#include "driver/usbh_device_driver.h" -#include "usbh_driver_hid_mouse.h" -#include "usart_helpers.h" - -#include - -enum STATES { - STATE_INACTIVE, - STATE_READING_COMPLETE, - STATE_READING_REQUEST, - STATE_SET_CONFIGURATION_REQUEST, - STATE_SET_CONFIGURATION_EMPTY_READ, - STATE_SET_CONFIGURATION_COMPLETE -}; - -struct _hid_mouse_device { - usbh_device_t *usbh_device; - uint8_t buffer[USBH_HID_MOUSE_BUFFER]; - uint16_t endpoint_in_maxpacketsize; - uint8_t endpoint_in_address; - enum STATES state_next; - uint8_t endpoint_in_toggle; - uint8_t device_id; - uint8_t configuration_value; -}; -typedef struct _hid_mouse_device hid_mouse_device_t; - -static hid_mouse_device_t mouse_device[USBH_HID_MOUSE_MAX_DEVICES]; -static const hid_mouse_config_t *mouse_config; - - - - -#include - -static bool initialized = false; - -void hid_mouse_driver_init(const hid_mouse_config_t *config) -{ - uint32_t i; - - initialized = true; - - mouse_config = config; - for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) { - mouse_device[i].state_next = STATE_INACTIVE; - } -} - -/** - * - * - */ -static void *init(void *usbh_dev) -{ - if (!initialized) { - LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__); - return 0; - } - - uint32_t i; - hid_mouse_device_t *drvdata = 0; - - // find free data space for mouse device - for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) { - if (mouse_device[i].state_next == STATE_INACTIVE) { - drvdata = &mouse_device[i]; - drvdata->device_id = i; - drvdata->endpoint_in_address = 0; - drvdata->endpoint_in_toggle = 0; - drvdata->usbh_device = (usbh_device_t *)usbh_dev; - break; - } - } - - return drvdata; -} - -/** - * Returns true if all needed data are parsed - */ -static bool analyze_descriptor(void *drvdata, void *descriptor) -{ - hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata; - uint8_t desc_type = ((uint8_t *)descriptor)[1]; - switch (desc_type) { - case USB_DT_CONFIGURATION: - { - struct usb_config_descriptor *cfg = (struct usb_config_descriptor*)descriptor; - mouse->configuration_value = cfg->bConfigurationValue; - } - break; - case USB_DT_DEVICE: - break; - case USB_DT_INTERFACE: - break; - case USB_DT_ENDPOINT: - { - struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor*)descriptor; - if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) { - uint8_t epaddr = ep->bEndpointAddress; - if (epaddr & (1<<7)) { - mouse->endpoint_in_address = epaddr&0x7f; - if (ep->wMaxPacketSize < USBH_HID_MOUSE_BUFFER) { - mouse->endpoint_in_maxpacketsize = ep->wMaxPacketSize; - } else { - mouse->endpoint_in_maxpacketsize = USBH_HID_MOUSE_BUFFER; - } - } - - if (mouse->endpoint_in_address) { - mouse->state_next = STATE_SET_CONFIGURATION_REQUEST; - return true; - } - } - } - break; - // TODO Class Specific descriptors - default: - break; - } - return false; -} - -static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data) -{ - hid_mouse_device_t *mouse = (hid_mouse_device_t *)dev->drvdata; - switch (mouse->state_next) { - case STATE_READING_COMPLETE: - { - switch (cb_data.status) { - case USBH_PACKET_CALLBACK_STATUS_OK: - case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: - mouse->state_next = STATE_READING_REQUEST; - break; - - case USBH_PACKET_CALLBACK_STATUS_EFATAL: - case USBH_PACKET_CALLBACK_STATUS_EAGAIN: - ERROR(cb_data.status); - mouse->state_next = STATE_INACTIVE; - break; - } - } - break; - - case STATE_SET_CONFIGURATION_EMPTY_READ: - { - LOG_PRINTF("|empty packet read|"); - switch (cb_data.status) { - case USBH_PACKET_CALLBACK_STATUS_OK: - mouse->state_next = STATE_SET_CONFIGURATION_COMPLETE; - device_xfer_control_read(0, 0, event, dev); - break; - - case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: - case USBH_PACKET_CALLBACK_STATUS_EFATAL: - case USBH_PACKET_CALLBACK_STATUS_EAGAIN: - ERROR(cb_data.status); - mouse->state_next = STATE_INACTIVE; - break; - } - } - break; - case STATE_SET_CONFIGURATION_COMPLETE: // Configured - { - switch (cb_data.status) { - case USBH_PACKET_CALLBACK_STATUS_OK: - mouse->state_next = STATE_READING_REQUEST; - mouse->endpoint_in_toggle = 0; - LOG_PRINTF("\nMOUSE CONFIGURED\n"); - break; - - case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: - case USBH_PACKET_CALLBACK_STATUS_EFATAL: - case USBH_PACKET_CALLBACK_STATUS_EAGAIN: - ERROR(cb_data.status); - mouse->state_next = STATE_INACTIVE; - break; - } - } - break; - default: - break; - } -} - - -static void read_mouse_in(void *drvdata) -{ - hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata; - usbh_packet_t packet; - - packet.address = mouse->usbh_device->address; - packet.data.in = &mouse->buffer[0]; - packet.datalen = mouse->endpoint_in_maxpacketsize; - packet.endpoint_address = mouse->endpoint_in_address; - packet.endpoint_size_max = mouse->endpoint_in_maxpacketsize; - packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT; - packet.speed = mouse->usbh_device->speed; - packet.callback = event; - packet.callback_arg = mouse->usbh_device; - packet.toggle = &mouse->endpoint_in_toggle; - - mouse->state_next = STATE_READING_COMPLETE; - usbh_read(mouse->usbh_device, &packet); - - // LOG_PRINTF("@MOUSE EP1 | \n"); - -} - -/** - * @param time_curr_us - monotically rising time - * unit is microseconds - * @see usbh_poll() - */ -static void poll(void *drvdata, uint32_t time_curr_us) -{ - (void)time_curr_us; - - hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata; - usbh_device_t *dev = mouse->usbh_device; - switch (mouse->state_next) { - case STATE_READING_REQUEST: - { - read_mouse_in(drvdata); - } - break; - - case STATE_SET_CONFIGURATION_REQUEST: - { - struct usb_setup_data setup_data; - - setup_data.bmRequestType = 0b00000000; - setup_data.bRequest = USB_REQ_SET_CONFIGURATION; - setup_data.wValue = mouse->configuration_value; - setup_data.wIndex = 0; - setup_data.wLength = 0; - - mouse->state_next = STATE_SET_CONFIGURATION_EMPTY_READ; - - device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev); - } - break; - - default: - // do nothing - probably transfer is in progress - break; - } -} - -static void remove(void *drvdata) -{ - hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata; - mouse->state_next = STATE_INACTIVE; - mouse->endpoint_in_address = 0; -} - -static const usbh_dev_driver_info_t driver_info = { - .deviceClass = -1, - .deviceSubClass = -1, - .deviceProtocol = -1, - .idVendor = -1, - .idProduct = -1, - .ifaceClass = 0x03, - .ifaceSubClass = -1, - .ifaceProtocol = 0x02 -}; - -const usbh_dev_driver_t usbh_hid_mouse_driver = { - .init = init, - .analyze_descriptor = analyze_descriptor, - .poll = poll, - .remove = remove, - .info = &driver_info -}; -- cgit