USB: compile-time endpoints allocation
[bertos.git] / bertos / drv / usbkbd.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \author Andrea Righi <arighi@develer.com>
34  *
35  * \brief Generic USB keyboard device driver.
36  *
37  */
38
39 #include "cfg/cfg_usbkbd.h"
40
41 #define LOG_LEVEL  USB_KEYBOARD_LOG_LEVEL
42 #define LOG_FORMAT USB_KEYBOARD_LOG_FORMAT
43
44 #include <cfg/log.h>
45 #include <cfg/debug.h>
46 #include <cfg/macros.h>
47 #include <cfg/compiler.h>
48 #include <cfg/module.h>
49
50 #include <cpu/power.h> // cpu_relax()
51
52 #include <drv/usb.h>
53 #include <drv/usb_endpoint.h>
54
55 #include "drv/usb_hid.h"
56 #include "drv/usbkbd.h"
57
58 /*
59  * HID device configuration (usb-keyboard)
60  */
61 #define USB_HID_VENDOR_ID       0xffff /* custom */
62 #define USB_HID_PRODUCT_ID      0x0000
63
64 #define USB_HID_INTERFACES      1
65 #define USB_HID_ENDPOINTS       1
66
67 #define USB_STRING_MANUFACTURER 1
68 #define USB_STRING_PRODUCT      2
69
70 #define USB_HID_REPORT_EP       (USB_DIR_IN | USB_KBD_EP_REPORT)
71
72 static UsbDeviceDesc usb_hid_device_descriptor =
73 {
74         .bLength = sizeof(usb_hid_device_descriptor),
75         .bDescriptorType = USB_DT_DEVICE,
76         .bcdUSB = 0x100,
77         .bDeviceClass = 0,
78         .bDeviceSubClass = 0,
79         .bDeviceProtocol = 0,
80         .idVendor = USB_HID_VENDOR_ID,
81         .idProduct = USB_HID_PRODUCT_ID,
82         .bcdDevice = 0,
83         .iManufacturer = USB_STRING_MANUFACTURER,
84         .iProduct = USB_STRING_PRODUCT,
85         .iSerialNumber = 0,
86         .bNumConfigurations = 1,
87 };
88
89 static const UsbConfigDesc usb_hid_config_descriptor =
90 {
91         .bLength = sizeof(usb_hid_config_descriptor),
92         .bDescriptorType = USB_DT_CONFIG,
93         .bNumInterfaces = USB_HID_INTERFACES,
94         .bConfigurationValue = 1,
95         .iConfiguration = 0,
96         .bmAttributes = USB_CONFIG_ATT_ONE,
97         .bMaxPower = 50, /* 100 mA */
98 };
99
100 static const UsbInterfaceDesc usb_hid_interface_descriptor =
101 {
102         .bLength = sizeof(usb_hid_interface_descriptor),
103         .bDescriptorType = USB_DT_INTERFACE,
104         .bInterfaceNumber = 0,
105         .bAlternateSetting = 0,
106         .bNumEndpoints = USB_HID_ENDPOINTS,
107         .bInterfaceClass = USB_CLASS_HID,
108         .bInterfaceSubClass = USB_INTERFACE_SUBCLASS_BOOT,
109         .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_KEYBOARD,
110         .iInterface = 0,
111 };
112
113 /*
114  * Keyboard report descriptor
115  *
116  * Taken from the USB HID spec:
117  *  - E.6 Report Descriptor (Keyboard), HID1_11.pdf, p.69
118  */
119 static const uint8_t hid_report_descriptor[] =
120 {
121         0x05, 0x01, // Usage Page (Generic Desktop)
122         0x09, 0x06, // Usage (Keyboard)
123         0xA1, 0x01, // Collection (Application)
124         0x05, 0x07, // Usage Page (Key Codes)
125         0x19, 0xE0, // Usage Minimum (224)
126         0x29, 0xE7, // Usage Maximum (231)
127         0x15, 0x00, // Logical Minimum (0)
128         0x25, 0x01, // Logical Maximum (1)
129         0x75, 0x01, // Report Size (1)
130         0x95, 0x08, // Report Count (8)
131         0x81, 0x02, // Input (Data, Variable, Absolute)
132         0x95, 0x01, // Report Count (1)
133         0x75, 0x08, // Report Size (8)
134         0x81, 0x01, // Input (Constant)
135         0x95, 0x05, // Report Count (5)
136         0x75, 0x01, // Report Size (1)
137         0x05, 0x08, // Usage Page (Page# for LEDs)
138         0x19, 0x01, // Usage Minimum (1)
139         0x29, 0x05, // Usage Maximum (5)
140         0x91, 0x02, // Output (Data, Variable, Absolute)
141         0x95, 0x01, // Report Count (1)
142         0x75, 0x03, // Report Size (3)
143         0x91, 0x01, // Output (Constant)
144         0x95, 0x06, // Report Count (6)
145         0x75, 0x08, // Report Size (8)
146         0x15, 0x00, // Logical Minimum (0)
147         0x25, 0x65, // Logical Maximum(101)
148         0x05, 0x07, // Usage Page (Key Codes)
149         0x19, 0x00, // Usage Minimum (0)
150         0x29, 0x65, // Usage Maximum (101)
151         0x81, 0x00, // Input (Data, Array)
152         0xC0, // End Collection
153 };
154
155 static const usb_HidDesc usb_hid_descriptor =
156 {
157         .bLength = sizeof(usb_hid_descriptor),
158         .bDescriptorType = HID_DT_HID,
159         .bcdHID = usb_cpu_to_le16((uint16_t)0x0110),
160         .bCountryCode = 0,
161         .bNumDescriptors = 1,
162         .bDescriptorHidType = HID_DT_REPORT,
163         .wDescriptorLength =
164                 usb_cpu_to_le16((uint16_t)sizeof(hid_report_descriptor)),
165 };
166
167 static const UsbEndpointDesc usb_hid_ep_descriptor =
168 {
169         .bLength = sizeof(usb_hid_ep_descriptor),
170         .bDescriptorType = USB_DT_ENDPOINT,
171         .bEndpointAddress = USB_HID_REPORT_EP,
172         .bmAttributes = USB_ENDPOINT_XFER_INT,
173         .wMaxPacketSize = usb_cpu_to_le16((uint16_t)4),
174         .bInterval = 10, /* resolution in ms */
175 };
176
177 static const UsbDescHeader *usb_hid_config[] =
178 {
179         (const UsbDescHeader *)&usb_hid_config_descriptor,
180         (const UsbDescHeader *)&usb_hid_interface_descriptor,
181         (const UsbDescHeader *)&usb_hid_descriptor,
182         (const UsbDescHeader *)&usb_hid_ep_descriptor,
183         NULL,
184 };
185
186 static DEFINE_USB_STRING(language_str, "\x09\x04"); // Language ID: en_US
187 static DEFINE_USB_STRING(manufacturer_str,
188                 USB_STRING("B", "e", "R", "T", "O", "S"));
189 static DEFINE_USB_STRING(product_str,
190                 USB_STRING("U", "S", "B", " ",
191                                 "K", "e", "y", "b", "o", "a", "r", "d"));
192
193 static const UsbStringDesc *usb_hid_strings[] =
194 {
195         (UsbStringDesc *)&language_str,
196         (UsbStringDesc *)&manufacturer_str,
197         (UsbStringDesc *)&product_str,
198         NULL,
199 };
200
201 static uint8_t report[8];
202
203 static bool hid_keyboard_configured;
204
205 static void usb_hid_event_cb(UsbCtrlRequest *ctrl)
206 {
207         uint16_t value = usb_le16_to_cpu(ctrl->wValue);
208         uint16_t index = usb_le16_to_cpu(ctrl->wIndex);
209         uint16_t length = usb_le16_to_cpu(ctrl->wLength);
210         uint8_t type = ctrl->mRequestType;
211         uint8_t request = ctrl->bRequest;
212
213         LOG_INFO("%s: s 0x%02x 0x%02x 0x%04x 0x%04x 0x%04x\n",
214                 __func__, type, request, value, index, length);
215         switch (ctrl->bRequest)
216         {
217         case USB_REQ_GET_DESCRIPTOR:
218                 switch (value >> 8)
219                 {
220                 case HID_DT_HID:
221                         LOG_INFO("%s: HID_DT_HID\n", __func__);
222                         usb_endpointWrite(USB_DIR_IN | 0,
223                                         &usb_hid_descriptor,
224                                         sizeof(usb_hid_descriptor));
225                         break;
226                 case HID_DT_REPORT:
227                         LOG_INFO("%s: HID_DT_REPORT\n", __func__);
228                         usb_endpointWrite(USB_DIR_IN | 0,
229                                         &hid_report_descriptor,
230                                         sizeof(hid_report_descriptor));
231                         hid_keyboard_configured = true;
232                         break;
233                 default:
234                         LOG_INFO("%s: unknown HID request\n", __func__);
235                         break;
236                 }
237                 break;
238         case HID_REQ_GET_REPORT:
239                 LOG_INFO("%s: HID_REQ_GET_REPORT\n", __func__);
240                 break;
241         case HID_REQ_SET_REPORT:
242                 LOG_INFO("%s: HID_REQ_SET_REPORT\n", __func__);
243                 usb_endpointWrite(USB_DIR_IN | 0, NULL, 0);
244                 break;
245         case HID_REQ_GET_IDLE:
246                 LOG_INFO("%s: HID_REQ_GET_IDLE\n", __func__);
247                 break;
248         case HID_REQ_SET_IDLE:
249                 LOG_INFO("%s: HID_REQ_SET_IDLE\n", __func__);
250                 usb_endpointWrite(USB_DIR_IN | 0, NULL, 0);
251                 break;
252         case HID_REQ_GET_PROTOCOL:
253                 LOG_INFO("%s: HID_REQ_GET_PROTOCOL\n", __func__);
254                 break;
255         case HID_REQ_SET_PROTOCOL:
256                 LOG_INFO("%s: HID_REQ_SET_PROTOCOL\n", __func__);
257                 break;
258         default:
259                 LOG_ERR("%s: unknown request: 0x%02x\n",
260                         __func__, ctrl->bRequest);
261                 break;
262         }
263 }
264
265 /* Global usb-keyboard descriptor that identifies the usb-keyboard device */
266 static UsbDevice usb_keyboard = {
267         .device = &usb_hid_device_descriptor,
268         .config = usb_hid_config,
269         .strings = usb_hid_strings,
270         .event_cb = usb_hid_event_cb,
271 };
272
273 /* Low-level usb-hid device initialization */
274 static int usb_keyboard_hw_init(void)
275 {
276         if (usb_deviceRegister(&usb_keyboard) < 0)
277                 return -1;
278         LOG_INFO("usb-hid: registered new USB keyboard device\n");
279         return 0;
280 }
281
282 /* Send a keyboard event */
283 void usbkbd_sendEvent(uint8_t mod, uint8_t code)
284 {
285         report[0] = mod;
286         report[2] = code;
287         usb_endpointWrite(USB_HID_REPORT_EP, &report, sizeof(report));
288 }
289
290 /*
291  * Initialize a USB HID keyboard device.
292  *
293  * TODO: support more than one device at the same time.
294  */
295 int usbkbd_init(UNUSED_ARG(int, unit))
296 {
297 #if CONFIG_KERN
298         MOD_CHECK(proc);
299 #endif
300         usb_keyboard_hw_init();
301         while (!hid_keyboard_configured)
302                 cpu_relax();
303         return 0;
304 }