usb_keyboard.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /* Support for the HID Boot Protocol. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2008, 2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB 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 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/term.h>
  20. #include <grub/machine/console.h>
  21. #include <grub/time.h>
  22. #include <grub/cpu/io.h>
  23. #include <grub/misc.h>
  24. #include <grub/term.h>
  25. #include <grub/usb.h>
  26. #include <grub/dl.h>
  27. #include <grub/time.h>
  28. static char keyboard_map[128] =
  29. {
  30. '\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
  31. 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
  32. 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
  33. 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
  34. '3', '4', '5', '6', '7', '8', '9', '0',
  35. '\n', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[',
  36. ']', '\\', '#', ';', '\'', '`', ',', '.',
  37. '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
  38. '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
  39. '\0', '\0', GRUB_TERM_HOME, GRUB_TERM_PPAGE, GRUB_TERM_DC, GRUB_TERM_END, GRUB_TERM_NPAGE, GRUB_TERM_RIGHT,
  40. GRUB_TERM_LEFT, GRUB_TERM_DOWN, GRUB_TERM_UP
  41. };
  42. static char keyboard_map_shift[128] =
  43. {
  44. '\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
  45. 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
  46. 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
  47. 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
  48. '#', '$', '%', '^', '&', '*', '(', ')',
  49. '\n', '\0', '\0', '\0', ' ', '_', '+', '{',
  50. '}', '|', '#', ':', '"', '`', '<', '>',
  51. '?'
  52. };
  53. static grub_usb_device_t usbdev;
  54. /* Valid values for bmRequestType. See HID definition version 1.11 section
  55. 7.2. */
  56. #define USB_HID_HOST_TO_DEVICE 0x21
  57. #define USB_HID_DEVICE_TO_HOST 0xA1
  58. /* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
  59. #define USB_HID_GET_REPORT 0x01
  60. #define USB_HID_GET_IDLE 0x02
  61. #define USB_HID_GET_PROTOCOL 0x03
  62. #define USB_HID_SET_REPORT 0x09
  63. #define USB_HID_SET_IDLE 0x0A
  64. #define USB_HID_SET_PROTOCOL 0x0B
  65. static int
  66. usb_iterate (grub_usb_device_t dev, void *closure __attribute__ ((unused)))
  67. {
  68. struct grub_usb_desc_device *descdev;
  69. descdev = &dev->descdev;
  70. grub_dprintf ("usb_keyboard", "%x %x %x\n",
  71. descdev->class, descdev->subclass, descdev->protocol);
  72. #if 0
  73. if (descdev->class != 0x09
  74. || descdev->subclass == 0x01
  75. || descdev->protocol != 0x02)
  76. return 0;
  77. #endif
  78. if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
  79. return 0;
  80. grub_printf ("HID found!\n");
  81. usbdev = dev;
  82. return 1;
  83. }
  84. static void
  85. grub_usb_hid (void)
  86. {
  87. grub_usb_iterate (usb_iterate, 0);
  88. /* Place the device in boot mode. */
  89. grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
  90. 0, 0, 0, 0);
  91. /* Reports every time an event occurs and not more often than that. */
  92. grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
  93. 0<<8, 0, 0, 0);
  94. }
  95. static grub_err_t
  96. grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
  97. {
  98. return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
  99. 0, 0, 8, (char *) report);
  100. }
  101. static int
  102. grub_usb_keyboard_checkkey (void)
  103. {
  104. grub_uint8_t data[8];
  105. int key;
  106. grub_err_t err;
  107. grub_uint64_t currtime;
  108. int timeout = 50;
  109. data[2] = 0;
  110. currtime = grub_get_time_ms ();
  111. do
  112. {
  113. /* Get_Report. */
  114. err = grub_usb_keyboard_getreport (usbdev, data);
  115. /* Implement a timeout. */
  116. if (grub_get_time_ms () > currtime + timeout)
  117. break;
  118. }
  119. while (err || !data[2]);
  120. if (err || !data[2])
  121. return -1;
  122. grub_dprintf ("usb_keyboard",
  123. "report: 0x%02x 0x%02x 0x%02x 0x%02x"
  124. " 0x%02x 0x%02x 0x%02x 0x%02x\n",
  125. data[0], data[1], data[2], data[3],
  126. data[4], data[5], data[6], data[7]);
  127. /* Check if the Control or Shift key was pressed. */
  128. if (data[0] & 0x01 || data[0] & 0x10)
  129. key = keyboard_map[data[2]] - 'a' + 1;
  130. else if (data[0] & 0x02 || data[0] & 0x20)
  131. key = keyboard_map_shift[data[2]];
  132. else
  133. key = keyboard_map[data[2]];
  134. if (key == 0)
  135. grub_printf ("Unknown key 0x%x detected\n", data[2]);
  136. #if 0
  137. /* Wait until the key is released. */
  138. while (!err && data[2])
  139. {
  140. err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST,
  141. USB_HID_GET_REPORT, 0, 0,
  142. sizeof (data), (char *) data);
  143. grub_dprintf ("usb_keyboard",
  144. "report2: 0x%02x 0x%02x 0x%02x 0x%02x"
  145. " 0x%02x 0x%02x 0x%02x 0x%02x\n",
  146. data[0], data[1], data[2], data[3],
  147. data[4], data[5], data[6], data[7]);
  148. }
  149. #endif
  150. grub_errno = GRUB_ERR_NONE;
  151. return key;
  152. }
  153. typedef enum
  154. {
  155. GRUB_HIDBOOT_REPEAT_NONE,
  156. GRUB_HIDBOOT_REPEAT_FIRST,
  157. GRUB_HIDBOOT_REPEAT
  158. } grub_usb_keyboard_repeat_t;
  159. static int
  160. grub_usb_keyboard_getkey (void)
  161. {
  162. int key;
  163. grub_err_t err;
  164. grub_uint8_t data[8];
  165. grub_uint64_t currtime;
  166. int timeout;
  167. static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
  168. again:
  169. do
  170. {
  171. key = grub_usb_keyboard_checkkey ();
  172. } while (key == -1);
  173. data[2] = !0; /* Or whatever. */
  174. err = 0;
  175. switch (repeat)
  176. {
  177. case GRUB_HIDBOOT_REPEAT_FIRST:
  178. timeout = 500;
  179. break;
  180. case GRUB_HIDBOOT_REPEAT:
  181. timeout = 50;
  182. break;
  183. default:
  184. timeout = 100;
  185. break;
  186. }
  187. /* Wait until the key is released. */
  188. currtime = grub_get_time_ms ();
  189. while (!err && data[2])
  190. {
  191. /* Implement a timeout. */
  192. if (grub_get_time_ms () > currtime + timeout)
  193. {
  194. if (repeat == 0)
  195. repeat = 1;
  196. else
  197. repeat = 2;
  198. grub_errno = GRUB_ERR_NONE;
  199. return key;
  200. }
  201. err = grub_usb_keyboard_getreport (usbdev, data);
  202. }
  203. if (repeat)
  204. {
  205. repeat = 0;
  206. goto again;
  207. }
  208. repeat = 0;
  209. grub_errno = GRUB_ERR_NONE;
  210. return key;
  211. }
  212. static int
  213. grub_usb_keyboard_getkeystatus (void)
  214. {
  215. grub_uint8_t data[8];
  216. int mods = 0;
  217. grub_err_t err;
  218. grub_uint64_t currtime;
  219. int timeout = 50;
  220. /* Set idle time to the minimum offered by the spec (4 milliseconds) so
  221. that we can find out the current state. */
  222. grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
  223. 0<<8, 0, 0, 0);
  224. currtime = grub_get_time_ms ();
  225. do
  226. {
  227. /* Get_Report. */
  228. err = grub_usb_keyboard_getreport (usbdev, data);
  229. /* Implement a timeout. */
  230. if (grub_get_time_ms () > currtime + timeout)
  231. break;
  232. }
  233. while (err || !data[0]);
  234. /* Go back to reporting every time an event occurs and not more often than
  235. that. */
  236. grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
  237. 0<<8, 0, 0, 0);
  238. /* We allowed a while for modifiers to show up in the report, but it is
  239. not an error if they never did. */
  240. if (err)
  241. return -1;
  242. grub_dprintf ("usb_keyboard",
  243. "report: 0x%02x 0x%02x 0x%02x 0x%02x"
  244. " 0x%02x 0x%02x 0x%02x 0x%02x\n",
  245. data[0], data[1], data[2], data[3],
  246. data[4], data[5], data[6], data[7]);
  247. /* Check Shift, Control, and Alt status. */
  248. if (data[0] & 0x02 || data[0] & 0x20)
  249. mods |= GRUB_TERM_STATUS_SHIFT;
  250. if (data[0] & 0x01 || data[0] & 0x10)
  251. mods |= GRUB_TERM_STATUS_CTRL;
  252. if (data[0] & 0x04 || data[0] & 0x40)
  253. mods |= GRUB_TERM_STATUS_ALT;
  254. grub_errno = GRUB_ERR_NONE;
  255. return mods;
  256. }
  257. static struct grub_term_input grub_usb_keyboard_term =
  258. {
  259. .name = "usb_keyboard",
  260. .checkkey = grub_usb_keyboard_checkkey,
  261. .getkey = grub_usb_keyboard_getkey,
  262. .getkeystatus = grub_usb_keyboard_getkeystatus,
  263. .next = 0
  264. };
  265. GRUB_MOD_INIT(usb_keyboard)
  266. {
  267. grub_usb_hid ();
  268. grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
  269. }
  270. GRUB_MOD_FINI(usb_keyboard)
  271. {
  272. grub_term_unregister_input (&grub_usb_keyboard_term);
  273. }