]> git.proxmox.com Git - qemu.git/blame - hw/usb-bus.c
usb: create USBPortOps, move attach there.
[qemu.git] / hw / usb-bus.c
CommitLineData
806b6024
GH
1#include "hw.h"
2#include "usb.h"
3#include "qdev.h"
a5d2f727
GH
4#include "sysemu.h"
5#include "monitor.h"
6
7static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
cdedd006 8static char *usbbus_get_fw_dev_path(DeviceState *dev);
806b6024
GH
9
10static struct BusInfo usb_bus_info = {
a5d2f727
GH
11 .name = "USB",
12 .size = sizeof(USBBus),
13 .print_dev = usb_bus_dev_print,
cdedd006 14 .get_fw_dev_path = usbbus_get_fw_dev_path,
806b6024
GH
15};
16static int next_usb_bus = 0;
72cf2d4f 17static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
806b6024 18
b2317837 19void usb_bus_new(USBBus *bus, DeviceState *host)
806b6024 20{
b2317837 21 qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
806b6024 22 bus->busnr = next_usb_bus++;
ef816d83 23 bus->qbus.allow_hotplug = 1; /* Yes, we can */
72cf2d4f
BS
24 QTAILQ_INIT(&bus->free);
25 QTAILQ_INIT(&bus->used);
26 QTAILQ_INSERT_TAIL(&busses, bus, next);
806b6024
GH
27}
28
29USBBus *usb_bus_find(int busnr)
30{
31 USBBus *bus;
32
33 if (-1 == busnr)
72cf2d4f
BS
34 return QTAILQ_FIRST(&busses);
35 QTAILQ_FOREACH(bus, &busses, next) {
806b6024
GH
36 if (bus->busnr == busnr)
37 return bus;
38 }
39 return NULL;
40}
41
42static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
43{
44 USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
45 USBDeviceInfo *info = DO_UPCAST(USBDeviceInfo, qdev, base);
46 int rc;
47
06384698 48 pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
806b6024 49 dev->info = info;
61e094c0 50 dev->auto_attach = 1;
132a3f55 51 QLIST_INIT(&dev->strings);
806b6024 52 rc = dev->info->init(dev);
61e094c0 53 if (rc == 0 && dev->auto_attach)
a5d2f727 54 usb_device_attach(dev);
806b6024
GH
55 return rc;
56}
57
a8e662b5
GH
58static int usb_qdev_exit(DeviceState *qdev)
59{
60 USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
61
62 usb_device_detach(dev);
63 if (dev->info->handle_destroy) {
64 dev->info->handle_destroy(dev);
65 }
66 return 0;
67}
68
806b6024
GH
69void usb_qdev_register(USBDeviceInfo *info)
70{
71 info->qdev.bus_info = &usb_bus_info;
72 info->qdev.init = usb_qdev_init;
ef816d83 73 info->qdev.unplug = qdev_simple_unplug_cb;
a8e662b5 74 info->qdev.exit = usb_qdev_exit;
806b6024
GH
75 qdev_register(&info->qdev);
76}
77
78void usb_qdev_register_many(USBDeviceInfo *info)
79{
80 while (info->qdev.name) {
81 usb_qdev_register(info);
82 info++;
83 }
84}
85
a5d2f727 86USBDevice *usb_create(USBBus *bus, const char *name)
806b6024
GH
87{
88 DeviceState *dev;
89
90#if 1
91 /* temporary stopgap until all usb is properly qdev-ified */
92 if (!bus) {
93 bus = usb_bus_find(-1);
94 if (!bus)
95 return NULL;
96 fprintf(stderr, "%s: no bus specified, using \"%s\" for \"%s\"\n",
97 __FUNCTION__, bus->qbus.name, name);
98 }
99#endif
100
101 dev = qdev_create(&bus->qbus, name);
806b6024
GH
102 return DO_UPCAST(USBDevice, qdev, dev);
103}
a5d2f727
GH
104
105USBDevice *usb_create_simple(USBBus *bus, const char *name)
106{
107 USBDevice *dev = usb_create(bus, name);
d44168ff
PB
108 if (!dev) {
109 hw_error("Failed to create USB device '%s'\n", name);
110 }
e23a1b33 111 qdev_init_nofail(&dev->qdev);
a5d2f727
GH
112 return dev;
113}
114
115void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
0d86d2be 116 USBDevice *pdev, USBPortOps *ops)
a5d2f727
GH
117{
118 port->opaque = opaque;
119 port->index = index;
ab28ccc0 120 port->pdev = pdev;
0d86d2be
GH
121 port->opaque = opaque;
122 port->index = index;
123 port->ops = ops;
72cf2d4f 124 QTAILQ_INSERT_TAIL(&bus->free, port, next);
a5d2f727
GH
125 bus->nfree++;
126}
127
a8e662b5
GH
128void usb_unregister_port(USBBus *bus, USBPort *port)
129{
130 if (port->dev)
131 qdev_free(&port->dev->qdev);
132 QTAILQ_REMOVE(&bus->free, port, next);
133 bus->nfree--;
134}
135
a5d2f727
GH
136static void do_attach(USBDevice *dev)
137{
138 USBBus *bus = usb_bus_from_device(dev);
139 USBPort *port;
140
141 if (dev->attached) {
142 fprintf(stderr, "Warning: tried to attach usb device %s twice\n",
0fe6d12e 143 dev->product_desc);
a5d2f727
GH
144 return;
145 }
146 dev->attached++;
147
72cf2d4f
BS
148 port = QTAILQ_FIRST(&bus->free);
149 QTAILQ_REMOVE(&bus->free, port, next);
a5d2f727
GH
150 bus->nfree--;
151
152 usb_attach(port, dev);
153
72cf2d4f 154 QTAILQ_INSERT_TAIL(&bus->used, port, next);
a5d2f727
GH
155 bus->nused++;
156}
157
158int usb_device_attach(USBDevice *dev)
159{
160 USBBus *bus = usb_bus_from_device(dev);
a5d2f727
GH
161
162 if (bus->nfree == 1) {
163 /* Create a new hub and chain it on. */
d4c4e6fd 164 usb_create_simple(bus, "usb-hub");
a5d2f727
GH
165 }
166 do_attach(dev);
167 return 0;
168}
169
a8e662b5
GH
170int usb_device_detach(USBDevice *dev)
171{
172 USBBus *bus = usb_bus_from_device(dev);
173 USBPort *port;
174
175 if (!dev->attached) {
176 fprintf(stderr, "Warning: tried to detach unattached usb device %s\n",
0fe6d12e 177 dev->product_desc);
a8e662b5
GH
178 return -1;
179 }
180 dev->attached--;
181
182 QTAILQ_FOREACH(port, &bus->used, next) {
183 if (port->dev == dev)
184 break;
185 }
186 assert(port != NULL);
187
188 QTAILQ_REMOVE(&bus->used, port, next);
189 bus->nused--;
190
191 usb_attach(port, NULL);
192
193 QTAILQ_INSERT_TAIL(&bus->free, port, next);
194 bus->nfree++;
195 return 0;
196}
197
a5d2f727
GH
198int usb_device_delete_addr(int busnr, int addr)
199{
200 USBBus *bus;
201 USBPort *port;
202 USBDevice *dev;
203
204 bus = usb_bus_find(busnr);
205 if (!bus)
206 return -1;
207
72cf2d4f 208 QTAILQ_FOREACH(port, &bus->used, next) {
a5d2f727
GH
209 if (port->dev->addr == addr)
210 break;
211 }
212 if (!port)
213 return -1;
a5d2f727 214 dev = port->dev;
a5d2f727 215
a8e662b5 216 qdev_free(&dev->qdev);
a5d2f727
GH
217 return 0;
218}
219
220static const char *usb_speed(unsigned int speed)
221{
222 static const char *txt[] = {
223 [ USB_SPEED_LOW ] = "1.5",
224 [ USB_SPEED_FULL ] = "12",
225 [ USB_SPEED_HIGH ] = "480",
226 };
227 if (speed >= ARRAY_SIZE(txt))
228 return "?";
229 return txt[speed];
230}
231
232static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
233{
234 USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
235 USBBus *bus = usb_bus_from_device(dev);
236
66a6593a
GH
237 monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s%s\n",
238 indent, "", bus->busnr, dev->addr,
0fe6d12e 239 usb_speed(dev->speed), dev->product_desc,
66a6593a 240 dev->attached ? ", attached" : "");
a5d2f727
GH
241}
242
243void usb_info(Monitor *mon)
244{
245 USBBus *bus;
246 USBDevice *dev;
247 USBPort *port;
248
72cf2d4f 249 if (QTAILQ_EMPTY(&busses)) {
a5d2f727
GH
250 monitor_printf(mon, "USB support not enabled\n");
251 return;
252 }
253
72cf2d4f
BS
254 QTAILQ_FOREACH(bus, &busses, next) {
255 QTAILQ_FOREACH(port, &bus->used, next) {
a5d2f727
GH
256 dev = port->dev;
257 if (!dev)
258 continue;
259 monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n",
0fe6d12e
MA
260 bus->busnr, dev->addr, usb_speed(dev->speed),
261 dev->product_desc);
a5d2f727
GH
262 }
263 }
264}
265
0958b4cc
GH
266/* handle legacy -usbdevice cmd line option */
267USBDevice *usbdevice_create(const char *cmdline)
268{
269 USBBus *bus = usb_bus_find(-1 /* any */);
270 DeviceInfo *info;
271 USBDeviceInfo *usb;
702f3e0f
JK
272 char driver[32];
273 const char *params;
0958b4cc
GH
274 int len;
275
276 params = strchr(cmdline,':');
277 if (params) {
278 params++;
279 len = params - cmdline;
280 if (len > sizeof(driver))
281 len = sizeof(driver);
282 pstrcpy(driver, len, cmdline);
283 } else {
702f3e0f 284 params = "";
0958b4cc
GH
285 pstrcpy(driver, sizeof(driver), cmdline);
286 }
287
288 for (info = device_info_list; info != NULL; info = info->next) {
289 if (info->bus_info != &usb_bus_info)
290 continue;
291 usb = DO_UPCAST(USBDeviceInfo, qdev, info);
292 if (usb->usbdevice_name == NULL)
293 continue;
294 if (strcmp(usb->usbdevice_name, driver) != 0)
295 continue;
296 break;
297 }
298 if (info == NULL) {
299#if 0
300 /* no error because some drivers are not converted (yet) */
1ecda02b 301 error_report("usbdevice %s not found", driver);
0958b4cc
GH
302#endif
303 return NULL;
304 }
305
306 if (!usb->usbdevice_init) {
98f22dc1 307 if (*params) {
1ecda02b 308 error_report("usbdevice %s accepts no params", driver);
0958b4cc
GH
309 return NULL;
310 }
311 return usb_create_simple(bus, usb->qdev.name);
312 }
313 return usb->usbdevice_init(params);
314}
cdedd006
GN
315
316static int usbbus_get_fw_dev_path_helper(USBDevice *d, USBBus *bus, char *p,
317 int len)
318{
319 int l = 0;
320 USBPort *port;
321
322 QTAILQ_FOREACH(port, &bus->used, next) {
323 if (port->dev == d) {
324 if (port->pdev) {
325 l = usbbus_get_fw_dev_path_helper(port->pdev, bus, p, len);
326 }
327 l += snprintf(p + l, len - l, "%s@%x/", qdev_fw_name(&d->qdev),
328 port->index);
329 break;
330 }
331 }
332
333 return l;
334}
335
336static char *usbbus_get_fw_dev_path(DeviceState *dev)
337{
338 USBDevice *d = (USBDevice*)dev;
339 USBBus *bus = usb_bus_from_device(d);
340 char path[100];
341 int l;
342
343 assert(d->attached != 0);
344
345 l = usbbus_get_fw_dev_path_helper(d, bus, path, sizeof(path));
346
347 if (l == 0) {
348 abort();
349 }
350
351 path[l-1] = '\0';
352
353 return strdup(path);
354}