~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to bus/usb/usbhub.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2010-07-21 10:10:21 UTC
  • mto: (17.6.16 experimental) (1.26.4 upstream)
  • mto: This revision was merged to the branch mainline in revision 76.
  • Revision ID: james.westby@ubuntu.com-20100721101021-49cpjklxtr2us40d
Tags: upstream-1.98+20100720
ImportĀ upstreamĀ versionĀ 1.98+20100720

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
#include <grub/misc.h>
24
24
#include <grub/time.h>
25
25
 
 
26
#define GRUB_USBHUB_MAX_DEVICES 128
 
27
 
26
28
/* USB Supports 127 devices, with device 0 as special case.  */
27
 
static struct grub_usb_device *grub_usb_devs[128];
 
29
static struct grub_usb_device *grub_usb_devs[GRUB_USBHUB_MAX_DEVICES];
 
30
 
 
31
struct grub_usb_hub
 
32
{
 
33
  struct grub_usb_hub *next;
 
34
  grub_usb_controller_t controller;
 
35
  int nports;
 
36
  grub_usb_speed_t *speed;
 
37
  grub_usb_device_t dev;
 
38
};
 
39
 
 
40
struct grub_usb_hub *hubs;
28
41
 
29
42
/* Add a device that currently has device number 0 and resides on
30
43
   CONTROLLER, the Hub reported that the device speed is SPEED.  */
33
46
{
34
47
  grub_usb_device_t dev;
35
48
  int i;
 
49
  grub_usb_err_t err;
36
50
 
37
51
  dev = grub_zalloc (sizeof (struct grub_usb_device));
38
52
  if (! dev)
41
55
  dev->controller = *controller;
42
56
  dev->speed = speed;
43
57
 
44
 
  grub_usb_device_initialize (dev);
 
58
  err = grub_usb_device_initialize (dev);
 
59
  if (err)
 
60
    {
 
61
      grub_free (dev);
 
62
      return NULL;
 
63
    }
45
64
 
46
65
  /* Assign a new address to the device.  */
47
 
  for (i = 1; i < 128; i++)
 
66
  for (i = 1; i < GRUB_USBHUB_MAX_DEVICES; i++)
48
67
    {
49
68
      if (! grub_usb_devs[i])
50
69
        break;
51
70
    }
52
 
  if (i == 128)
 
71
  if (i == GRUB_USBHUB_MAX_DEVICES)
53
72
    {
54
73
      grub_error (GRUB_ERR_IO, "can't assign address to USB device");
 
74
      for (i = 0; i < 8; i++)
 
75
        grub_free (dev->config[i].descconf);
 
76
      grub_free (dev);
55
77
      return NULL;
56
78
    }
57
79
 
58
 
  grub_usb_control_msg (dev,
59
 
                        (GRUB_USB_REQTYPE_OUT
60
 
                         | GRUB_USB_REQTYPE_STANDARD
61
 
                         | GRUB_USB_REQTYPE_TARGET_DEV),
62
 
                        GRUB_USB_REQ_SET_ADDRESS,
63
 
                        i, 0, 0, NULL);
 
80
  err = grub_usb_control_msg (dev,
 
81
                              (GRUB_USB_REQTYPE_OUT
 
82
                               | GRUB_USB_REQTYPE_STANDARD
 
83
                               | GRUB_USB_REQTYPE_TARGET_DEV),
 
84
                              GRUB_USB_REQ_SET_ADDRESS,
 
85
                              i, 0, 0, NULL);
 
86
  if (err)
 
87
    {
 
88
      for (i = 0; i < 8; i++)
 
89
        grub_free (dev->config[i].descconf);
 
90
      grub_free (dev);
 
91
      return NULL;
 
92
    }
64
93
 
65
94
  dev->addr = i;
66
95
  dev->initialized = 1;
67
96
  grub_usb_devs[i] = dev;
68
97
 
 
98
  /* Wait "recovery interval", spec. says 2ms */
 
99
  grub_millisleep (2);
 
100
  
 
101
  grub_usb_device_attach (dev);
 
102
  
69
103
  return dev;
70
104
}
71
105
 
133
167
      if (err)
134
168
        continue;
135
169
      grub_dprintf ("usb", "Hub port %d status: 0x%02x\n", i, status);
136
 
            
 
170
 
137
171
      /* If connected, reset and enable the port.  */
138
172
      if (status & GRUB_USB_HUB_STATUS_CONNECTED)
139
173
        {
180
214
                 (grub_get_time_ms() < timeout) );
181
215
          if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) )
182
216
            continue;
 
217
 
 
218
          /* Wait a recovery time after reset, spec. says 10ms */
 
219
          grub_millisleep (10);
183
220
   
 
221
          /* Do reset of connection change bit */
 
222
          err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 
223
                                            | GRUB_USB_REQTYPE_CLASS
 
224
                                            | GRUB_USB_REQTYPE_TARGET_OTHER),
 
225
                                      GRUB_USB_REQ_CLEAR_FEATURE,
 
226
                                      GRUB_USB_HUB_FEATURE_C_CONNECTED,
 
227
                                      i, 0, 0);
 
228
          /* Just ignore the device if the Hub reports some error */
 
229
          if (err)
 
230
             continue;
 
231
          grub_dprintf ("usb", "Hub port - cleared connection change\n");
 
232
 
184
233
          /* Add the device and assign a device address to it.  */
185
234
          grub_dprintf ("usb", "Call hub_add_dev - port %d\n", i);
186
235
          next_dev = grub_usb_hub_add_dev (&dev->controller, speed);
196
245
  return GRUB_ERR_NONE;
197
246
}
198
247
 
 
248
static void
 
249
attach_root_port (grub_usb_controller_t controller, int portno,
 
250
                  grub_usb_speed_t speed)
 
251
{
 
252
  grub_usb_device_t dev;
 
253
  grub_err_t err;
 
254
 
 
255
  /* Disable the port. XXX: Why? */
 
256
  err = controller->dev->portstatus (controller, portno, 0);
 
257
  if (err)
 
258
    return;
 
259
 
 
260
  /* Enable the port.  */
 
261
  err = controller->dev->portstatus (controller, portno, 1);
 
262
  if (err)
 
263
    return;
 
264
 
 
265
  /* Enable the port and create a device.  */
 
266
  dev = grub_usb_hub_add_dev (controller, speed);
 
267
  if (! dev)
 
268
    return;
 
269
 
 
270
  /* If the device is a Hub, scan it for more devices.  */
 
271
  if (dev->descdev.class == 0x09)
 
272
    grub_usb_add_hub (dev);
 
273
}
 
274
 
199
275
grub_usb_err_t
200
276
grub_usb_root_hub (grub_usb_controller_t controller)
201
277
{
202
 
  grub_err_t err;
203
 
  int ports;
204
278
  int i;
 
279
  struct grub_usb_hub *hub;
 
280
  int changed=0;
 
281
 
 
282
  hub = grub_malloc (sizeof (*hub));
 
283
  if (!hub)
 
284
    return GRUB_USB_ERR_INTERNAL;
 
285
 
 
286
  hub->next = hubs;
 
287
  hubs = hub;
 
288
  hub->controller = grub_malloc (sizeof (*controller));
 
289
  if (!hub->controller)
 
290
    {
 
291
      grub_free (hub);
 
292
      return GRUB_USB_ERR_INTERNAL;
 
293
    }
 
294
 
 
295
  grub_memcpy (hub->controller, controller, sizeof (*controller));
 
296
  hub->dev = 0;
205
297
 
206
298
  /* Query the number of ports the root Hub has.  */
207
 
  ports = controller->dev->hubports (controller);
208
 
 
209
 
  for (i = 0; i < ports; i++)
210
 
    {
211
 
      grub_usb_speed_t speed = controller->dev->detect_dev (controller, i);
212
 
 
213
 
      if (speed != GRUB_USB_SPEED_NONE)
214
 
        {
215
 
          grub_usb_device_t dev;
216
 
 
217
 
          /* Enable the port.  */
218
 
          err = controller->dev->portstatus (controller, i, 1);
219
 
          if (err)
220
 
            continue;
221
 
 
222
 
          /* Enable the port and create a device.  */
223
 
          dev = grub_usb_hub_add_dev (controller, speed);
224
 
          if (! dev)
225
 
            continue;
226
 
 
227
 
          /* If the device is a Hub, scan it for more devices.  */
228
 
          if (dev->descdev.class == 0x09)
229
 
            grub_usb_add_hub (dev);
230
 
        }
 
299
  hub->nports = controller->dev->hubports (controller);
 
300
  hub->speed = grub_malloc (sizeof (hub->speed[0]) * hub->nports);
 
301
  if (!hub->speed)
 
302
    {
 
303
      grub_free (hub->controller);
 
304
      grub_free (hub);
 
305
      return GRUB_USB_ERR_INTERNAL;
 
306
    }
 
307
 
 
308
  for (i = 0; i < hub->nports; i++)
 
309
    {
 
310
      hub->speed[i] = controller->dev->detect_dev (hub->controller, i,
 
311
                                                   &changed);
 
312
 
 
313
      if (hub->speed[i] != GRUB_USB_SPEED_NONE)
 
314
        attach_root_port (hub->controller, i, hub->speed[i]);
231
315
    }
232
316
 
233
317
  return GRUB_USB_ERR_NONE;
234
318
}
235
319
 
 
320
static void
 
321
poll_nonroot_hub (grub_usb_device_t dev)
 
322
{
 
323
  struct grub_usb_usb_hubdesc hubdesc;
 
324
  grub_err_t err;
 
325
  int i;
 
326
  grub_uint64_t timeout;
 
327
  grub_usb_device_t next_dev;
 
328
  
 
329
  err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 
330
                                    | GRUB_USB_REQTYPE_CLASS
 
331
                                    | GRUB_USB_REQTYPE_TARGET_DEV),
 
332
                              GRUB_USB_REQ_GET_DESCRIPTOR,
 
333
                              (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
 
334
                              0, sizeof (hubdesc), (char *) &hubdesc);
 
335
  if (err)
 
336
    return;
 
337
    
 
338
  /* Iterate over the Hub ports.  */
 
339
  for (i = 1; i <= hubdesc.portcnt; i++)
 
340
    {
 
341
      grub_uint32_t status;
 
342
 
 
343
      /* Get the port status.  */
 
344
      err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 
345
                                        | GRUB_USB_REQTYPE_CLASS
 
346
                                        | GRUB_USB_REQTYPE_TARGET_OTHER),
 
347
                                  GRUB_USB_REQ_GET_STATUS,
 
348
                                  0, i, sizeof (status), (char *) &status);
 
349
      /* Just ignore the device if the Hub does not report the
 
350
         status.  */
 
351
      if (err)
 
352
        continue;
 
353
            
 
354
      /* Connected and status of connection changed ? */
 
355
      if ((status & GRUB_USB_HUB_STATUS_CONNECTED)
 
356
          && (status & GRUB_USB_HUB_STATUS_C_CONNECTED))
 
357
        {
 
358
          grub_usb_speed_t speed;
 
359
 
 
360
          /* Determine the device speed.  */
 
361
          if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
 
362
            speed = GRUB_USB_SPEED_LOW;
 
363
          else
 
364
            {
 
365
              if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
 
366
                speed = GRUB_USB_SPEED_HIGH;
 
367
              else
 
368
                speed = GRUB_USB_SPEED_FULL;
 
369
            }
 
370
 
 
371
          /* A device is actually connected to this port.
 
372
           * Now do reset of port. */
 
373
          err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 
374
                                            | GRUB_USB_REQTYPE_CLASS
 
375
                                            | GRUB_USB_REQTYPE_TARGET_OTHER),
 
376
                                      GRUB_USB_REQ_SET_FEATURE,
 
377
                                      GRUB_USB_HUB_FEATURE_PORT_RESET,
 
378
                                      i, 0, 0);
 
379
          /* If the Hub does not cooperate for this port, just skip
 
380
             the port.  */
 
381
          if (err)
 
382
            continue;
 
383
 
 
384
          /* Wait for reset procedure done */
 
385
          timeout = grub_get_time_ms () + 1000;
 
386
          do
 
387
            {
 
388
              /* Get the port status.  */
 
389
              err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 
390
                                                | GRUB_USB_REQTYPE_CLASS
 
391
                                                | GRUB_USB_REQTYPE_TARGET_OTHER),
 
392
                                          GRUB_USB_REQ_GET_STATUS,
 
393
                                          0, i, sizeof (status), (char *) &status);
 
394
            }
 
395
          while (!err &&
 
396
                 !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) &&
 
397
                 (grub_get_time_ms() < timeout) );
 
398
          if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) )
 
399
            continue;
 
400
 
 
401
          /* Wait a recovery time after reset, spec. says 10ms */
 
402
          grub_millisleep (10);
 
403
 
 
404
          /* Do reset of connection change bit */
 
405
          err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 
406
                                            | GRUB_USB_REQTYPE_CLASS
 
407
                                            | GRUB_USB_REQTYPE_TARGET_OTHER),
 
408
                                      GRUB_USB_REQ_CLEAR_FEATURE,
 
409
                                      GRUB_USB_HUB_FEATURE_C_CONNECTED,
 
410
                                      i, 0, 0);
 
411
          /* Just ignore the device if the Hub reports some error */
 
412
          if (err)
 
413
             continue;
 
414
 
 
415
          /* Add the device and assign a device address to it.  */
 
416
          next_dev = grub_usb_hub_add_dev (&dev->controller, speed);
 
417
          if (! next_dev)
 
418
            continue;
 
419
 
 
420
          /* If the device is a Hub, scan it for more devices.  */
 
421
          if (next_dev->descdev.class == 0x09)
 
422
            grub_usb_add_hub (next_dev);
 
423
        }
 
424
    }
 
425
 
 
426
  return;
 
427
}
 
428
 
 
429
void
 
430
grub_usb_poll_devices (void)
 
431
{
 
432
  struct grub_usb_hub *hub;
 
433
  int i;
 
434
 
 
435
  for (hub = hubs; hub; hub = hub->next)
 
436
    {
 
437
      int changed=0;
 
438
      /* Do we have to recheck number of ports?  */
 
439
      /* No, it should be never changed, it should be constant. */
 
440
      for (i = 0; i < hub->nports; i++)
 
441
        {
 
442
          grub_usb_speed_t speed;
 
443
 
 
444
          speed = hub->controller->dev->detect_dev (hub->controller, i,
 
445
                                                    &changed);
 
446
 
 
447
          if (speed != GRUB_USB_SPEED_NONE)
 
448
            {
 
449
              if (changed)
 
450
                attach_root_port (hub->controller, i, speed);
 
451
            }
 
452
 
 
453
          /* XXX: There should be also handling
 
454
           * of disconnected devices. */
 
455
           
 
456
          hub->speed[i] = speed;
 
457
        }
 
458
    }
 
459
 
 
460
  /* We should check changes of non-root hubs too. */
 
461
  for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
 
462
    {
 
463
      grub_usb_device_t dev = grub_usb_devs[i];
 
464
 
 
465
      if (dev && dev->descdev.class == 0x09)
 
466
        {
 
467
          poll_nonroot_hub (dev);
 
468
        }
 
469
    }
 
470
 
 
471
}
 
472
 
236
473
int
237
474
grub_usb_iterate (int (*hook) (grub_usb_device_t dev))
238
475
{
239
476
  int i;
240
477
 
241
 
  for (i = 0; i < 128; i++)
 
478
  for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
242
479
    {
243
480
      if (grub_usb_devs[i])
244
481
        {