/** * \file * * * \author Andrea Righi * * \brief Generic USB mouse device driver. * * notest: avr * notest: arm */ #include "usb_hid.h" #include "usbmouse.h" #include "cfg/cfg_usbmouse.h" #define LOG_LEVEL USB_MOUSE_LOG_LEVEL #define LOG_FORMAT USB_MOUSE_LOG_FORMAT #include #include #include #include #include #include // cpu_relax() #include #include /* * HID device configuration (usb-mouse) */ #define USB_HID_VENDOR_ID USB_MOUSE_VENDOR_ID #define USB_HID_PRODUCT_ID USB_MOUSE_PRODUCT_ID #define USB_HID_INTERFACES 1 #define USB_HID_ENDPOINTS 1 #define USB_STRING_MANUFACTURER 1 #define USB_STRING_PRODUCT 2 #define USB_HID_REPORT_EP (USB_DIR_IN | USB_MOUSE_EP_REPORT) static UsbDeviceDesc usb_hid_device_descriptor = { .bLength = sizeof(usb_hid_device_descriptor), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x100, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .idVendor = USB_HID_VENDOR_ID, .idProduct = USB_HID_PRODUCT_ID, .bcdDevice = 0, .iManufacturer = USB_STRING_MANUFACTURER, .iProduct = USB_STRING_PRODUCT, .iSerialNumber = 0, .bNumConfigurations = 1, }; static const UsbConfigDesc usb_hid_config_descriptor = { .bLength = sizeof(usb_hid_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .bNumInterfaces = USB_HID_INTERFACES, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = USB_CONFIG_ATT_ONE, .bMaxPower = 50, /* 100 mA */ }; static const UsbInterfaceDesc usb_hid_interface_descriptor = { .bLength = sizeof(usb_hid_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = USB_HID_ENDPOINTS, .bInterfaceClass = USB_CLASS_HID, .bInterfaceSubClass = USB_INTERFACE_SUBCLASS_BOOT, .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE, .iInterface = 0, }; static const uint8_t hid_report_descriptor[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Buttons) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x03, // Usage Maximum (3) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x95, 0x03, // Report Count (3) 0x75, 0x01, // Report Size (1) 0x81, 0x02, // Input (Data, Variable, Absolute) 0x95, 0x01, // Report Count (1) 0x75, 0x05, // Report Size (5) 0x81, 0x01, // Input (03=Logitech, 01=Hid spec:Constant) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x38, // Usage (Logitech:_) 0x15, 0x81, // Logical Minimum (-127) 0x25, 0x7F, // Logical Maximum (127) 0x75, 0x08, // Report Size (8) 0x95, 0x02, // Report Count (Logitech=3, Hid spec =2) 0x81, 0x06, // Input (Data, Variable, Relative) 0xC0, // End Collection 0xC0 // End Collection }; static const usb_HidDesc usb_hid_descriptor = { .bLength = sizeof(usb_hid_descriptor), .bDescriptorType = HID_DT_HID, .bcdHID = usb_cpu_to_le16((uint16_t)0x0110), .bCountryCode = 0, .bNumDescriptors = 1, .bDescriptorHidType = HID_DT_REPORT, .wDescriptorLength = usb_cpu_to_le16((uint16_t)sizeof(hid_report_descriptor)), }; static const UsbEndpointDesc usb_hid_ep_descriptor = { .bLength = sizeof(usb_hid_ep_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_HID_REPORT_EP, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = usb_cpu_to_le16((uint16_t)4), .bInterval = 10, /* resolution in ms */ }; static const UsbDescHeader *usb_hid_config[] = { (const UsbDescHeader *)&usb_hid_config_descriptor, (const UsbDescHeader *)&usb_hid_interface_descriptor, (const UsbDescHeader *)&usb_hid_descriptor, (const UsbDescHeader *)&usb_hid_ep_descriptor, NULL, }; static const DEFINE_USB_STRING(language_str, "\x09\x04"); // Language ID: en_US static const DEFINE_USB_STRING(manufacturer_str, USB_STRING("B", "e", "R", "T", "O", "S")); static const DEFINE_USB_STRING(product_str, USB_STRING("U", "S", "B", " ", "M", "o", "u", "s", "e")); static const UsbStringDesc *usb_hid_strings[] = { (const UsbStringDesc *)&language_str, (const UsbStringDesc *)&manufacturer_str, (const UsbStringDesc *)&product_str, NULL, }; typedef struct mouse_report { uint8_t buttons; int8_t x; int8_t y; } PACKED mouse_report_t; static mouse_report_t report; static bool hid_mouse_configured; static void usb_hid_event_cb(UsbCtrlRequest *ctrl) { uint16_t value = usb_le16_to_cpu(ctrl->wValue); uint16_t index = usb_le16_to_cpu(ctrl->wIndex); uint16_t length = usb_le16_to_cpu(ctrl->wLength); uint8_t type = ctrl->mRequestType; uint8_t request = ctrl->bRequest; LOG_INFO("%s: s 0x%02x 0x%02x 0x%04x 0x%04x 0x%04x\n", __func__, type, request, value, index, length); switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: switch (value >> 8) { case HID_DT_HID: LOG_INFO("%s: HID_DT_HID\n", __func__); usb_endpointWrite(USB_DIR_IN | 0, &usb_hid_descriptor, sizeof(usb_hid_descriptor)); break; case HID_DT_REPORT: LOG_INFO("%s: HID_DT_REPORT\n", __func__); usb_endpointWrite(USB_DIR_IN | 0, &hid_report_descriptor, sizeof(hid_report_descriptor)); hid_mouse_configured = true; break; } break; case HID_REQ_GET_REPORT: LOG_INFO("%s: HID_REQ_GET_REPORT\n", __func__); break; case HID_REQ_SET_REPORT: LOG_INFO("%s: HID_REQ_SET_REPORT\n", __func__); break; case HID_REQ_GET_IDLE: LOG_INFO("%s: HID_REQ_GET_IDLE\n", __func__); break; case HID_REQ_SET_IDLE: LOG_INFO("%s: HID_REQ_SET_IDLE\n", __func__); usb_endpointWrite(USB_DIR_IN | 0, NULL, 0); break; case HID_REQ_GET_PROTOCOL: LOG_INFO("%s: HID_REQ_GET_PROTOCOL\n", __func__); break; case HID_REQ_SET_PROTOCOL: LOG_INFO("%s: HID_REQ_SET_PROTOCOL\n", __func__); break; default: LOG_ERR("%s: unknown request: 0x%02x\n", __func__, ctrl->bRequest); break; } } /* Global usb-mouse descriptor that identifies the usb-mouse device */ static UsbDevice usb_mouse = { .device = &usb_hid_device_descriptor, .config = usb_hid_config, .strings = usb_hid_strings, .event_cb = usb_hid_event_cb, }; /* Low-level usb-hid device initialization */ static int usb_mouse_hw_init(void) { if (usb_deviceRegister(&usb_mouse) < 0) return -1; LOG_INFO("usb-hid: registered new USB mouse device\n"); return 0; } /* Send a mouse event */ void usbmouse_sendEvent(int8_t x, int8_t y, int8_t buttons) { report.x = x; report.y = y; report.buttons = buttons; usb_endpointWrite(USB_HID_REPORT_EP, &report, sizeof(report)); } /* * Initialize a USB HID device. * * TODO: support more than one device at the same time. */ int usbmouse_init(UNUSED_ARG(int, unit)) { #if CONFIG_KERN MOD_CHECK(proc); #endif usb_mouse_hw_init(); while (!hid_mouse_configured) cpu_relax(); return 0; }