~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/term/usb_keyboard.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
20
#include <grub/term.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
#include <grub/keyboard_layouts.h>
 
29
 
 
30
 
 
31
 
 
32
enum
 
33
  {
 
34
    KEY_NO_KEY = 0x00,
 
35
    KEY_ERR_BUFFER = 0x01,
 
36
    KEY_ERR_POST  = 0x02,
 
37
    KEY_ERR_UNDEF = 0x03,
 
38
    KEY_CAPS_LOCK = 0x39,
 
39
    KEY_NUM_LOCK  = 0x53,
 
40
  };
 
41
 
 
42
enum
 
43
  {
 
44
    LED_NUM_LOCK = 0x01,
 
45
    LED_CAPS_LOCK = 0x02
 
46
  };
 
47
 
 
48
/* Valid values for bRequest.  See HID definition version 1.11 section 7.2. */
 
49
#define USB_HID_GET_REPORT      0x01
 
50
#define USB_HID_GET_IDLE        0x02
 
51
#define USB_HID_GET_PROTOCOL    0x03
 
52
#define USB_HID_SET_REPORT      0x09
 
53
#define USB_HID_SET_IDLE        0x0A
 
54
#define USB_HID_SET_PROTOCOL    0x0B
 
55
 
 
56
#define USB_HID_BOOT_SUBCLASS   0x01
 
57
#define USB_HID_KBD_PROTOCOL    0x01
 
58
 
 
59
#define GRUB_USB_KEYBOARD_LEFT_CTRL   0x01
 
60
#define GRUB_USB_KEYBOARD_LEFT_SHIFT  0x02
 
61
#define GRUB_USB_KEYBOARD_LEFT_ALT    0x04
 
62
#define GRUB_USB_KEYBOARD_RIGHT_CTRL  0x10
 
63
#define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
 
64
#define GRUB_USB_KEYBOARD_RIGHT_ALT   0x40
 
65
 
 
66
struct grub_usb_keyboard_data
 
67
{
 
68
  grub_usb_device_t usbdev;
 
69
  grub_uint8_t status;
 
70
  grub_uint16_t mods;
 
71
  int interfno;
 
72
  struct grub_usb_desc_endp *endp;
 
73
  grub_usb_transfer_t transfer;
 
74
  grub_uint8_t report[8];
 
75
  int dead;
 
76
  int last_key;
 
77
  grub_uint64_t repeat_time;
 
78
  grub_uint8_t current_report[8];
 
79
  grub_uint8_t last_report[8];
 
80
  int index;
 
81
  int max_index;
 
82
};
 
83
 
 
84
static int grub_usb_keyboard_getkey (struct grub_term_input *term);
 
85
static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
 
86
 
 
87
static struct grub_term_input grub_usb_keyboard_term =
 
88
  {
 
89
    .getkey = grub_usb_keyboard_getkey,
 
90
    .getkeystatus = grub_usb_keyboard_getkeystatus,
 
91
    .next = 0
 
92
  };
 
93
 
 
94
static struct grub_term_input grub_usb_keyboards[16];
 
95
 
 
96
static int
 
97
interpret_status (grub_uint8_t data0)
 
98
{
 
99
  int mods = 0;
 
100
 
 
101
  /* Check Shift, Control, and Alt status.  */
 
102
  if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
 
103
    mods |= GRUB_TERM_STATUS_LSHIFT;
 
104
  if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
 
105
    mods |= GRUB_TERM_STATUS_RSHIFT;
 
106
  if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
 
107
    mods |= GRUB_TERM_STATUS_LCTRL;
 
108
  if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
 
109
    mods |= GRUB_TERM_STATUS_RCTRL;
 
110
  if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
 
111
    mods |= GRUB_TERM_STATUS_LALT;
 
112
  if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
 
113
    mods |= GRUB_TERM_STATUS_RALT;
 
114
 
 
115
  return mods;
 
116
}
 
117
 
 
118
static void
 
119
grub_usb_keyboard_detach (grub_usb_device_t usbdev,
 
120
                          int config __attribute__ ((unused)),
 
121
                          int interface __attribute__ ((unused)))
 
122
{
 
123
  unsigned i;
 
124
  for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
 
125
    {
 
126
      struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
 
127
 
 
128
      if (!data)
 
129
        continue;
 
130
 
 
131
      if (data->usbdev != usbdev)
 
132
        continue;
 
133
 
 
134
      if (data->transfer)
 
135
        grub_usb_cancel_transfer (data->transfer);
 
136
 
 
137
      grub_term_unregister_input (&grub_usb_keyboards[i]);
 
138
      grub_free ((char *) grub_usb_keyboards[i].name);
 
139
      grub_usb_keyboards[i].name = NULL;
 
140
      grub_free (grub_usb_keyboards[i].data);
 
141
      grub_usb_keyboards[i].data = 0;
 
142
    }
 
143
}
 
144
 
 
145
static int
 
146
grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
 
147
{
 
148
  unsigned curnum;
 
149
  struct grub_usb_keyboard_data *data;
 
150
  struct grub_usb_desc_endp *endp = NULL;
 
151
  int j;
 
152
 
 
153
  grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
 
154
                usbdev->descdev.class, usbdev->descdev.subclass,
 
155
                usbdev->descdev.protocol, configno, interfno);
 
156
 
 
157
  for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
 
158
    if (!grub_usb_keyboards[curnum].data)
 
159
      break;
 
160
 
 
161
  if (curnum == ARRAY_SIZE (grub_usb_keyboards))
 
162
    return 0;
 
163
 
 
164
  if (usbdev->descdev.class != 0 
 
165
      || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
 
166
    return 0;
 
167
 
 
168
  if (usbdev->config[configno].interf[interfno].descif->subclass
 
169
      != USB_HID_BOOT_SUBCLASS
 
170
      || usbdev->config[configno].interf[interfno].descif->protocol
 
171
      != USB_HID_KBD_PROTOCOL)
 
172
    return 0;
 
173
 
 
174
  for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
 
175
       j++)
 
176
    {
 
177
      endp = &usbdev->config[configno].interf[interfno].descendp[j];
 
178
 
 
179
      if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
 
180
          == GRUB_USB_EP_INTERRUPT)
 
181
        break;
 
182
    }
 
183
  if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
 
184
    return 0;
 
185
 
 
186
  grub_dprintf ("usb_keyboard", "HID found!\n");
 
187
 
 
188
  data = grub_malloc (sizeof (*data));
 
189
  if (!data)
 
190
    {
 
191
      grub_print_error ();
 
192
      return 0;
 
193
    }
 
194
 
 
195
  data->usbdev = usbdev;
 
196
  data->interfno = interfno;
 
197
  data->endp = endp;
 
198
 
 
199
  /* Configure device */
 
200
  grub_usb_set_configuration (usbdev, configno + 1);
 
201
  
 
202
  /* Place the device in boot mode.  */
 
203
  grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
 
204
                        USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
 
205
 
 
206
  /* Reports every time an event occurs and not more often than that.  */
 
207
  grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
 
208
                        USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
 
209
 
 
210
  grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
 
211
               sizeof (grub_usb_keyboards[curnum]));
 
212
  grub_usb_keyboards[curnum].data = data;
 
213
  usbdev->config[configno].interf[interfno].detach_hook
 
214
    = grub_usb_keyboard_detach;
 
215
  grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
 
216
  if (!grub_usb_keyboards[curnum].name)
 
217
    {
 
218
      grub_print_error ();
 
219
      return 0;
 
220
    }
 
221
 
 
222
  /* Test showed that getting report may make the keyboard go nuts.
 
223
     Moreover since we're reattaching keyboard it usually sends
 
224
     an initial message on interrupt pipe and so we retrieve
 
225
     the same keystatus.
 
226
   */
 
227
#if 0
 
228
  {
 
229
    grub_uint8_t report[8];
 
230
    grub_usb_err_t err;
 
231
    grub_memset (report, 0, sizeof (report));
 
232
    err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
 
233
                                USB_HID_GET_REPORT, 0x0100, interfno,
 
234
                                sizeof (report), (char *) report);
 
235
    if (err)
 
236
      data->status = 0;
 
237
    else
 
238
      data->status = report[0];
 
239
  }
 
240
#else
 
241
  data->status = 0;
 
242
#endif
 
243
 
 
244
  data->transfer = grub_usb_bulk_read_background (usbdev,
 
245
                                                  data->endp->endp_addr,
 
246
                                                  sizeof (data->report),
 
247
                                                  (char *) data->report);
 
248
  if (!data->transfer)
 
249
    {
 
250
      grub_print_error ();
 
251
      return 0;
 
252
    }
 
253
 
 
254
  data->last_key = -1;
 
255
  data->mods = 0;
 
256
  data->dead = 0;
 
257
 
 
258
  grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
 
259
 
 
260
  return 1;
 
261
}
 
262
 
 
263
 
 
264
 
 
265
static void
 
266
send_leds (struct grub_usb_keyboard_data *termdata)
 
267
{
 
268
  char report[1];
 
269
  report[0] = 0;
 
270
  if (termdata->mods & GRUB_TERM_STATUS_CAPS)
 
271
    report[0] |= LED_CAPS_LOCK;
 
272
  if (termdata->mods & GRUB_TERM_STATUS_NUM)
 
273
    report[0] |= LED_NUM_LOCK;
 
274
  grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
 
275
                        USB_HID_SET_REPORT, 0x0200, termdata->interfno,
 
276
                        sizeof (report), (char *) report);
 
277
  grub_errno = GRUB_ERR_NONE;
 
278
}
 
279
 
 
280
static int
 
281
parse_keycode (struct grub_usb_keyboard_data *termdata)
 
282
{
 
283
  int index = termdata->index;
 
284
  int i, keycode;
 
285
 
 
286
  /* Sanity check */
 
287
  if (index < 2)
 
288
    index = 2;
 
289
 
 
290
  for ( ; index < termdata->max_index; index++)
 
291
    {
 
292
      keycode = termdata->current_report[index];
 
293
      
 
294
      if (keycode == KEY_NO_KEY
 
295
          || keycode == KEY_ERR_BUFFER
 
296
          || keycode == KEY_ERR_POST
 
297
          || keycode == KEY_ERR_UNDEF)
 
298
        {
 
299
          /* Don't parse (rest of) this report */
 
300
          termdata->index = 0;
 
301
          if (keycode != KEY_NO_KEY)
 
302
          /* Don't replace last report with current faulty report
 
303
           * in future ! */
 
304
            grub_memcpy (termdata->current_report,
 
305
                         termdata->last_report,
 
306
                         sizeof (termdata->report));
 
307
          return GRUB_TERM_NO_KEY;
 
308
        }
 
309
 
 
310
      /* Try to find current keycode in last report. */
 
311
      for (i = 2; i < 8; i++)
 
312
        if (keycode == termdata->last_report[i])
 
313
          break;
 
314
      if (i < 8)
 
315
        /* Keycode is in last report, it means it was not released,
 
316
         * ignore it. */
 
317
        continue;
 
318
        
 
319
      if (keycode == KEY_CAPS_LOCK)
 
320
        {
 
321
          termdata->mods ^= GRUB_TERM_STATUS_CAPS;
 
322
          send_leds (termdata);
 
323
          continue;
 
324
        }
 
325
 
 
326
      if (keycode == KEY_NUM_LOCK)
 
327
        {
 
328
          termdata->mods ^= GRUB_TERM_STATUS_NUM;
 
329
          send_leds (termdata);
 
330
          continue;
 
331
        }
 
332
 
 
333
      termdata->last_key = grub_term_map_key (keycode,
 
334
                                              interpret_status (termdata->current_report[0])
 
335
                                                | termdata->mods);
 
336
      termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
 
337
 
 
338
      grub_errno = GRUB_ERR_NONE;
 
339
 
 
340
      index++;
 
341
      if (index >= termdata->max_index)
 
342
        termdata->index = 0;
 
343
      else
 
344
        termdata->index = index;
 
345
 
 
346
      return termdata->last_key;
 
347
    }
 
348
 
 
349
  /* All keycodes parsed */
 
350
  termdata->index = 0;
 
351
  return GRUB_TERM_NO_KEY;
 
352
}
 
353
 
 
354
static int
 
355
grub_usb_keyboard_getkey (struct grub_term_input *term)
 
356
{
 
357
  grub_usb_err_t err;
 
358
  struct grub_usb_keyboard_data *termdata = term->data;
 
359
  grub_size_t actual;
 
360
  int keycode = GRUB_TERM_NO_KEY;
 
361
 
 
362
  if (termdata->dead)
 
363
    return GRUB_TERM_NO_KEY;
 
364
 
 
365
  if (termdata->index)
 
366
    keycode = parse_keycode (termdata);
 
367
  if (keycode != GRUB_TERM_NO_KEY)
 
368
    return keycode;
 
369
    
 
370
  /* Poll interrupt pipe.  */
 
371
  err = grub_usb_check_transfer (termdata->transfer, &actual);
 
372
 
 
373
  if (err == GRUB_USB_ERR_WAIT)
 
374
    {
 
375
      if (termdata->last_key != -1
 
376
          && grub_get_time_ms () > termdata->repeat_time)
 
377
        {
 
378
          termdata->repeat_time = grub_get_time_ms ()
 
379
            + GRUB_TERM_REPEAT_INTERVAL;
 
380
          return termdata->last_key;
 
381
        }
 
382
      return GRUB_TERM_NO_KEY;
 
383
    }
 
384
 
 
385
  if (!err && (actual >= 3))
 
386
    grub_memcpy (termdata->last_report,
 
387
                 termdata->current_report,
 
388
                 sizeof (termdata->report));
 
389
                 
 
390
  grub_memcpy (termdata->current_report,
 
391
               termdata->report,
 
392
               sizeof (termdata->report));
 
393
 
 
394
  termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
 
395
                                                      termdata->endp->endp_addr,
 
396
                                                      sizeof (termdata->report),
 
397
                                                      (char *) termdata->report);
 
398
  if (!termdata->transfer)
 
399
    {
 
400
      grub_printf ("%s failed. Stopped\n", term->name);
 
401
      termdata->dead = 1;
 
402
    }
 
403
 
 
404
  termdata->last_key = -1;
 
405
 
 
406
  grub_dprintf ("usb_keyboard",
 
407
                "err = %d, actual = %" PRIuGRUB_SIZE
 
408
                " report: 0x%02x 0x%02x 0x%02x 0x%02x"
 
409
                " 0x%02x 0x%02x 0x%02x 0x%02x\n",
 
410
                err, actual,
 
411
                termdata->current_report[0], termdata->current_report[1],
 
412
                termdata->current_report[2], termdata->current_report[3],
 
413
                termdata->current_report[4], termdata->current_report[5],
 
414
                termdata->current_report[6], termdata->current_report[7]);
 
415
 
 
416
  if (err || actual < 1)
 
417
    return GRUB_TERM_NO_KEY;
 
418
 
 
419
  termdata->status = termdata->current_report[0];
 
420
 
 
421
  if (actual < 3)
 
422
    return GRUB_TERM_NO_KEY;
 
423
 
 
424
  termdata->index = 2; /* New data received. */
 
425
  termdata->max_index = actual;
 
426
  
 
427
  return parse_keycode (termdata);
 
428
}
 
429
 
 
430
static int
 
431
grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
 
432
{
 
433
  struct grub_usb_keyboard_data *termdata = term->data;
 
434
 
 
435
  return interpret_status (termdata->status) | termdata->mods;
 
436
}
 
437
 
 
438
struct grub_usb_attach_desc attach_hook =
 
439
{
 
440
  .class = GRUB_USB_CLASS_HID,
 
441
  .hook = grub_usb_keyboard_attach
 
442
};
 
443
 
 
444
GRUB_MOD_INIT(usb_keyboard)
 
445
{
 
446
  grub_usb_register_attach_hook_class (&attach_hook);
 
447
}
 
448
 
 
449
GRUB_MOD_FINI(usb_keyboard)
 
450
{
 
451
  unsigned i;
 
452
  for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
 
453
    if (grub_usb_keyboards[i].data)
 
454
      {
 
455
        struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
 
456
 
 
457
        if (!data)
 
458
          continue;
 
459
        
 
460
        if (data->transfer)
 
461
          grub_usb_cancel_transfer (data->transfer);
 
462
 
 
463
        grub_term_unregister_input (&grub_usb_keyboards[i]);
 
464
        grub_free ((char *) grub_usb_keyboards[i].name);
 
465
        grub_usb_keyboards[i].name = NULL;
 
466
        grub_free (grub_usb_keyboards[i].data);
 
467
        grub_usb_keyboards[i].data = 0;
 
468
      }
 
469
  grub_usb_unregister_attach_hook_class (&attach_hook);
 
470
}