]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
affae2bf WD |
2 | /* |
3 | * (C) Copyright 2001 | |
4 | * Denis Peter, MPL AG Switzerland | |
5 | * | |
6 | * Part of this source has been derived from the Linux USB | |
7 | * project. | |
affae2bf WD |
8 | */ |
9 | #include <common.h> | |
24b852a7 | 10 | #include <console.h> |
697033cb | 11 | #include <dm.h> |
7b51b576 | 12 | #include <env.h> |
0ea09dfe | 13 | #include <errno.h> |
f7ae49fc | 14 | #include <log.h> |
9a8c72a6 | 15 | #include <malloc.h> |
cf92e05c | 16 | #include <memalign.h> |
52cb4d4f | 17 | #include <stdio_dev.h> |
f8f3e0e5 | 18 | #include <watchdog.h> |
c918261c | 19 | #include <asm/byteorder.h> |
affae2bf | 20 | |
affae2bf WD |
21 | #include <usb.h> |
22 | ||
affae2bf | 23 | /* |
00b7d6ec | 24 | * If overwrite_console returns 1, the stdin, stderr and stdout |
affae2bf WD |
25 | * are switched to the serial port, else the settings in the |
26 | * environment are used | |
27 | */ | |
6d0f6bcf | 28 | #ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE |
00b7d6ec | 29 | extern int overwrite_console(void); |
affae2bf | 30 | #else |
00b7d6ec | 31 | int overwrite_console(void) |
affae2bf | 32 | { |
00b7d6ec | 33 | return 0; |
affae2bf WD |
34 | } |
35 | #endif | |
36 | ||
9a8c72a6 | 37 | /* Keyboard sampling rate */ |
8454c84a | 38 | #define REPEAT_RATE 40 /* 40msec -> 25cps */ |
9a8c72a6 | 39 | #define REPEAT_DELAY 10 /* 10 x REPEAT_RATE = 400msec */ |
affae2bf WD |
40 | |
41 | #define NUM_LOCK 0x53 | |
00b7d6ec MV |
42 | #define CAPS_LOCK 0x39 |
43 | #define SCROLL_LOCK 0x47 | |
affae2bf | 44 | |
affae2bf | 45 | /* Modifier bits */ |
9a8c72a6 MV |
46 | #define LEFT_CNTR (1 << 0) |
47 | #define LEFT_SHIFT (1 << 1) | |
48 | #define LEFT_ALT (1 << 2) | |
49 | #define LEFT_GUI (1 << 3) | |
50 | #define RIGHT_CNTR (1 << 4) | |
51 | #define RIGHT_SHIFT (1 << 5) | |
52 | #define RIGHT_ALT (1 << 6) | |
53 | #define RIGHT_GUI (1 << 7) | |
54 | ||
55 | /* Size of the keyboard buffer */ | |
56 | #define USB_KBD_BUFFER_LEN 0x20 | |
57 | ||
58 | /* Device name */ | |
00b7d6ec | 59 | #define DEVNAME "usbkbd" |
affae2bf | 60 | |
9a8c72a6 MV |
61 | /* Keyboard maps */ |
62 | static const unsigned char usb_kbd_numkey[] = { | |
00b7d6ec MV |
63 | '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', |
64 | '\r', 0x1b, '\b', '\t', ' ', '-', '=', '[', ']', | |
65 | '\\', '#', ';', '\'', '`', ',', '.', '/' | |
affae2bf | 66 | }; |
9a8c72a6 | 67 | static const unsigned char usb_kbd_numkey_shifted[] = { |
00b7d6ec MV |
68 | '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', |
69 | '\r', 0x1b, '\b', '\t', ' ', '_', '+', '{', '}', | |
70 | '|', '~', ':', '"', '~', '<', '>', '?' | |
affae2bf WD |
71 | }; |
72 | ||
d53da847 VP |
73 | static const unsigned char usb_kbd_num_keypad[] = { |
74 | '/', '*', '-', '+', '\r', | |
75 | '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', | |
76 | '.', 0, 0, 0, '=' | |
77 | }; | |
78 | ||
3352c211 | 79 | static const u8 usb_special_keys[] = { |
87e91bcc HS |
80 | #ifdef CONFIG_USB_KEYBOARD_FN_KEYS |
81 | '2', 'H', '5', '3', 'F', '6', 'C', 'D', 'B', 'A' | |
82 | #else | |
3352c211 | 83 | 'C', 'D', 'B', 'A' |
87e91bcc | 84 | #endif |
4151a400 AM |
85 | }; |
86 | ||
9a8c72a6 MV |
87 | /* |
88 | * NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this | |
89 | * order. See usb_kbd_setled() function! | |
90 | */ | |
91 | #define USB_KBD_NUMLOCK (1 << 0) | |
92 | #define USB_KBD_CAPSLOCK (1 << 1) | |
93 | #define USB_KBD_SCROLLLOCK (1 << 2) | |
94 | #define USB_KBD_CTRL (1 << 3) | |
48c8073e | 95 | |
9a8c72a6 MV |
96 | #define USB_KBD_LEDMASK \ |
97 | (USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK) | |
48c8073e | 98 | |
9a8c72a6 | 99 | struct usb_kbd_pdata { |
8f8d7d24 HG |
100 | unsigned long intpipe; |
101 | int intpktsize; | |
102 | int intinterval; | |
8454c84a | 103 | unsigned long last_report; |
8e553119 | 104 | struct int_queue *intq; |
8f8d7d24 | 105 | |
9a8c72a6 | 106 | uint32_t repeat_delay; |
affae2bf | 107 | |
9a8c72a6 MV |
108 | uint32_t usb_in_pointer; |
109 | uint32_t usb_out_pointer; | |
110 | uint8_t usb_kbd_buffer[USB_KBD_BUFFER_LEN]; | |
48c8073e | 111 | |
d7475386 | 112 | uint8_t *new; |
08a98b89 | 113 | uint8_t old[USB_KBD_BOOT_REPORT_SIZE]; |
48c8073e | 114 | |
9a8c72a6 MV |
115 | uint8_t flags; |
116 | }; | |
48c8073e | 117 | |
07551f23 JL |
118 | extern int __maybe_unused net_busy_flag; |
119 | ||
120 | /* The period of time between two calls of usb_kbd_testc(). */ | |
121 | static unsigned long __maybe_unused kbd_testc_tms; | |
122 | ||
9a8c72a6 | 123 | /* Puts character in the queue and sets up the in and out pointer. */ |
28dfa7d8 | 124 | static void usb_kbd_put_queue(struct usb_kbd_pdata *data, u8 c) |
affae2bf | 125 | { |
9a8c72a6 MV |
126 | if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) { |
127 | /* Check for buffer full. */ | |
128 | if (data->usb_out_pointer == 0) | |
129 | return; | |
affae2bf | 130 | |
9a8c72a6 MV |
131 | data->usb_in_pointer = 0; |
132 | } else { | |
133 | /* Check for buffer full. */ | |
134 | if (data->usb_in_pointer == data->usb_out_pointer - 1) | |
135 | return; | |
affae2bf | 136 | |
9a8c72a6 MV |
137 | data->usb_in_pointer++; |
138 | } | |
affae2bf | 139 | |
9a8c72a6 | 140 | data->usb_kbd_buffer[data->usb_in_pointer] = c; |
affae2bf WD |
141 | } |
142 | ||
9a8c72a6 MV |
143 | /* |
144 | * Set the LEDs. Since this is used in the irq routine, the control job is | |
145 | * issued with a timeout of 0. This means, that the job is queued without | |
146 | * waiting for job completion. | |
affae2bf | 147 | */ |
affae2bf WD |
148 | static void usb_kbd_setled(struct usb_device *dev) |
149 | { | |
9a8c72a6 MV |
150 | struct usb_interface *iface = &dev->config.if_desc[0]; |
151 | struct usb_kbd_pdata *data = dev->privptr; | |
9b239381 | 152 | ALLOC_ALIGN_BUFFER(uint32_t, leds, 1, USB_DMA_MINALIGN); |
9a8c72a6 | 153 | |
9b239381 | 154 | *leds = data->flags & USB_KBD_LEDMASK; |
affae2bf WD |
155 | usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
156 | USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, | |
9b239381 | 157 | 0x200, iface->desc.bInterfaceNumber, leds, 1, 0); |
affae2bf WD |
158 | } |
159 | ||
9a8c72a6 | 160 | #define CAPITAL_MASK 0x20 |
affae2bf | 161 | /* Translate the scancode in ASCII */ |
9a8c72a6 MV |
162 | static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode, |
163 | unsigned char modifier, int pressed) | |
affae2bf | 164 | { |
9a8c72a6 | 165 | uint8_t keycode = 0; |
affae2bf | 166 | |
9a8c72a6 | 167 | /* Key released */ |
00b7d6ec | 168 | if (pressed == 0) { |
9a8c72a6 | 169 | data->repeat_delay = 0; |
affae2bf WD |
170 | return 0; |
171 | } | |
9a8c72a6 | 172 | |
00b7d6ec | 173 | if (pressed == 2) { |
9a8c72a6 MV |
174 | data->repeat_delay++; |
175 | if (data->repeat_delay < REPEAT_DELAY) | |
affae2bf | 176 | return 0; |
9a8c72a6 MV |
177 | |
178 | data->repeat_delay = REPEAT_DELAY; | |
affae2bf | 179 | } |
9a8c72a6 MV |
180 | |
181 | /* Alphanumeric values */ | |
182 | if ((scancode > 3) && (scancode <= 0x1d)) { | |
183 | keycode = scancode - 4 + 'a'; | |
184 | ||
185 | if (data->flags & USB_KBD_CAPSLOCK) | |
00b7d6ec | 186 | keycode &= ~CAPITAL_MASK; |
9a8c72a6 MV |
187 | |
188 | if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) { | |
189 | /* Handle CAPSLock + Shift pressed simultaneously */ | |
00b7d6ec | 190 | if (keycode & CAPITAL_MASK) |
00b7d6ec | 191 | keycode &= ~CAPITAL_MASK; |
affae2bf | 192 | else |
00b7d6ec | 193 | keycode |= CAPITAL_MASK; |
affae2bf WD |
194 | } |
195 | } | |
9a8c72a6 | 196 | |
bdbcbe75 | 197 | if ((scancode > 0x1d) && (scancode < 0x39)) { |
9a8c72a6 MV |
198 | /* Shift pressed */ |
199 | if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) | |
200 | keycode = usb_kbd_numkey_shifted[scancode - 0x1e]; | |
201 | else | |
202 | keycode = usb_kbd_numkey[scancode - 0x1e]; | |
affae2bf | 203 | } |
4785a694 | 204 | |
d53da847 VP |
205 | /* Numeric keypad */ |
206 | if ((scancode >= 0x54) && (scancode <= 0x67)) | |
207 | keycode = usb_kbd_num_keypad[scancode - 0x54]; | |
208 | ||
9a8c72a6 | 209 | if (data->flags & USB_KBD_CTRL) |
4785a694 ZW |
210 | keycode = scancode - 0x3; |
211 | ||
00b7d6ec MV |
212 | if (pressed == 1) { |
213 | if (scancode == NUM_LOCK) { | |
9a8c72a6 | 214 | data->flags ^= USB_KBD_NUMLOCK; |
affae2bf WD |
215 | return 1; |
216 | } | |
9a8c72a6 | 217 | |
00b7d6ec | 218 | if (scancode == CAPS_LOCK) { |
9a8c72a6 | 219 | data->flags ^= USB_KBD_CAPSLOCK; |
affae2bf WD |
220 | return 1; |
221 | } | |
00b7d6ec | 222 | if (scancode == SCROLL_LOCK) { |
9a8c72a6 | 223 | data->flags ^= USB_KBD_SCROLLLOCK; |
affae2bf WD |
224 | return 1; |
225 | } | |
226 | } | |
9a8c72a6 MV |
227 | |
228 | /* Report keycode if any */ | |
3352c211 | 229 | if (keycode) { |
ceb4972a | 230 | debug("%c", keycode); |
9a8c72a6 | 231 | usb_kbd_put_queue(data, keycode); |
3352c211 | 232 | return 0; |
affae2bf | 233 | } |
9a8c72a6 | 234 | |
87e91bcc HS |
235 | #ifdef CONFIG_USB_KEYBOARD_FN_KEYS |
236 | if (scancode < 0x3a || scancode > 0x52 || | |
237 | scancode == 0x46 || scancode == 0x47) | |
238 | return 1; | |
239 | ||
240 | usb_kbd_put_queue(data, 0x1b); | |
241 | if (scancode < 0x3e) { | |
242 | /* F1 - F4 */ | |
243 | usb_kbd_put_queue(data, 0x4f); | |
244 | usb_kbd_put_queue(data, scancode - 0x3a + 'P'); | |
245 | return 0; | |
246 | } | |
247 | usb_kbd_put_queue(data, '['); | |
248 | if (scancode < 0x42) { | |
249 | /* F5 - F8 */ | |
250 | usb_kbd_put_queue(data, '1'); | |
251 | if (scancode == 0x3e) | |
252 | --scancode; | |
253 | keycode = scancode - 0x3f + '7'; | |
254 | } else if (scancode < 0x49) { | |
255 | /* F9 - F12 */ | |
256 | usb_kbd_put_queue(data, '2'); | |
257 | if (scancode > 0x43) | |
258 | ++scancode; | |
259 | keycode = scancode - 0x42 + '0'; | |
260 | } else { | |
261 | /* | |
262 | * INSERT, HOME, PAGE UP, DELETE, END, PAGE DOWN, | |
263 | * RIGHT, LEFT, DOWN, UP | |
264 | */ | |
265 | keycode = usb_special_keys[scancode - 0x49]; | |
266 | } | |
267 | usb_kbd_put_queue(data, keycode); | |
268 | if (scancode < 0x4f && scancode != 0x4a && scancode != 0x4d) | |
269 | usb_kbd_put_queue(data, '~'); | |
270 | return 0; | |
271 | #else | |
3352c211 HS |
272 | /* Left, Right, Up, Down */ |
273 | if (scancode > 0x4e && scancode < 0x53) { | |
274 | usb_kbd_put_queue(data, 0x1b); | |
275 | usb_kbd_put_queue(data, '['); | |
276 | usb_kbd_put_queue(data, usb_special_keys[scancode - 0x4f]); | |
277 | return 0; | |
278 | } | |
279 | return 1; | |
87e91bcc | 280 | #endif /* CONFIG_USB_KEYBOARD_FN_KEYS */ |
affae2bf WD |
281 | } |
282 | ||
9a8c72a6 MV |
283 | static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up) |
284 | { | |
285 | uint32_t res = 0; | |
286 | struct usb_kbd_pdata *data = dev->privptr; | |
287 | uint8_t *new; | |
288 | uint8_t *old; | |
289 | ||
290 | if (up) { | |
291 | new = data->old; | |
292 | old = data->new; | |
293 | } else { | |
294 | new = data->new; | |
295 | old = data->old; | |
296 | } | |
297 | ||
08a98b89 AC |
298 | if ((old[i] > 3) && |
299 | (memscan(new + 2, old[i], USB_KBD_BOOT_REPORT_SIZE - 2) == | |
300 | new + USB_KBD_BOOT_REPORT_SIZE)) { | |
9a8c72a6 | 301 | res |= usb_kbd_translate(data, old[i], data->new[0], up); |
08a98b89 | 302 | } |
9a8c72a6 MV |
303 | |
304 | return res; | |
305 | } | |
306 | ||
affae2bf | 307 | /* Interrupt service routine */ |
48c8073e | 308 | static int usb_kbd_irq_worker(struct usb_device *dev) |
affae2bf | 309 | { |
9a8c72a6 MV |
310 | struct usb_kbd_pdata *data = dev->privptr; |
311 | int i, res = 0; | |
4785a694 | 312 | |
9a8c72a6 MV |
313 | /* No combo key pressed */ |
314 | if (data->new[0] == 0x00) | |
315 | data->flags &= ~USB_KBD_CTRL; | |
316 | /* Left or Right Ctrl pressed */ | |
317 | else if ((data->new[0] == LEFT_CNTR) || (data->new[0] == RIGHT_CNTR)) | |
318 | data->flags |= USB_KBD_CTRL; | |
00b7d6ec | 319 | |
08a98b89 | 320 | for (i = 2; i < USB_KBD_BOOT_REPORT_SIZE; i++) { |
9a8c72a6 MV |
321 | res |= usb_kbd_service_key(dev, i, 0); |
322 | res |= usb_kbd_service_key(dev, i, 1); | |
affae2bf | 323 | } |
00b7d6ec | 324 | |
9a8c72a6 MV |
325 | /* Key is still pressed */ |
326 | if ((data->new[2] > 3) && (data->old[2] == data->new[2])) | |
327 | res |= usb_kbd_translate(data, data->new[2], data->new[0], 2); | |
328 | ||
00b7d6ec | 329 | if (res == 1) |
affae2bf | 330 | usb_kbd_setled(dev); |
00b7d6ec | 331 | |
08a98b89 | 332 | memcpy(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE); |
00b7d6ec | 333 | |
9a8c72a6 | 334 | return 1; |
affae2bf WD |
335 | } |
336 | ||
9a8c72a6 | 337 | /* Keyboard interrupt handler */ |
48c8073e MV |
338 | static int usb_kbd_irq(struct usb_device *dev) |
339 | { | |
08a98b89 AC |
340 | if ((dev->irq_status != 0) || |
341 | (dev->irq_act_len != USB_KBD_BOOT_REPORT_SIZE)) { | |
ceb4972a VG |
342 | debug("USB KBD: Error %lX, len %d\n", |
343 | dev->irq_status, dev->irq_act_len); | |
48c8073e MV |
344 | return 1; |
345 | } | |
346 | ||
347 | return usb_kbd_irq_worker(dev); | |
348 | } | |
349 | ||
9a8c72a6 MV |
350 | /* Interrupt polling */ |
351 | static inline void usb_kbd_poll_for_event(struct usb_device *dev) | |
352 | { | |
8454c84a | 353 | #if defined(CONFIG_SYS_USB_EVENT_POLL) |
8f8d7d24 | 354 | struct usb_kbd_pdata *data = dev->privptr; |
f9636e8d | 355 | |
216db3af | 356 | /* Submit an interrupt transfer request */ |
fdd135bf | 357 | if (usb_int_msg(dev, data->intpipe, &data->new[0], |
3437121c | 358 | data->intpktsize, data->intinterval, true) >= 0) |
3e816a24 | 359 | usb_kbd_irq_worker(dev); |
8454c84a HG |
360 | #elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) || \ |
361 | defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE) | |
362 | #if defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) | |
9a8c72a6 MV |
363 | struct usb_interface *iface; |
364 | struct usb_kbd_pdata *data = dev->privptr; | |
365 | iface = &dev->config.if_desc[0]; | |
366 | usb_get_report(dev, iface->desc.bInterfaceNumber, | |
08a98b89 | 367 | 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE); |
8454c84a | 368 | if (memcmp(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE)) { |
9a8c72a6 | 369 | usb_kbd_irq_worker(dev); |
8454c84a | 370 | #else |
8e553119 HG |
371 | struct usb_kbd_pdata *data = dev->privptr; |
372 | if (poll_int_queue(dev, data->intq)) { | |
373 | usb_kbd_irq_worker(dev); | |
374 | /* We've consumed all queued int packets, create new */ | |
375 | destroy_int_queue(dev, data->intq); | |
376 | data->intq = create_int_queue(dev, data->intpipe, 1, | |
8bb6c1d1 HG |
377 | USB_KBD_BOOT_REPORT_SIZE, data->new, |
378 | data->intinterval); | |
8454c84a HG |
379 | #endif |
380 | data->last_report = get_timer(0); | |
381 | /* Repeat last usb hid report every REPEAT_RATE ms for keyrepeat */ | |
382 | } else if (data->last_report != -1 && | |
383 | get_timer(data->last_report) > REPEAT_RATE) { | |
384 | usb_kbd_irq_worker(dev); | |
385 | data->last_report = get_timer(0); | |
8e553119 | 386 | } |
9a8c72a6 MV |
387 | #endif |
388 | } | |
389 | ||
390 | /* test if a character is in the queue */ | |
709ea543 | 391 | static int usb_kbd_testc(struct stdio_dev *sdev) |
9a8c72a6 MV |
392 | { |
393 | struct stdio_dev *dev; | |
394 | struct usb_device *usb_kbd_dev; | |
395 | struct usb_kbd_pdata *data; | |
396 | ||
c95e2b9e JL |
397 | #ifdef CONFIG_CMD_NET |
398 | /* | |
399 | * If net_busy_flag is 1, NET transfer is running, | |
400 | * then we check key-pressed every second (first check may be | |
401 | * less than 1 second) to improve TFTP booting performance. | |
402 | */ | |
403 | if (net_busy_flag && (get_timer(kbd_testc_tms) < CONFIG_SYS_HZ)) | |
404 | return 0; | |
405 | kbd_testc_tms = get_timer(0); | |
406 | #endif | |
4fb67f47 | 407 | dev = stdio_get_by_name(sdev->name); |
9a8c72a6 MV |
408 | usb_kbd_dev = (struct usb_device *)dev->priv; |
409 | data = usb_kbd_dev->privptr; | |
410 | ||
411 | usb_kbd_poll_for_event(usb_kbd_dev); | |
412 | ||
413 | return !(data->usb_in_pointer == data->usb_out_pointer); | |
414 | } | |
415 | ||
416 | /* gets the character from the queue */ | |
709ea543 | 417 | static int usb_kbd_getc(struct stdio_dev *sdev) |
9a8c72a6 MV |
418 | { |
419 | struct stdio_dev *dev; | |
420 | struct usb_device *usb_kbd_dev; | |
421 | struct usb_kbd_pdata *data; | |
422 | ||
4fb67f47 | 423 | dev = stdio_get_by_name(sdev->name); |
9a8c72a6 MV |
424 | usb_kbd_dev = (struct usb_device *)dev->priv; |
425 | data = usb_kbd_dev->privptr; | |
426 | ||
f8f3e0e5 MS |
427 | while (data->usb_in_pointer == data->usb_out_pointer) { |
428 | WATCHDOG_RESET(); | |
9a8c72a6 | 429 | usb_kbd_poll_for_event(usb_kbd_dev); |
f8f3e0e5 | 430 | } |
9a8c72a6 MV |
431 | |
432 | if (data->usb_out_pointer == USB_KBD_BUFFER_LEN - 1) | |
433 | data->usb_out_pointer = 0; | |
434 | else | |
435 | data->usb_out_pointer++; | |
436 | ||
437 | return data->usb_kbd_buffer[data->usb_out_pointer]; | |
438 | } | |
439 | ||
440 | /* probes the USB device dev for keyboard type. */ | |
34ab37ee | 441 | static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) |
affae2bf | 442 | { |
8f8bd565 | 443 | struct usb_interface *iface; |
affae2bf | 444 | struct usb_endpoint_descriptor *ep; |
9a8c72a6 | 445 | struct usb_kbd_pdata *data; |
affae2bf | 446 | |
00b7d6ec MV |
447 | if (dev->descriptor.bNumConfigurations != 1) |
448 | return 0; | |
9a8c72a6 | 449 | |
affae2bf WD |
450 | iface = &dev->config.if_desc[ifnum]; |
451 | ||
2cdb58eb | 452 | if (iface->desc.bInterfaceClass != USB_CLASS_HID) |
8f8bd565 | 453 | return 0; |
9a8c72a6 | 454 | |
2cdb58eb | 455 | if (iface->desc.bInterfaceSubClass != USB_SUB_HID_BOOT) |
8f8bd565 | 456 | return 0; |
9a8c72a6 | 457 | |
2cdb58eb | 458 | if (iface->desc.bInterfaceProtocol != USB_PROT_HID_KEYBOARD) |
8f8bd565 | 459 | return 0; |
9a8c72a6 | 460 | |
8f8bd565 TR |
461 | if (iface->desc.bNumEndpoints != 1) |
462 | return 0; | |
affae2bf WD |
463 | |
464 | ep = &iface->ep_desc[0]; | |
465 | ||
9a8c72a6 | 466 | /* Check if endpoint 1 is interrupt endpoint */ |
00b7d6ec MV |
467 | if (!(ep->bEndpointAddress & 0x80)) |
468 | return 0; | |
9a8c72a6 | 469 | |
00b7d6ec MV |
470 | if ((ep->bmAttributes & 3) != 3) |
471 | return 0; | |
9a8c72a6 | 472 | |
ceb4972a | 473 | debug("USB KBD: found set protocol...\n"); |
9a8c72a6 MV |
474 | |
475 | data = malloc(sizeof(struct usb_kbd_pdata)); | |
476 | if (!data) { | |
477 | printf("USB KBD: Error allocating private data\n"); | |
478 | return 0; | |
479 | } | |
480 | ||
481 | /* Clear private data */ | |
482 | memset(data, 0, sizeof(struct usb_kbd_pdata)); | |
483 | ||
d7475386 | 484 | /* allocate input buffer aligned and sized to USB DMA alignment */ |
08a98b89 AC |
485 | data->new = memalign(USB_DMA_MINALIGN, |
486 | roundup(USB_KBD_BOOT_REPORT_SIZE, USB_DMA_MINALIGN)); | |
d7475386 | 487 | |
9a8c72a6 MV |
488 | /* Insert private data into USB device structure */ |
489 | dev->privptr = data; | |
490 | ||
491 | /* Set IRQ handler */ | |
492 | dev->irq_handle = usb_kbd_irq; | |
493 | ||
8f8d7d24 HG |
494 | data->intpipe = usb_rcvintpipe(dev, ep->bEndpointAddress); |
495 | data->intpktsize = min(usb_maxpacket(dev, data->intpipe), | |
496 | USB_KBD_BOOT_REPORT_SIZE); | |
497 | data->intinterval = ep->bInterval; | |
8454c84a | 498 | data->last_report = -1; |
9a8c72a6 MV |
499 | |
500 | /* We found a USB Keyboard, install it. */ | |
501 | usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); | |
502 | ||
de451493 | 503 | debug("USB KBD: found set idle...\n"); |
8454c84a HG |
504 | #if !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) && \ |
505 | !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE) | |
8454c84a | 506 | usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE / 4, 0); |
de451493 HG |
507 | #else |
508 | usb_set_idle(dev, iface->desc.bInterfaceNumber, 0, 0); | |
8454c84a | 509 | #endif |
9a8c72a6 | 510 | |
ceb4972a | 511 | debug("USB KBD: enable interrupt pipe...\n"); |
8e553119 HG |
512 | #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE |
513 | data->intq = create_int_queue(dev, data->intpipe, 1, | |
8bb6c1d1 HG |
514 | USB_KBD_BOOT_REPORT_SIZE, data->new, |
515 | data->intinterval); | |
8e553119 | 516 | if (!data->intq) { |
e4b70d80 SW |
517 | #elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) |
518 | if (usb_get_report(dev, iface->desc.bInterfaceNumber, | |
519 | 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE) < 0) { | |
8e553119 | 520 | #else |
fdd135bf | 521 | if (usb_int_msg(dev, data->intpipe, data->new, data->intpktsize, |
3437121c | 522 | data->intinterval, false) < 0) { |
8e553119 | 523 | #endif |
5da2dc97 VP |
524 | printf("Failed to get keyboard state from device %04x:%04x\n", |
525 | dev->descriptor.idVendor, dev->descriptor.idProduct); | |
526 | /* Abort, we don't want to use that non-functional keyboard. */ | |
527 | return 0; | |
528 | } | |
9a8c72a6 MV |
529 | |
530 | /* Success. */ | |
affae2bf WD |
531 | return 1; |
532 | } | |
533 | ||
603afaf0 SG |
534 | static int probe_usb_keyboard(struct usb_device *dev) |
535 | { | |
536 | char *stdinname; | |
537 | struct stdio_dev usb_kbd_dev; | |
538 | int error; | |
539 | ||
540 | /* Try probing the keyboard */ | |
34ab37ee | 541 | if (usb_kbd_probe_dev(dev, 0) != 1) |
603afaf0 SG |
542 | return -ENOENT; |
543 | ||
544 | /* Register the keyboard */ | |
545 | debug("USB KBD: register.\n"); | |
546 | memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); | |
547 | strcpy(usb_kbd_dev.name, DEVNAME); | |
1caf934a | 548 | usb_kbd_dev.flags = DEV_FLAGS_INPUT; |
603afaf0 SG |
549 | usb_kbd_dev.getc = usb_kbd_getc; |
550 | usb_kbd_dev.tstc = usb_kbd_testc; | |
551 | usb_kbd_dev.priv = (void *)dev; | |
552 | error = stdio_register(&usb_kbd_dev); | |
553 | if (error) | |
554 | return error; | |
555 | ||
00caae6d | 556 | stdinname = env_get("stdin"); |
b0265429 | 557 | #if CONFIG_IS_ENABLED(CONSOLE_MUX) |
603afaf0 SG |
558 | error = iomux_doenv(stdin, stdinname); |
559 | if (error) | |
560 | return error; | |
561 | #else | |
562 | /* Check if this is the standard input device. */ | |
563 | if (strcmp(stdinname, DEVNAME)) | |
564 | return 1; | |
565 | ||
566 | /* Reassign the console */ | |
567 | if (overwrite_console()) | |
568 | return 1; | |
569 | ||
570 | error = console_assign(stdin, DEVNAME); | |
571 | if (error) | |
572 | return error; | |
573 | #endif | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
fd09c205 | 578 | #if !CONFIG_IS_ENABLED(DM_USB) |
9a8c72a6 MV |
579 | /* Search for keyboard and register it if found. */ |
580 | int drv_usb_kbd_init(void) | |
581 | { | |
9a8c72a6 MV |
582 | int error, i; |
583 | ||
697033cb | 584 | debug("%s: Probing for keyboard\n", __func__); |
697033cb | 585 | /* Scan all USB Devices */ |
9a8c72a6 | 586 | for (i = 0; i < USB_MAX_DEVICE; i++) { |
603afaf0 SG |
587 | struct usb_device *dev; |
588 | ||
9a8c72a6 MV |
589 | /* Get USB device. */ |
590 | dev = usb_get_dev_index(i); | |
591 | if (!dev) | |
603afaf0 | 592 | break; |
9a8c72a6 MV |
593 | |
594 | if (dev->devnum == -1) | |
595 | continue; | |
596 | ||
603afaf0 SG |
597 | error = probe_usb_keyboard(dev); |
598 | if (!error) | |
9a8c72a6 | 599 | return 1; |
603afaf0 | 600 | if (error && error != -ENOENT) |
9a8c72a6 | 601 | return error; |
9a8c72a6 MV |
602 | } |
603 | ||
604 | /* No USB Keyboard found */ | |
605 | return -1; | |
606 | } | |
607 | ||
608 | /* Deregister the keyboard. */ | |
8a8a2257 | 609 | int usb_kbd_deregister(int force) |
9a8c72a6 | 610 | { |
869588de | 611 | #if CONFIG_IS_ENABLED(SYS_STDIO_DEREGISTER) |
dfe5b1c8 HG |
612 | struct stdio_dev *dev; |
613 | struct usb_device *usb_kbd_dev; | |
614 | struct usb_kbd_pdata *data; | |
615 | ||
616 | dev = stdio_get_by_name(DEVNAME); | |
617 | if (dev) { | |
618 | usb_kbd_dev = (struct usb_device *)dev->priv; | |
619 | data = usb_kbd_dev->privptr; | |
620 | if (stdio_deregister_dev(dev, force) != 0) | |
621 | return 1; | |
b0265429 | 622 | #if CONFIG_IS_ENABLED(CONSOLE_MUX) |
00caae6d | 623 | if (iomux_doenv(stdin, env_get("stdin")) != 0) |
3cbcb289 HG |
624 | return 1; |
625 | #endif | |
8e553119 HG |
626 | #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE |
627 | destroy_int_queue(usb_kbd_dev, data->intq); | |
628 | #endif | |
dfe5b1c8 HG |
629 | free(data->new); |
630 | free(data); | |
631 | } | |
0ea09dfe HG |
632 | |
633 | return 0; | |
9a8c72a6 MV |
634 | #else |
635 | return 1; | |
636 | #endif | |
637 | } | |
34ab37ee | 638 | |
9a80e714 HG |
639 | #endif |
640 | ||
fd09c205 | 641 | #if CONFIG_IS_ENABLED(DM_USB) |
34ab37ee SG |
642 | |
643 | static int usb_kbd_probe(struct udevice *dev) | |
644 | { | |
645 | struct usb_device *udev = dev_get_parent_priv(dev); | |
34ab37ee | 646 | |
8319aeb1 | 647 | return probe_usb_keyboard(udev); |
34ab37ee SG |
648 | } |
649 | ||
8a834870 SG |
650 | static int usb_kbd_remove(struct udevice *dev) |
651 | { | |
652 | struct usb_device *udev = dev_get_parent_priv(dev); | |
653 | struct usb_kbd_pdata *data; | |
654 | struct stdio_dev *sdev; | |
655 | int ret; | |
656 | ||
657 | sdev = stdio_get_by_name(DEVNAME); | |
658 | if (!sdev) { | |
659 | ret = -ENXIO; | |
660 | goto err; | |
661 | } | |
662 | data = udev->privptr; | |
663 | if (stdio_deregister_dev(sdev, true)) { | |
664 | ret = -EPERM; | |
665 | goto err; | |
666 | } | |
b0265429 | 667 | #if CONFIG_IS_ENABLED(CONSOLE_MUX) |
00caae6d | 668 | if (iomux_doenv(stdin, env_get("stdin"))) { |
8a834870 SG |
669 | ret = -ENOLINK; |
670 | goto err; | |
671 | } | |
672 | #endif | |
673 | #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE | |
674 | destroy_int_queue(udev, data->intq); | |
675 | #endif | |
676 | free(data->new); | |
677 | free(data); | |
678 | ||
679 | return 0; | |
680 | err: | |
681 | printf("%s: warning, ret=%d", __func__, ret); | |
682 | return ret; | |
683 | } | |
684 | ||
34ab37ee SG |
685 | static const struct udevice_id usb_kbd_ids[] = { |
686 | { .compatible = "usb-keyboard" }, | |
687 | { } | |
688 | }; | |
689 | ||
690 | U_BOOT_DRIVER(usb_kbd) = { | |
691 | .name = "usb_kbd", | |
692 | .id = UCLASS_KEYBOARD, | |
693 | .of_match = usb_kbd_ids, | |
694 | .probe = usb_kbd_probe, | |
8a834870 | 695 | .remove = usb_kbd_remove, |
34ab37ee SG |
696 | }; |
697 | ||
34ab37ee SG |
698 | static const struct usb_device_id kbd_id_table[] = { |
699 | { | |
700 | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | | |
701 | USB_DEVICE_ID_MATCH_INT_SUBCLASS | | |
702 | USB_DEVICE_ID_MATCH_INT_PROTOCOL, | |
703 | .bInterfaceClass = USB_CLASS_HID, | |
2cdb58eb SG |
704 | .bInterfaceSubClass = USB_SUB_HID_BOOT, |
705 | .bInterfaceProtocol = USB_PROT_HID_KEYBOARD, | |
34ab37ee SG |
706 | }, |
707 | { } /* Terminating entry */ | |
708 | }; | |
709 | ||
710 | U_BOOT_USB_DEVICE(usb_kbd, kbd_id_table); | |
711 | ||
712 | #endif |