blob: abc7e61a59ec1337476dc92c8d60ace15e4be150 [file] [log] [blame]
Gerd Hoffmann806b6022009-08-31 14:23:59 +02001#include "hw.h"
2#include "usb.h"
3#include "qdev.h"
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +02004#include "sysemu.h"
5#include "monitor.h"
6
7static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
Gerd Hoffmannc7a21962010-12-10 11:37:45 +01008
9static char *usb_get_dev_path(DeviceState *dev);
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +010010static char *usb_get_fw_dev_path(DeviceState *qdev);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020011
12static struct BusInfo usb_bus_info = {
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +020013 .name = "USB",
14 .size = sizeof(USBBus),
15 .print_dev = usb_bus_dev_print,
Gerd Hoffmannc7a21962010-12-10 11:37:45 +010016 .get_dev_path = usb_get_dev_path,
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +010017 .get_fw_dev_path = usb_get_fw_dev_path,
Gerd Hoffmann5f690762010-12-10 11:43:35 +010018 .props = (Property[]) {
19 DEFINE_PROP_STRING("port", USBDevice, port_path),
20 DEFINE_PROP_END_OF_LIST()
21 },
Gerd Hoffmann806b6022009-08-31 14:23:59 +020022};
23static int next_usb_bus = 0;
Blue Swirl72cf2d42009-09-12 07:36:22 +000024static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020025
Gerd Hoffmannc1ecb402010-12-10 14:20:46 +010026const 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
Gerd Hoffmannb2317832009-09-16 22:25:29 +020042void usb_bus_new(USBBus *bus, DeviceState *host)
Gerd Hoffmann806b6022009-08-31 14:23:59 +020043{
Gerd Hoffmannb2317832009-09-16 22:25:29 +020044 qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020045 bus->busnr = next_usb_bus++;
Gerd Hoffmannef816d82009-09-25 21:42:42 +020046 bus->qbus.allow_hotplug = 1; /* Yes, we can */
Blue Swirl72cf2d42009-09-12 07:36:22 +000047 QTAILQ_INIT(&bus->free);
48 QTAILQ_INIT(&bus->used);
49 QTAILQ_INSERT_TAIL(&busses, bus, next);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020050}
51
52USBBus *usb_bus_find(int busnr)
53{
54 USBBus *bus;
55
56 if (-1 == busnr)
Blue Swirl72cf2d42009-09-12 07:36:22 +000057 return QTAILQ_FIRST(&busses);
58 QTAILQ_FOREACH(bus, &busses, next) {
Gerd Hoffmann806b6022009-08-31 14:23:59 +020059 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
Markus Armbruster06384692009-12-09 17:07:52 +010071 pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020072 dev->info = info;
Gerd Hoffmann61e094c2009-10-26 15:56:48 +010073 dev->auto_attach = 1;
Gerd Hoffmann132a3f52010-11-26 12:25:32 +010074 QLIST_INIT(&dev->strings);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020075 rc = dev->info->init(dev);
Gerd Hoffmann61e094c2009-10-26 15:56:48 +010076 if (rc == 0 && dev->auto_attach)
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +020077 usb_device_attach(dev);
Gerd Hoffmann806b6022009-08-31 14:23:59 +020078 return rc;
79}
80
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +020081static 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
Gerd Hoffmann806b6022009-08-31 14:23:59 +020092void usb_qdev_register(USBDeviceInfo *info)
93{
94 info->qdev.bus_info = &usb_bus_info;
95 info->qdev.init = usb_qdev_init;
Gerd Hoffmannef816d82009-09-25 21:42:42 +020096 info->qdev.unplug = qdev_simple_unplug_cb;
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +020097 info->qdev.exit = usb_qdev_exit;
Gerd Hoffmann806b6022009-08-31 14:23:59 +020098 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
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200109USBDevice *usb_create(USBBus *bus, const char *name)
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200110{
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);
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200125 return DO_UPCAST(USBDevice, qdev, dev);
126}
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200127
128USBDevice *usb_create_simple(USBBus *bus, const char *name)
129{
130 USBDevice *dev = usb_create(bus, name);
Paul Brookd44168f2010-02-25 13:29:06 +0000131 if (!dev) {
132 hw_error("Failed to create USB device '%s'\n", name);
133 }
Markus Armbrustere23a1b32009-10-07 01:15:58 +0200134 qdev_init_nofail(&dev->qdev);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200135 return dev;
136}
137
138void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
Gerd Hoffmannace13182011-01-12 11:34:50 +0100139 USBPortOps *ops, int speedmask)
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200140{
141 port->opaque = opaque;
142 port->index = index;
Gerd Hoffmann0d86d2b2010-12-01 11:08:44 +0100143 port->opaque = opaque;
144 port->index = index;
145 port->ops = ops;
Gerd Hoffmann843d4e02010-12-03 17:30:13 +0100146 port->speedmask = speedmask;
Blue Swirl72cf2d42009-09-12 07:36:22 +0000147 QTAILQ_INSERT_TAIL(&bus->free, port, next);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200148 bus->nfree++;
149}
150
Gerd Hoffmannc7a21962010-12-10 11:37:45 +0100151void 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
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200161void 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
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200169static 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",
Markus Armbruster0fe6d122009-12-09 17:07:51 +0100176 dev->product_desc);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200177 return;
178 }
Gerd Hoffmann5f690762010-12-10 11:43:35 +0100179 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 }
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200193
Gerd Hoffmann5f690762010-12-10 11:43:35 +0100194 dev->attached++;
Blue Swirl72cf2d42009-09-12 07:36:22 +0000195 QTAILQ_REMOVE(&bus->free, port, next);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200196 bus->nfree--;
197
198 usb_attach(port, dev);
199
Blue Swirl72cf2d42009-09-12 07:36:22 +0000200 QTAILQ_INSERT_TAIL(&bus->used, port, next);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200201 bus->nused++;
202}
203
204int usb_device_attach(USBDevice *dev)
205{
206 USBBus *bus = usb_bus_from_device(dev);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200207
Gerd Hoffmann5f690762010-12-10 11:43:35 +0100208 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). */
Blue Swirld4c4e6f2010-04-25 18:23:04 +0000211 usb_create_simple(bus, "usb-hub");
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200212 }
213 do_attach(dev);
214 return 0;
215}
216
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200217int 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",
Markus Armbruster0fe6d122009-12-09 17:07:51 +0100224 dev->product_desc);
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200225 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
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200245int 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
Blue Swirl72cf2d42009-09-12 07:36:22 +0000255 QTAILQ_FOREACH(port, &bus->used, next) {
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200256 if (port->dev->addr == addr)
257 break;
258 }
259 if (!port)
260 return -1;
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200261 dev = port->dev;
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200262
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200263 qdev_free(&dev->qdev);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200264 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
Gerd Hoffmannc7a21962010-12-10 11:37:45 +0100284 monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
Gerd Hoffmann66a65932009-10-26 15:56:51 +0100285 indent, "", bus->busnr, dev->addr,
Gerd Hoffmannc7a21962010-12-10 11:37:45 +0100286 dev->port ? dev->port->path : "-",
Markus Armbruster0fe6d122009-12-09 17:07:51 +0100287 usb_speed(dev->speed), dev->product_desc,
Gerd Hoffmann66a65932009-10-26 15:56:51 +0100288 dev->attached ? ", attached" : "");
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200289}
290
Gerd Hoffmannc7a21962010-12-10 11:37:45 +0100291static 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
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100297static char *usb_get_fw_dev_path(DeviceState *qdev)
298{
299 USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
300 char *fw_path, *in;
Blue Swirlea87e952011-01-23 08:48:41 +0000301 ssize_t pos = 0, fw_len;
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100302 long nr;
303
Blue Swirlea87e952011-01-23 08:48:41 +0000304 fw_len = 32 + strlen(dev->port->path) * 6;
305 fw_path = qemu_malloc(fw_len);
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100306 in = dev->port->path;
Blue Swirlea87e952011-01-23 08:48:41 +0000307 while (fw_len - pos > 0) {
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100308 nr = strtol(in, &in, 10);
309 if (in[0] == '.') {
310 /* some hub between root port and device */
Blue Swirlea87e952011-01-23 08:48:41 +0000311 pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100312 in++;
313 } else {
314 /* the device itself */
Blue Swirlea87e952011-01-23 08:48:41 +0000315 pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
316 qdev_fw_name(qdev), nr);
Gerd Hoffmann70d31cb2011-01-12 10:58:27 +0100317 break;
318 }
319 }
320 return fw_path;
321}
322
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200323void usb_info(Monitor *mon)
324{
325 USBBus *bus;
326 USBDevice *dev;
327 USBPort *port;
328
Blue Swirl72cf2d42009-09-12 07:36:22 +0000329 if (QTAILQ_EMPTY(&busses)) {
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200330 monitor_printf(mon, "USB support not enabled\n");
331 return;
332 }
333
Blue Swirl72cf2d42009-09-12 07:36:22 +0000334 QTAILQ_FOREACH(bus, &busses, next) {
335 QTAILQ_FOREACH(port, &bus->used, next) {
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200336 dev = port->dev;
337 if (!dev)
338 continue;
Gerd Hoffmannc7a21962010-12-10 11:37:45 +0100339 monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
340 bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
Markus Armbruster0fe6d122009-12-09 17:07:51 +0100341 dev->product_desc);
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200342 }
343 }
344}
345
Gerd Hoffmann0958b4c2009-10-26 15:56:45 +0100346/* handle legacy -usbdevice cmd line option */
347USBDevice *usbdevice_create(const char *cmdline)
348{
349 USBBus *bus = usb_bus_find(-1 /* any */);
350 DeviceInfo *info;
351 USBDeviceInfo *usb;
Jan Kiszka702f3e02010-03-07 12:17:08 +0100352 char driver[32];
353 const char *params;
Gerd Hoffmann0958b4c2009-10-26 15:56:45 +0100354 int len;
355
356 params = strchr(cmdline,':');
357 if (params) {
358 params++;
359 len = params - cmdline;
360 if (len > sizeof(driver))
361 len = sizeof(driver);
362 pstrcpy(driver, len, cmdline);
363 } else {
Jan Kiszka702f3e02010-03-07 12:17:08 +0100364 params = "";
Gerd Hoffmann0958b4c2009-10-26 15:56:45 +0100365 pstrcpy(driver, sizeof(driver), cmdline);
366 }
367
368 for (info = device_info_list; info != NULL; info = info->next) {
369 if (info->bus_info != &usb_bus_info)
370 continue;
371 usb = DO_UPCAST(USBDeviceInfo, qdev, info);
372 if (usb->usbdevice_name == NULL)
373 continue;
374 if (strcmp(usb->usbdevice_name, driver) != 0)
375 continue;
376 break;
377 }
378 if (info == NULL) {
379#if 0
380 /* no error because some drivers are not converted (yet) */
Markus Armbruster1ecda022010-02-18 17:25:24 +0100381 error_report("usbdevice %s not found", driver);
Gerd Hoffmann0958b4c2009-10-26 15:56:45 +0100382#endif
383 return NULL;
384 }
385
386 if (!usb->usbdevice_init) {
TeLeMan98f22dc2010-03-30 09:33:24 +0800387 if (*params) {
Markus Armbruster1ecda022010-02-18 17:25:24 +0100388 error_report("usbdevice %s accepts no params", driver);
Gerd Hoffmann0958b4c2009-10-26 15:56:45 +0100389 return NULL;
390 }
391 return usb_create_simple(bus, usb->qdev.name);
392 }
393 return usb->usbdevice_init(params);
394}