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