/* usb.c - Generic USB interfaces. */ /* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2008 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); static struct VasEBoot_usb_attach_desc *attach_hooks; #if 0 /* Context for VasEBoot_usb_controller_iterate. */ struct VasEBoot_usb_controller_iterate_ctx { VasEBoot_usb_controller_iterate_hook_t hook; void *hook_data; VasEBoot_usb_controller_dev_t p; }; /* Helper for VasEBoot_usb_controller_iterate. */ static int VasEBoot_usb_controller_iterate_iter (VasEBoot_usb_controller_t dev, void *data) { struct VasEBoot_usb_controller_iterate_ctx *ctx = data; dev->dev = ctx->p; if (ctx->hook (dev, ctx->hook_data)) return 1; return 0; } int VasEBoot_usb_controller_iterate (VasEBoot_usb_controller_iterate_hook_t hook, void *hook_data) { struct VasEBoot_usb_controller_iterate_ctx ctx = { .hook = hook, .hook_data = hook_data }; /* Iterate over all controller drivers. */ for (ctx.p = VasEBoot_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 (VasEBoot_usb_controller_iterate_iter, &ctx)) return 1; } return 0; } #endif VasEBoot_usb_err_t VasEBoot_usb_clear_halt (VasEBoot_usb_device_t dev, int endpoint) { dev->toggle[endpoint] = 0; return VasEBoot_usb_control_msg (dev, (VasEBoot_USB_REQTYPE_OUT | VasEBoot_USB_REQTYPE_STANDARD | VasEBoot_USB_REQTYPE_TARGET_ENDP), VasEBoot_USB_REQ_CLEAR_FEATURE, VasEBoot_USB_FEATURE_ENDP_HALT, endpoint, 0, 0); } VasEBoot_usb_err_t VasEBoot_usb_set_configuration (VasEBoot_usb_device_t dev, int configuration) { VasEBoot_memset (dev->toggle, 0, sizeof (dev->toggle)); return VasEBoot_usb_control_msg (dev, (VasEBoot_USB_REQTYPE_OUT | VasEBoot_USB_REQTYPE_STANDARD | VasEBoot_USB_REQTYPE_TARGET_DEV), VasEBoot_USB_REQ_SET_CONFIGURATION, configuration, 0, 0, NULL); } VasEBoot_usb_err_t VasEBoot_usb_get_descriptor (VasEBoot_usb_device_t dev, VasEBoot_uint8_t type, VasEBoot_uint8_t index, VasEBoot_size_t size, char *data) { return VasEBoot_usb_control_msg (dev, (VasEBoot_USB_REQTYPE_IN | VasEBoot_USB_REQTYPE_STANDARD | VasEBoot_USB_REQTYPE_TARGET_DEV), VasEBoot_USB_REQ_GET_DESCRIPTOR, (type << 8) | index, 0, size, data); } VasEBoot_usb_err_t VasEBoot_usb_device_initialize (VasEBoot_usb_device_t dev) { struct VasEBoot_usb_desc_device *descdev; struct VasEBoot_usb_desc_config config; VasEBoot_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 = VasEBoot_usb_get_descriptor (dev, VasEBoot_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 = VasEBoot_usb_get_descriptor (dev, VasEBoot_USB_DESCRIPTOR_DEVICE, 0, sizeof (struct VasEBoot_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 = VasEBoot_USB_ERR_BADDEVICE; goto fail; } for (i = 0; i < descdev->configcnt; i++) { int pos; int currif; char *data; struct VasEBoot_usb_desc *desc; /* 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 = VasEBoot_usb_get_descriptor (dev, VasEBoot_USB_DESCRIPTOR_CONFIG, i, 4, (char *) &config); data = VasEBoot_malloc (config.totallen); if (! data) { err = VasEBoot_USB_ERR_INTERNAL; goto fail; } dev->config[i].descconf = (struct VasEBoot_usb_desc_config *) data; err = VasEBoot_usb_get_descriptor (dev, VasEBoot_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) { desc = (struct VasEBoot_usb_desc *)&data[pos]; if (desc->type == VasEBoot_USB_DESCRIPTOR_INTERFACE) break; if (!desc->length) { err = VasEBoot_USB_ERR_BADDEVICE; goto fail; } pos += desc->length; } dev->config[i].interf[currif].descif = (struct VasEBoot_usb_desc_if *) &data[pos]; pos += dev->config[i].interf[currif].descif->length; while (pos < config.totallen) { desc = (struct VasEBoot_usb_desc *)&data[pos]; if (desc->type == VasEBoot_USB_DESCRIPTOR_ENDPOINT) break; if (!desc->length) { err = VasEBoot_USB_ERR_BADDEVICE; goto fail; } pos += desc->length; } /* Point to the first endpoint. */ dev->config[i].interf[currif].descendp = (struct VasEBoot_usb_desc_endp *) &data[pos]; pos += (sizeof (struct VasEBoot_usb_desc_endp) * dev->config[i].interf[currif].descif->endpointcnt); } } return VasEBoot_USB_ERR_NONE; fail: for (i = 0; i < 8; i++) VasEBoot_free (dev->config[i].descconf); return err; } void VasEBoot_usb_device_attach (VasEBoot_usb_device_t dev) { int i; /* XXX: Just check configuration 0 for now. */ for (i = 0; i < dev->config[0].descconf->numif; i++) { struct VasEBoot_usb_desc_if *interf; struct VasEBoot_usb_attach_desc *desc; interf = dev->config[0].interf[i].descif; VasEBoot_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) { VasEBoot_boot_time ("Probing USB device driver class %x", desc->class); if (desc->hook (dev, 0, i)) dev->config[0].interf[i].attached = 1; VasEBoot_boot_time ("Probed USB device driver class %x", desc->class); } if (dev->config[0].interf[i].attached) continue; switch (interf->class) { case VasEBoot_USB_CLASS_MASS_STORAGE: VasEBoot_dl_load ("usbms"); VasEBoot_print_error (); break; case VasEBoot_USB_CLASS_HID: VasEBoot_dl_load ("usb_keyboard"); VasEBoot_print_error (); break; case 0xff: /* FIXME: don't load useless modules. */ VasEBoot_dl_load ("usbserial_ftdi"); VasEBoot_print_error (); VasEBoot_dl_load ("usbserial_pl2303"); VasEBoot_print_error (); VasEBoot_dl_load ("usbserial_usbdebug"); VasEBoot_print_error (); break; } } } /* Helper for VasEBoot_usb_register_attach_hook_class. */ static int VasEBoot_usb_register_attach_hook_class_iter (VasEBoot_usb_device_t usbdev, void *data) { struct VasEBoot_usb_attach_desc *desc = data; struct VasEBoot_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 VasEBoot_usb_desc_if *interf; interf = usbdev->config[0].interf[i].descif; VasEBoot_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 VasEBoot_usb_register_attach_hook_class (struct VasEBoot_usb_attach_desc *desc) { desc->next = attach_hooks; attach_hooks = desc; VasEBoot_usb_iterate (VasEBoot_usb_register_attach_hook_class_iter, desc); } void VasEBoot_usb_unregister_attach_hook_class (struct VasEBoot_usb_attach_desc *desc) { VasEBoot_list_remove (VasEBoot_AS_LIST (desc)); } VasEBoot_MOD_INIT(usb) { VasEBoot_term_poll_usb = VasEBoot_usb_poll_devices; } VasEBoot_MOD_FINI(usb) { VasEBoot_term_poll_usb = NULL; }