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