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