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