1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddk/debug.h> 6#include <ddk/protocol/usb.h> 7#include <ddk/protocol/usb-bus.h> 8#include <stdlib.h> 9#include <stdio.h> 10#include <string.h> 11 12#include "usb-bus.h" 13#include "usb-device.h" 14 15static zx_status_t bus_add_device(void* ctx, uint32_t device_id, uint32_t hub_id, 16 usb_speed_t speed) { 17 usb_bus_t* bus = ctx; 18 19 if (device_id >= bus->max_device_count) return ZX_ERR_INVALID_ARGS; 20 21 // bus->devices[device_id] must be set before usb_device_add() creates the interface devices 22 // so we pass pointer to it here rather than setting it after usb_device_add() returns. 23 return usb_device_add(bus, device_id, hub_id, speed, &bus->devices[device_id]); 24} 25 26static void bus_remove_device(void* ctx, uint32_t device_id) { 27 usb_bus_t* bus = ctx; 28 if (device_id >= bus->max_device_count) { 29 zxlogf(ERROR, "device_id out of range in usb_bus_remove_device\n"); 30 return; 31 } 32 usb_device_t* device = bus->devices[device_id]; 33 if (device) { 34 device_remove(device->zxdev); 35 bus->devices[device_id] = NULL; 36 } 37} 38 39static void bus_reset_hub_port(void* ctx, uint32_t hub_id, uint32_t port) { 40 usb_bus_t* bus = ctx; 41 if (hub_id >= bus->max_device_count) { 42 zxlogf(ERROR, "hub_id out of range in usb_bus_reset_hub_port\n"); 43 return; 44 } 45 usb_device_t* device = bus->devices[hub_id]; 46 if (!device) { 47 zxlogf(ERROR, "hub not found in usb_bus_reset_hub_port\n"); 48 return; 49 } 50 if (device->hub_intf.ops == NULL) { 51 zxlogf(ERROR, "hub interface not set in usb_bus_reset_hub_port\n"); 52 return; 53 } 54 usb_hub_reset_port(&device->hub_intf, port); 55} 56 57static usb_bus_interface_ops_t _bus_interface = { 58 .add_device = bus_add_device, 59 .remove_device = bus_remove_device, 60 .reset_hub_port = bus_reset_hub_port, 61}; 62 63static zx_status_t bus_get_device_id(zx_device_t* device, uint32_t* out) { 64 usb_protocol_t usb; 65 if (device_get_protocol(device, ZX_PROTOCOL_USB, &usb) != ZX_OK) { 66 return ZX_ERR_INTERNAL; 67 } 68 *out = usb_get_device_id(&usb); 69 return ZX_OK; 70} 71 72static zx_status_t bus_configure_hub(void* ctx, zx_device_t* hub_device, usb_speed_t speed, 73 usb_hub_descriptor_t* descriptor) { 74 usb_bus_t* bus = ctx; 75 uint32_t hub_id; 76 if (bus_get_device_id(hub_device, &hub_id) != ZX_OK) { 77 return ZX_ERR_INTERNAL; 78 } 79 return usb_hci_configure_hub(&bus->hci, hub_id, speed, descriptor); 80} 81 82static zx_status_t bus_device_added(void* ctx, zx_device_t* hub_device, int port, usb_speed_t speed) { 83 usb_bus_t* bus = ctx; 84 uint32_t hub_id; 85 if (bus_get_device_id(hub_device, &hub_id) != ZX_OK) { 86 return ZX_ERR_INTERNAL; 87 } 88 return usb_hci_hub_device_added(&bus->hci, hub_id, port, speed); 89} 90 91static zx_status_t bus_device_removed(void* ctx, zx_device_t* hub_device, int port) { 92 usb_bus_t* bus = ctx; 93 uint32_t hub_id; 94 if (bus_get_device_id(hub_device, &hub_id) != ZX_OK) { 95 return ZX_ERR_INTERNAL; 96 } 97 return usb_hci_hub_device_removed(&bus->hci, hub_id, port); 98} 99 100static zx_status_t bus_set_hub_interface(void* ctx, zx_device_t* usb_device, usb_hub_interface_t* hub) { 101 usb_bus_t* bus = ctx; 102 uint32_t usb_device_id; 103 if (bus_get_device_id(usb_device, &usb_device_id) != ZX_OK) { 104 return ZX_ERR_INTERNAL; 105 } 106 usb_device_t* usb_dev = bus->devices[usb_device_id]; 107 if (!usb_dev) { 108 zxlogf(ERROR, "bus_set_hub_interface: no device for usb_device_id %u\n", usb_device_id); 109 return ZX_ERR_INTERNAL; 110 } 111 usb_device_set_hub_interface(usb_dev, hub); 112 return ZX_OK; 113} 114 115static usb_bus_protocol_ops_t _bus_protocol = { 116 .configure_hub = bus_configure_hub, 117 .hub_device_added = bus_device_added, 118 .hub_device_removed = bus_device_removed, 119 .set_hub_interface = bus_set_hub_interface, 120}; 121 122static void usb_bus_unbind(void* ctx) { 123 zxlogf(INFO, "usb_bus_unbind\n"); 124 usb_bus_t* bus = ctx; 125 usb_hci_set_bus_interface(&bus->hci, NULL); 126 127 for (size_t i = 0; i < bus->max_device_count; i++) { 128 usb_device_t* device = bus->devices[i]; 129 if (device) { 130 device_remove(device->zxdev); 131 bus->devices[i] = NULL; 132 } 133 } 134 device_remove(bus->zxdev); 135} 136 137static void usb_bus_release(void* ctx) { 138 zxlogf(INFO, "usb_bus_release\n"); 139 usb_bus_t* bus = ctx; 140 free(bus->devices); 141 free(bus); 142} 143 144static zx_protocol_device_t usb_bus_device_proto = { 145 .version = DEVICE_OPS_VERSION, 146 .unbind = usb_bus_unbind, 147 .release = usb_bus_release, 148}; 149 150static zx_status_t usb_bus_bind(void* ctx, zx_device_t* device) { 151 usb_bus_t* bus = calloc(1, sizeof(usb_bus_t)); 152 if (!bus) { 153 zxlogf(ERROR, "Not enough memory for usb_bus_t.\n"); 154 return ZX_ERR_NO_MEMORY; 155 } 156 157 if (device_get_protocol(device, ZX_PROTOCOL_USB_HCI, &bus->hci)) { 158 free(bus); 159 return ZX_ERR_NOT_SUPPORTED; 160 } 161 162 zx_status_t status = usb_hci_get_bti(&bus->hci, &bus->bti_handle); 163 if (status != ZX_OK) { 164 free(bus); 165 return status; 166 } 167 168 bus->hci_zxdev = device; 169 bus->max_device_count = usb_hci_get_max_device_count(&bus->hci); 170 bus->devices = calloc(bus->max_device_count, sizeof(usb_device_t *)); 171 if (!bus->devices) { 172 zxlogf(ERROR, "Not enough memory for usb_bus_t->devices. max_device_count: %zu\n", 173 bus->max_device_count); 174 free(bus); 175 return ZX_ERR_NO_MEMORY; 176 } 177 178 device_add_args_t args = { 179 .version = DEVICE_ADD_ARGS_VERSION, 180 .name = "usb", 181 .ctx = bus, 182 .ops = &usb_bus_device_proto, 183 .proto_id = ZX_PROTOCOL_USB_BUS, 184 .proto_ops = &_bus_protocol, 185 .flags = DEVICE_ADD_NON_BINDABLE, 186 }; 187 188 status = device_add(device, &args, &bus->zxdev); 189 if (status == ZX_OK) { 190 static usb_bus_interface_t bus_intf; 191 bus_intf.ops = &_bus_interface; 192 bus_intf.ctx = bus; 193 usb_hci_set_bus_interface(&bus->hci, &bus_intf); 194 } else { 195 free(bus->devices); 196 free(bus); 197 } 198 199 return status; 200} 201 202static zx_driver_ops_t usb_bus_driver_ops = { 203 .version = DRIVER_OPS_VERSION, 204 .bind = usb_bus_bind, 205}; 206 207ZIRCON_DRIVER_BEGIN(usb_bus, usb_bus_driver_ops, "zircon", "0.1", 1) 208 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_USB_HCI), 209ZIRCON_DRIVER_END(usb_bus) 210