/* usb.c - Generic USB interfaces. */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2008 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ #include #include #include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); static grub_usb_controller_dev_t grub_usb_list; static struct grub_usb_attach_desc *attach_hooks; /* Iterate over all controllers found by the driver. */ static int grub_usb_controller_dev_register_iter (grub_usb_controller_t dev, void *data) { grub_usb_controller_dev_t usb = data; dev->dev = usb; /* Enable the ports of the USB Root Hub. */ grub_usb_root_hub (dev); return 0; } void grub_usb_controller_dev_register (grub_usb_controller_dev_t usb) { usb->next = grub_usb_list; grub_usb_list = usb; if (usb->iterate) usb->iterate (grub_usb_controller_dev_register_iter, usb); } void grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb) { grub_usb_controller_dev_t *p, q; for (p = &grub_usb_list, q = *p; q; p = &(q->next), q = q->next) if (q == usb) { *p = q->next; break; } } #if 0 /* Context for grub_usb_controller_iterate. */ struct grub_usb_controller_iterate_ctx { grub_usb_controller_iterate_hook_t hook; void *hook_data; grub_usb_controller_dev_t p; }; /* Helper for grub_usb_controller_iterate. */ static int grub_usb_controller_iterate_iter (grub_usb_controller_t dev, void *data) { struct grub_usb_controller_iterate_ctx *ctx = data; dev->dev = ctx->p; if (ctx->hook (dev, ctx->hook_data)) return 1; return 0; } int grub_usb_controller_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data) { struct grub_usb_controller_iterate_ctx ctx = { .hook = hook, .hook_data = hook_data }; /* Iterate over all controller drivers. */ for (ctx.p = grub_usb_list; ctx.p; ctx.p = ctx.p->next) { /* Iterate over the busses of the controllers. XXX: Actually, a hub driver should do this. */ if (ctx.p->iterate (grub_usb_controller_iterate_iter, &ctx)) return 1; } return 0; } #endif grub_usb_err_t grub_usb_clear_halt (grub_usb_device_t dev, int endpoint) { dev->toggle[endpoint] = 0; return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT | GRUB_USB_REQTYPE_STANDARD | GRUB_USB_REQTYPE_TARGET_ENDP), GRUB_USB_REQ_CLEAR_FEATURE, GRUB_USB_FEATURE_ENDP_HALT, endpoint, 0, 0); } grub_usb_err_t grub_usb_set_configuration (grub_usb_device_t dev, int configuration) { grub_memset (dev->toggle, 0, sizeof (dev->toggle)); return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT | GRUB_USB_REQTYPE_STANDARD | GRUB_USB_REQTYPE_TARGET_DEV), GRUB_USB_REQ_SET_CONFIGURATION, configuration, 0, 0, NULL); } grub_usb_err_t grub_usb_get_descriptor (grub_usb_device_t dev, grub_uint8_t type, grub_uint8_t index, grub_size_t size, char *data) { return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN | GRUB_USB_REQTYPE_STANDARD | GRUB_USB_REQTYPE_TARGET_DEV), GRUB_USB_REQ_GET_DESCRIPTOR, (type << 8) | index, 0, size, data); } struct grub_usb_desc_endp * grub_usb_get_endpdescriptor (grub_usb_device_t usbdev, int addr) { int i; for (i = 0; i < usbdev->config[0].descconf->numif; i++) { struct grub_usb_desc_if *interf; int j; interf = usbdev->config[0].interf[i].descif; for (j = 0; j < interf->endpointcnt; j++) { struct grub_usb_desc_endp *endp; endp = &usbdev->config[0].interf[i].descendp[j]; if (endp->endp_addr == addr) return endp; } } return NULL; } grub_usb_err_t grub_usb_device_initialize (grub_usb_device_t dev) { struct grub_usb_desc_device *descdev; struct grub_usb_desc_config config; grub_usb_err_t err; int i; /* First we have to read first 8 bytes only and determine * max. size of packet */ dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */ err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, 0, 8, (char *) &dev->descdev); if (err) return err; /* Now we have valid value in dev->descdev.maxsize0, * so we can read whole device descriptor */ err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, 0, sizeof (struct grub_usb_desc_device), (char *) &dev->descdev); if (err) return err; descdev = &dev->descdev; for (i = 0; i < 8; i++) dev->config[i].descconf = NULL; if (descdev->configcnt == 0) { err = GRUB_USB_ERR_BADDEVICE; goto fail; } for (i = 0; i < descdev->configcnt; i++) { int pos; int currif; char *data; /* First just read the first 4 bytes of the configuration descriptor, after that it is known how many bytes really have to be read. */ err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, 4, (char *) &config); data = grub_malloc (config.totallen); if (! data) { err = GRUB_USB_ERR_INTERNAL; goto fail; } dev->config[i].descconf = (struct grub_usb_desc_config *) data; err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, config.totallen, data); if (err) goto fail; /* Skip the configuration descriptor. */ pos = dev->config[i].descconf->length; /* Read all interfaces. */ for (currif = 0; currif < dev->config[i].descconf->numif; currif++) { while (pos < config.totallen && ((struct grub_usb_desc *)&data[pos])->type != GRUB_USB_DESCRIPTOR_INTERFACE) pos += ((struct grub_usb_desc *)&data[pos])->length; dev->config[i].interf[currif].descif = (struct grub_usb_desc_if *) &data[pos]; pos += dev->config[i].interf[currif].descif->length; while (pos < config.totallen && ((struct grub_usb_desc *)&data[pos])->type != GRUB_USB_DESCRIPTOR_ENDPOINT) pos += ((struct grub_usb_desc *)&data[pos])->length; /* Point to the first endpoint. */ dev->config[i].interf[currif].descendp = (struct grub_usb_desc_endp *) &data[pos]; pos += (sizeof (struct grub_usb_desc_endp) * dev->config[i].interf[currif].descif->endpointcnt); } } return GRUB_USB_ERR_NONE; fail: for (i = 0; i < 8; i++) grub_free (dev->config[i].descconf); return err; } void grub_usb_device_attach (grub_usb_device_t dev) { int i; /* XXX: Just check configuration 0 for now. */ for (i = 0; i < dev->config[0].descconf->numif; i++) { struct grub_usb_desc_if *interf; struct grub_usb_attach_desc *desc; interf = dev->config[0].interf[i].descif; grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", i, interf->class, interf->subclass, interf->protocol); if (dev->config[0].interf[i].attached) continue; for (desc = attach_hooks; desc; desc = desc->next) if (interf->class == desc->class && desc->hook (dev, 0, i)) dev->config[0].interf[i].attached = 1; if (dev->config[0].interf[i].attached) continue; switch (interf->class) { case GRUB_USB_CLASS_MASS_STORAGE: grub_dl_load ("usbms"); break; case GRUB_USB_CLASS_HID: grub_dl_load ("usb_keyboard"); break; case 0xff: /* FIXME: don't load useless modules. */ grub_dl_load ("usbserial_ftdi"); grub_dl_load ("usbserial_pl2303"); break; } } } /* Helper for grub_usb_register_attach_hook_class. */ static int grub_usb_register_attach_hook_class_iter (grub_usb_device_t usbdev, void *data) { struct grub_usb_attach_desc *desc = data; struct grub_usb_desc_device *descdev = &usbdev->descdev; int i; if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 || descdev->configcnt == 0) return 0; /* XXX: Just check configuration 0 for now. */ for (i = 0; i < usbdev->config[0].descconf->numif; i++) { struct grub_usb_desc_if *interf; interf = usbdev->config[0].interf[i].descif; grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", i, interf->class, interf->subclass, interf->protocol); if (usbdev->config[0].interf[i].attached) continue; if (interf->class != desc->class) continue; if (desc->hook (usbdev, 0, i)) usbdev->config[0].interf[i].attached = 1; } return 0; } void grub_usb_register_attach_hook_class (struct grub_usb_attach_desc *desc) { desc->next = attach_hooks; attach_hooks = desc; grub_usb_iterate (grub_usb_register_attach_hook_class_iter, desc); } void grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc) { grub_list_remove (GRUB_AS_LIST (desc)); } GRUB_MOD_INIT(usb) { grub_term_poll_usb = grub_usb_poll_devices; } GRUB_MOD_FINI(usb) { grub_term_poll_usb = NULL; }