/* * This file is part of the unicore-mx project. * * Copyright (C) 2016 Kuldeep Singh Dhaka * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include "usbh-private.h" #include /** * procedure to enumerate a device * - prepare the device object * - set address * - get device descriptor (get ep0 max size) */ #define CONTROL_TIMEOUT 500 static void enum_failed(usbh_device *dev) { LOG_LN("failed to enumerate device"); usbh_device_invalidate(dev); } static void enum_success(usbh_device *dev) { usbh_host *host = dev->host; LOG_LN("successfully enumerated device"); if (host->connected != NULL) { host->connected(dev); } } /** * Callback when the partial descriptor is readed from the device. * @param dev USB Device * @param status Transfer status * @param urb_id URB ID (ignored) * @warning @a desc has only first 8bytes valid */ static void got_partial_dev_desc(const usbh_transfer *transfer, usbh_transfer_status status, usbh_urb_id urb_id) { (void) urb_id; usbh_device *dev = transfer->device; if (status != USBH_SUCCESS) { LOG_LN("failed to read partial device descriptor from device"); enum_failed(dev); return; } LOG_LN("success in reading partial device descriptor from device"); struct usb_device_descriptor *desc = transfer->data; LOGF_LN("bLength: %"PRIu8, desc->bLength); LOGF_LN("bDescriptorType: 0x%"PRIx8, desc->bDescriptorType); LOGF_LN("bcdUSB: 0x%"PRIx16, desc->bcdUSB); LOGF_LN("bDeviceClass: 0x%"PRIx8, desc->bDeviceClass); LOGF_LN("bDeviceSubClass: 0x%"PRIx8, desc->bDeviceSubClass); LOGF_LN("bDeviceProtocol: 0x%"PRIx8, desc->bDeviceProtocol); LOGF_LN("bMaxPacketSize0: %"PRIu8, desc->bMaxPacketSize0); dev->ep0_max_packet_size = desc->bMaxPacketSize0; enum_success(dev); } /** * Callback when an address is assigned to the device. * @param dev USB Device * @param status Transfer status * @param urb_id URB ID (ignored) */ static void address_assigned(const usbh_transfer *transfer, usbh_transfer_status status, usbh_urb_id urb_id) { (void) urb_id; usbh_device *dev = transfer->device; if (status != USBH_SUCCESS) { LOG_LN("failed to set address to device"); enum_failed(dev); return; } LOG_LN("succeeded in set address to device"); dev->address = transfer->setup.wValue; LOG_LN("trying to read partial device descriptor from device"); usbh_ctrlreq_read_dev_desc(dev, dev->host->buffer, 8, got_partial_dev_desc); } /** * Allocate the next device address from the host * @param host USB Host * @return valid non-zero 7bit address * @return zero on failure */ static uint8_t alloc_device_address(usbh_host *host) { unsigned i, j; for (i = 0; i < 127; i++) { /* fail after 127 attempts! (address space is full) */ uint8_t addr = host->next_device_address & 0x7F; addr = (!addr) ? 1 : addr; host->next_device_address = (addr == 0x7F) ? 1 : (addr + 1); for (j = 0; j < DEVICE_ARRAY_LENGTH; j++) { usbh_device *dev = &host->devices[j]; if (IS_DEVICE_VALID(dev) && dev->address == addr) { /* address is being used! */ addr = 0; break; } } if (addr) { /* tada! we found a unused address */ LOGF_LN("address 0x%"PRIx8" is free for use", addr); return addr; } } LOG_LN("tried all possibilities, no valid address found"); return 0; } /** * Place a SET_ADDRESS request for @a dev * @param dev USB Device */ static void set_address(usbh_device *dev) { uint8_t addr = alloc_device_address(dev->host); if (!addr) { LOG_LN("no device address to assign for SET_ADDRESS"); enum_failed(dev); return; } LOGF_LN("trying to set address (0x%"PRIx8") to device", addr); usbh_ctrlreq_set_address(dev, addr, address_assigned); } /** * Start the device enumeration. * @param dev USB Device */ void usbh_device_enum_start(usbh_device *dev) { LOG_CALL set_address(dev); }