]> Git Repo - qemu.git/blob - hw/usb-bus.c
usb-bus: Don't allow attaching a device to a bus with no free ports
[qemu.git] / hw / usb-bus.c
1 #include "hw.h"
2 #include "usb.h"
3 #include "qdev.h"
4 #include "sysemu.h"
5 #include "monitor.h"
6
7 static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
8
9 static char *usb_get_dev_path(DeviceState *dev);
10 static char *usb_get_fw_dev_path(DeviceState *qdev);
11
12 static struct BusInfo usb_bus_info = {
13     .name      = "USB",
14     .size      = sizeof(USBBus),
15     .print_dev = usb_bus_dev_print,
16     .get_dev_path = usb_get_dev_path,
17     .get_fw_dev_path = usb_get_fw_dev_path,
18     .props      = (Property[]) {
19         DEFINE_PROP_STRING("port", USBDevice, port_path),
20         DEFINE_PROP_END_OF_LIST()
21     },
22 };
23 static int next_usb_bus = 0;
24 static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
25
26 const VMStateDescription vmstate_usb_device = {
27     .name = "USBDevice",
28     .version_id = 1,
29     .minimum_version_id = 1,
30     .fields = (VMStateField []) {
31         VMSTATE_UINT8(addr, USBDevice),
32         VMSTATE_INT32(state, USBDevice),
33         VMSTATE_INT32(remote_wakeup, USBDevice),
34         VMSTATE_INT32(setup_state, USBDevice),
35         VMSTATE_INT32(setup_len, USBDevice),
36         VMSTATE_INT32(setup_index, USBDevice),
37         VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
38         VMSTATE_END_OF_LIST(),
39     }
40 };
41
42 void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
43 {
44     qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
45     bus->ops = ops;
46     bus->busnr = next_usb_bus++;
47     bus->qbus.allow_hotplug = 1; /* Yes, we can */
48     QTAILQ_INIT(&bus->free);
49     QTAILQ_INIT(&bus->used);
50     QTAILQ_INSERT_TAIL(&busses, bus, next);
51 }
52
53 USBBus *usb_bus_find(int busnr)
54 {
55     USBBus *bus;
56
57     if (-1 == busnr)
58         return QTAILQ_FIRST(&busses);
59     QTAILQ_FOREACH(bus, &busses, next) {
60         if (bus->busnr == busnr)
61             return bus;
62     }
63     return NULL;
64 }
65
66 static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
67 {
68     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
69     USBDeviceInfo *info = DO_UPCAST(USBDeviceInfo, qdev, base);
70     int rc;
71
72     pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
73     dev->info = info;
74     dev->auto_attach = 1;
75     QLIST_INIT(&dev->strings);
76     rc = dev->info->init(dev);
77     if (rc == 0 && dev->auto_attach)
78         usb_device_attach(dev);
79     return rc;
80 }
81
82 static int usb_qdev_exit(DeviceState *qdev)
83 {
84     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
85     USBBus *bus = usb_bus_from_device(dev);
86
87     if (dev->attached) {
88         usb_device_detach(dev);
89     }
90     bus->ops->device_destroy(bus, dev);
91     if (dev->info->handle_destroy) {
92         dev->info->handle_destroy(dev);
93     }
94     return 0;
95 }
96
97 void usb_qdev_register(USBDeviceInfo *info)
98 {
99     info->qdev.bus_info = &usb_bus_info;
100     info->qdev.init     = usb_qdev_init;
101     info->qdev.unplug   = qdev_simple_unplug_cb;
102     info->qdev.exit     = usb_qdev_exit;
103     qdev_register(&info->qdev);
104 }
105
106 void usb_qdev_register_many(USBDeviceInfo *info)
107 {
108     while (info->qdev.name) {
109         usb_qdev_register(info);
110         info++;
111     }
112 }
113
114 USBDevice *usb_create(USBBus *bus, const char *name)
115 {
116     DeviceState *dev;
117
118 #if 1
119     /* temporary stopgap until all usb is properly qdev-ified */
120     if (!bus) {
121         bus = usb_bus_find(-1);
122         if (!bus)
123             return NULL;
124         fprintf(stderr, "%s: no bus specified, using \"%s\" for \"%s\"\n",
125                 __FUNCTION__, bus->qbus.name, name);
126     }
127 #endif
128
129     dev = qdev_create(&bus->qbus, name);
130     return DO_UPCAST(USBDevice, qdev, dev);
131 }
132
133 USBDevice *usb_create_simple(USBBus *bus, const char *name)
134 {
135     USBDevice *dev = usb_create(bus, name);
136     if (!dev) {
137         hw_error("Failed to create USB device '%s'\n", name);
138     }
139     qdev_init_nofail(&dev->qdev);
140     return dev;
141 }
142
143 void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
144                        USBPortOps *ops, int speedmask)
145 {
146     port->opaque = opaque;
147     port->index = index;
148     port->opaque = opaque;
149     port->index = index;
150     port->ops = ops;
151     port->speedmask = speedmask;
152     QTAILQ_INSERT_TAIL(&bus->free, port, next);
153     bus->nfree++;
154 }
155
156 void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
157 {
158     if (upstream) {
159         snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
160                  upstream->path, portnr);
161     } else {
162         snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
163     }
164 }
165
166 void usb_unregister_port(USBBus *bus, USBPort *port)
167 {
168     if (port->dev)
169         qdev_free(&port->dev->qdev);
170     QTAILQ_REMOVE(&bus->free, port, next);
171     bus->nfree--;
172 }
173
174 static void do_attach(USBDevice *dev)
175 {
176     USBBus *bus = usb_bus_from_device(dev);
177     USBPort *port;
178
179     if (dev->attached) {
180         fprintf(stderr, "Warning: tried to attach usb device %s twice\n",
181                 dev->product_desc);
182         return;
183     }
184     if (bus->nfree == 0) {
185         fprintf(stderr, "Warning: tried to attach usb device %s to a bus with no free ports\n",
186                 dev->product_desc);
187         return;
188     }
189     if (dev->port_path) {
190         QTAILQ_FOREACH(port, &bus->free, next) {
191             if (strcmp(port->path, dev->port_path) == 0) {
192                 break;
193             }
194         }
195         if (port == NULL) {
196             fprintf(stderr, "Warning: usb port %s (bus %s) not found\n",
197                     dev->port_path, bus->qbus.name);
198             return;
199         }
200     } else {
201         port = QTAILQ_FIRST(&bus->free);
202     }
203
204     dev->attached++;
205     QTAILQ_REMOVE(&bus->free, port, next);
206     bus->nfree--;
207
208     usb_attach(port, dev);
209
210     QTAILQ_INSERT_TAIL(&bus->used, port, next);
211     bus->nused++;
212 }
213
214 int usb_device_attach(USBDevice *dev)
215 {
216     USBBus *bus = usb_bus_from_device(dev);
217
218     if (bus->nfree == 1 && dev->port_path == NULL) {
219         /* Create a new hub and chain it on
220            (unless a physical port location is specified). */
221         usb_create_simple(bus, "usb-hub");
222     }
223     do_attach(dev);
224     return 0;
225 }
226
227 int usb_device_detach(USBDevice *dev)
228 {
229     USBBus *bus = usb_bus_from_device(dev);
230     USBPort *port;
231
232     if (!dev->attached) {
233         fprintf(stderr, "Warning: tried to detach unattached usb device %s\n",
234                 dev->product_desc);
235         return -1;
236     }
237     dev->attached--;
238
239     QTAILQ_FOREACH(port, &bus->used, next) {
240         if (port->dev == dev)
241             break;
242     }
243     assert(port != NULL);
244
245     QTAILQ_REMOVE(&bus->used, port, next);
246     bus->nused--;
247
248     usb_attach(port, NULL);
249
250     QTAILQ_INSERT_TAIL(&bus->free, port, next);
251     bus->nfree++;
252     return 0;
253 }
254
255 int usb_device_delete_addr(int busnr, int addr)
256 {
257     USBBus *bus;
258     USBPort *port;
259     USBDevice *dev;
260
261     bus = usb_bus_find(busnr);
262     if (!bus)
263         return -1;
264
265     QTAILQ_FOREACH(port, &bus->used, next) {
266         if (port->dev->addr == addr)
267             break;
268     }
269     if (!port)
270         return -1;
271     dev = port->dev;
272
273     qdev_free(&dev->qdev);
274     return 0;
275 }
276
277 static const char *usb_speed(unsigned int speed)
278 {
279     static const char *txt[] = {
280         [ USB_SPEED_LOW  ] = "1.5",
281         [ USB_SPEED_FULL ] = "12",
282         [ USB_SPEED_HIGH ] = "480",
283         [ USB_SPEED_SUPER ] = "5000",
284     };
285     if (speed >= ARRAY_SIZE(txt))
286         return "?";
287     return txt[speed];
288 }
289
290 static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
291 {
292     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
293     USBBus *bus = usb_bus_from_device(dev);
294
295     monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
296                    indent, "", bus->busnr, dev->addr,
297                    dev->port ? dev->port->path : "-",
298                    usb_speed(dev->speed), dev->product_desc,
299                    dev->attached ? ", attached" : "");
300 }
301
302 static char *usb_get_dev_path(DeviceState *qdev)
303 {
304     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
305     return qemu_strdup(dev->port->path);
306 }
307
308 static char *usb_get_fw_dev_path(DeviceState *qdev)
309 {
310     USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
311     char *fw_path, *in;
312     ssize_t pos = 0, fw_len;
313     long nr;
314
315     fw_len = 32 + strlen(dev->port->path) * 6;
316     fw_path = qemu_malloc(fw_len);
317     in = dev->port->path;
318     while (fw_len - pos > 0) {
319         nr = strtol(in, &in, 10);
320         if (in[0] == '.') {
321             /* some hub between root port and device */
322             pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
323             in++;
324         } else {
325             /* the device itself */
326             pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
327                             qdev_fw_name(qdev), nr);
328             break;
329         }
330     }
331     return fw_path;
332 }
333
334 void usb_info(Monitor *mon)
335 {
336     USBBus *bus;
337     USBDevice *dev;
338     USBPort *port;
339
340     if (QTAILQ_EMPTY(&busses)) {
341         monitor_printf(mon, "USB support not enabled\n");
342         return;
343     }
344
345     QTAILQ_FOREACH(bus, &busses, next) {
346         QTAILQ_FOREACH(port, &bus->used, next) {
347             dev = port->dev;
348             if (!dev)
349                 continue;
350             monitor_printf(mon, "  Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
351                            bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
352                            dev->product_desc);
353         }
354     }
355 }
356
357 /* handle legacy -usbdevice cmd line option */
358 USBDevice *usbdevice_create(const char *cmdline)
359 {
360     USBBus *bus = usb_bus_find(-1 /* any */);
361     DeviceInfo *info;
362     USBDeviceInfo *usb;
363     char driver[32];
364     const char *params;
365     int len;
366
367     params = strchr(cmdline,':');
368     if (params) {
369         params++;
370         len = params - cmdline;
371         if (len > sizeof(driver))
372             len = sizeof(driver);
373         pstrcpy(driver, len, cmdline);
374     } else {
375         params = "";
376         pstrcpy(driver, sizeof(driver), cmdline);
377     }
378
379     for (info = device_info_list; info != NULL; info = info->next) {
380         if (info->bus_info != &usb_bus_info)
381             continue;
382         usb = DO_UPCAST(USBDeviceInfo, qdev, info);
383         if (usb->usbdevice_name == NULL)
384             continue;
385         if (strcmp(usb->usbdevice_name, driver) != 0)
386             continue;
387         break;
388     }
389     if (info == NULL) {
390 #if 0
391         /* no error because some drivers are not converted (yet) */
392         error_report("usbdevice %s not found", driver);
393 #endif
394         return NULL;
395     }
396
397     if (!usb->usbdevice_init) {
398         if (*params) {
399             error_report("usbdevice %s accepts no params", driver);
400             return NULL;
401         }
402         return usb_create_simple(bus, usb->qdev.name);
403     }
404     return usb->usbdevice_init(params);
405 }
This page took 0.04881 seconds and 4 git commands to generate.