root / hw / usb-bus.c @ 93148aa5
History | View | Annotate | Download (14.7 kB)
1 | 806b6024 | Gerd Hoffmann | #include "hw.h" |
---|---|---|---|
2 | 806b6024 | Gerd Hoffmann | #include "usb.h" |
3 | 806b6024 | Gerd Hoffmann | #include "qdev.h" |
4 | a5d2f727 | Gerd Hoffmann | #include "sysemu.h" |
5 | a5d2f727 | Gerd Hoffmann | #include "monitor.h" |
6 | 891fb2cd | Gerd Hoffmann | #include "trace.h" |
7 | a5d2f727 | Gerd Hoffmann | |
8 | a5d2f727 | Gerd Hoffmann | static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
9 | c7a2196a | Gerd Hoffmann | |
10 | c7a2196a | Gerd Hoffmann | static char *usb_get_dev_path(DeviceState *dev); |
11 | 70d31cb2 | Gerd Hoffmann | static char *usb_get_fw_dev_path(DeviceState *qdev); |
12 | f462141f | Gerd Hoffmann | static int usb_qdev_exit(DeviceState *qdev); |
13 | 806b6024 | Gerd Hoffmann | |
14 | 806b6024 | Gerd Hoffmann | static struct BusInfo usb_bus_info = { |
15 | a5d2f727 | Gerd Hoffmann | .name = "USB",
|
16 | a5d2f727 | Gerd Hoffmann | .size = sizeof(USBBus),
|
17 | a5d2f727 | Gerd Hoffmann | .print_dev = usb_bus_dev_print, |
18 | c7a2196a | Gerd Hoffmann | .get_dev_path = usb_get_dev_path, |
19 | 70d31cb2 | Gerd Hoffmann | .get_fw_dev_path = usb_get_fw_dev_path, |
20 | 5f69076b | Gerd Hoffmann | .props = (Property[]) { |
21 | 5f69076b | Gerd Hoffmann | DEFINE_PROP_STRING("port", USBDevice, port_path),
|
22 | 5f69076b | Gerd Hoffmann | DEFINE_PROP_END_OF_LIST() |
23 | 5f69076b | Gerd Hoffmann | }, |
24 | 806b6024 | Gerd Hoffmann | }; |
25 | 806b6024 | Gerd Hoffmann | static int next_usb_bus = 0; |
26 | 72cf2d4f | Blue Swirl | static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
|
27 | 806b6024 | Gerd Hoffmann | |
28 | c1ecb40a | Gerd Hoffmann | const VMStateDescription vmstate_usb_device = {
|
29 | c1ecb40a | Gerd Hoffmann | .name = "USBDevice",
|
30 | c1ecb40a | Gerd Hoffmann | .version_id = 1,
|
31 | c1ecb40a | Gerd Hoffmann | .minimum_version_id = 1,
|
32 | c1ecb40a | Gerd Hoffmann | .fields = (VMStateField []) { |
33 | c1ecb40a | Gerd Hoffmann | VMSTATE_UINT8(addr, USBDevice), |
34 | c1ecb40a | Gerd Hoffmann | VMSTATE_INT32(state, USBDevice), |
35 | c1ecb40a | Gerd Hoffmann | VMSTATE_INT32(remote_wakeup, USBDevice), |
36 | c1ecb40a | Gerd Hoffmann | VMSTATE_INT32(setup_state, USBDevice), |
37 | c1ecb40a | Gerd Hoffmann | VMSTATE_INT32(setup_len, USBDevice), |
38 | c1ecb40a | Gerd Hoffmann | VMSTATE_INT32(setup_index, USBDevice), |
39 | c1ecb40a | Gerd Hoffmann | VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
|
40 | c1ecb40a | Gerd Hoffmann | VMSTATE_END_OF_LIST(), |
41 | c1ecb40a | Gerd Hoffmann | } |
42 | c1ecb40a | Gerd Hoffmann | }; |
43 | c1ecb40a | Gerd Hoffmann | |
44 | 07771f6f | Gerd Hoffmann | void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host)
|
45 | 806b6024 | Gerd Hoffmann | { |
46 | b2317837 | Gerd Hoffmann | qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
|
47 | 07771f6f | Gerd Hoffmann | bus->ops = ops; |
48 | 806b6024 | Gerd Hoffmann | bus->busnr = next_usb_bus++; |
49 | ef816d83 | Gerd Hoffmann | bus->qbus.allow_hotplug = 1; /* Yes, we can */ |
50 | 72cf2d4f | Blue Swirl | QTAILQ_INIT(&bus->free); |
51 | 72cf2d4f | Blue Swirl | QTAILQ_INIT(&bus->used); |
52 | 72cf2d4f | Blue Swirl | QTAILQ_INSERT_TAIL(&busses, bus, next); |
53 | 806b6024 | Gerd Hoffmann | } |
54 | 806b6024 | Gerd Hoffmann | |
55 | 806b6024 | Gerd Hoffmann | USBBus *usb_bus_find(int busnr)
|
56 | 806b6024 | Gerd Hoffmann | { |
57 | 806b6024 | Gerd Hoffmann | USBBus *bus; |
58 | 806b6024 | Gerd Hoffmann | |
59 | 806b6024 | Gerd Hoffmann | if (-1 == busnr) |
60 | 72cf2d4f | Blue Swirl | return QTAILQ_FIRST(&busses);
|
61 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(bus, &busses, next) { |
62 | 806b6024 | Gerd Hoffmann | if (bus->busnr == busnr)
|
63 | 806b6024 | Gerd Hoffmann | return bus;
|
64 | 806b6024 | Gerd Hoffmann | } |
65 | 806b6024 | Gerd Hoffmann | return NULL; |
66 | 806b6024 | Gerd Hoffmann | } |
67 | 806b6024 | Gerd Hoffmann | |
68 | 62aed765 | Anthony Liguori | static int usb_device_init(USBDevice *dev) |
69 | 62aed765 | Anthony Liguori | { |
70 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
71 | 62aed765 | Anthony Liguori | if (klass->init) {
|
72 | 62aed765 | Anthony Liguori | return klass->init(dev);
|
73 | 62aed765 | Anthony Liguori | } |
74 | 62aed765 | Anthony Liguori | return 0; |
75 | 62aed765 | Anthony Liguori | } |
76 | 62aed765 | Anthony Liguori | |
77 | 73796fe6 | Gerd Hoffmann | USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr) |
78 | 62aed765 | Anthony Liguori | { |
79 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
80 | 73796fe6 | Gerd Hoffmann | if (klass->find_device) {
|
81 | 73796fe6 | Gerd Hoffmann | return klass->find_device(dev, addr);
|
82 | 62aed765 | Anthony Liguori | } |
83 | 73796fe6 | Gerd Hoffmann | return NULL; |
84 | 62aed765 | Anthony Liguori | } |
85 | 62aed765 | Anthony Liguori | |
86 | 62aed765 | Anthony Liguori | static void usb_device_handle_destroy(USBDevice *dev) |
87 | 62aed765 | Anthony Liguori | { |
88 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
89 | 62aed765 | Anthony Liguori | if (klass->handle_destroy) {
|
90 | 62aed765 | Anthony Liguori | klass->handle_destroy(dev); |
91 | 62aed765 | Anthony Liguori | } |
92 | 62aed765 | Anthony Liguori | } |
93 | 62aed765 | Anthony Liguori | |
94 | 62aed765 | Anthony Liguori | void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
|
95 | 62aed765 | Anthony Liguori | { |
96 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
97 | 62aed765 | Anthony Liguori | if (klass->cancel_packet) {
|
98 | 62aed765 | Anthony Liguori | klass->cancel_packet(dev, p); |
99 | 62aed765 | Anthony Liguori | } |
100 | 62aed765 | Anthony Liguori | } |
101 | 62aed765 | Anthony Liguori | |
102 | 62aed765 | Anthony Liguori | void usb_device_handle_attach(USBDevice *dev)
|
103 | 62aed765 | Anthony Liguori | { |
104 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
105 | 62aed765 | Anthony Liguori | if (klass->handle_attach) {
|
106 | 62aed765 | Anthony Liguori | klass->handle_attach(dev); |
107 | 62aed765 | Anthony Liguori | } |
108 | 62aed765 | Anthony Liguori | } |
109 | 62aed765 | Anthony Liguori | |
110 | 62aed765 | Anthony Liguori | void usb_device_handle_reset(USBDevice *dev)
|
111 | 62aed765 | Anthony Liguori | { |
112 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
113 | 62aed765 | Anthony Liguori | if (klass->handle_reset) {
|
114 | 62aed765 | Anthony Liguori | klass->handle_reset(dev); |
115 | 62aed765 | Anthony Liguori | } |
116 | 62aed765 | Anthony Liguori | } |
117 | 62aed765 | Anthony Liguori | |
118 | 62aed765 | Anthony Liguori | int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, |
119 | 62aed765 | Anthony Liguori | int value, int index, int length, uint8_t *data) |
120 | 62aed765 | Anthony Liguori | { |
121 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
122 | 62aed765 | Anthony Liguori | if (klass->handle_control) {
|
123 | 62aed765 | Anthony Liguori | return klass->handle_control(dev, p, request, value, index, length,
|
124 | 62aed765 | Anthony Liguori | data); |
125 | 62aed765 | Anthony Liguori | } |
126 | 62aed765 | Anthony Liguori | return -ENOSYS;
|
127 | 62aed765 | Anthony Liguori | } |
128 | 62aed765 | Anthony Liguori | |
129 | 62aed765 | Anthony Liguori | int usb_device_handle_data(USBDevice *dev, USBPacket *p)
|
130 | 62aed765 | Anthony Liguori | { |
131 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
132 | 62aed765 | Anthony Liguori | if (klass->handle_data) {
|
133 | 62aed765 | Anthony Liguori | return klass->handle_data(dev, p);
|
134 | 62aed765 | Anthony Liguori | } |
135 | 62aed765 | Anthony Liguori | return -ENOSYS;
|
136 | 62aed765 | Anthony Liguori | } |
137 | 62aed765 | Anthony Liguori | |
138 | 62aed765 | Anthony Liguori | const char *usb_device_get_product_desc(USBDevice *dev) |
139 | 62aed765 | Anthony Liguori | { |
140 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
141 | 62aed765 | Anthony Liguori | return klass->product_desc;
|
142 | 62aed765 | Anthony Liguori | } |
143 | 62aed765 | Anthony Liguori | |
144 | 62aed765 | Anthony Liguori | const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
|
145 | 62aed765 | Anthony Liguori | { |
146 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
147 | 62aed765 | Anthony Liguori | return klass->usb_desc;
|
148 | 62aed765 | Anthony Liguori | } |
149 | 62aed765 | Anthony Liguori | |
150 | 62aed765 | Anthony Liguori | void usb_device_set_interface(USBDevice *dev, int interface, |
151 | 62aed765 | Anthony Liguori | int alt_old, int alt_new) |
152 | 62aed765 | Anthony Liguori | { |
153 | 62aed765 | Anthony Liguori | USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); |
154 | 62aed765 | Anthony Liguori | if (klass->set_interface) {
|
155 | 62aed765 | Anthony Liguori | klass->set_interface(dev, interface, alt_old, alt_new); |
156 | 62aed765 | Anthony Liguori | } |
157 | 62aed765 | Anthony Liguori | } |
158 | 62aed765 | Anthony Liguori | |
159 | d307af79 | Anthony Liguori | static int usb_qdev_init(DeviceState *qdev) |
160 | 806b6024 | Gerd Hoffmann | { |
161 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
162 | 806b6024 | Gerd Hoffmann | int rc;
|
163 | 806b6024 | Gerd Hoffmann | |
164 | 62aed765 | Anthony Liguori | pstrcpy(dev->product_desc, sizeof(dev->product_desc),
|
165 | 62aed765 | Anthony Liguori | usb_device_get_product_desc(dev)); |
166 | 61e094c0 | Gerd Hoffmann | dev->auto_attach = 1;
|
167 | 132a3f55 | Gerd Hoffmann | QLIST_INIT(&dev->strings); |
168 | d8e17efd | Gerd Hoffmann | usb_ep_init(dev); |
169 | 891fb2cd | Gerd Hoffmann | rc = usb_claim_port(dev); |
170 | f462141f | Gerd Hoffmann | if (rc != 0) { |
171 | db3a5ed7 | Stefan Hajnoczi | return rc;
|
172 | 891fb2cd | Gerd Hoffmann | } |
173 | 62aed765 | Anthony Liguori | rc = usb_device_init(dev); |
174 | f462141f | Gerd Hoffmann | if (rc != 0) { |
175 | db3a5ed7 | Stefan Hajnoczi | usb_release_port(dev); |
176 | db3a5ed7 | Stefan Hajnoczi | return rc;
|
177 | f462141f | Gerd Hoffmann | } |
178 | f462141f | Gerd Hoffmann | if (dev->auto_attach) {
|
179 | fa19bf83 | Hans de Goede | rc = usb_device_attach(dev); |
180 | f462141f | Gerd Hoffmann | if (rc != 0) { |
181 | db3a5ed7 | Stefan Hajnoczi | usb_qdev_exit(qdev); |
182 | db3a5ed7 | Stefan Hajnoczi | return rc;
|
183 | f462141f | Gerd Hoffmann | } |
184 | 891fb2cd | Gerd Hoffmann | } |
185 | f462141f | Gerd Hoffmann | return 0; |
186 | 806b6024 | Gerd Hoffmann | } |
187 | 806b6024 | Gerd Hoffmann | |
188 | a8e662b5 | Gerd Hoffmann | static int usb_qdev_exit(DeviceState *qdev) |
189 | a8e662b5 | Gerd Hoffmann | { |
190 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
191 | a8e662b5 | Gerd Hoffmann | |
192 | 290a5c60 | Hans de Goede | if (dev->attached) {
|
193 | 290a5c60 | Hans de Goede | usb_device_detach(dev); |
194 | 290a5c60 | Hans de Goede | } |
195 | 62aed765 | Anthony Liguori | usb_device_handle_destroy(dev); |
196 | 891fb2cd | Gerd Hoffmann | if (dev->port) {
|
197 | 891fb2cd | Gerd Hoffmann | usb_release_port(dev); |
198 | 891fb2cd | Gerd Hoffmann | } |
199 | a8e662b5 | Gerd Hoffmann | return 0; |
200 | a8e662b5 | Gerd Hoffmann | } |
201 | a8e662b5 | Gerd Hoffmann | |
202 | 62aed765 | Anthony Liguori | typedef struct LegacyUSBFactory |
203 | 806b6024 | Gerd Hoffmann | { |
204 | 62aed765 | Anthony Liguori | const char *name; |
205 | 62aed765 | Anthony Liguori | const char *usbdevice_name; |
206 | 3741715c | Jan Kiszka | USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); |
207 | 62aed765 | Anthony Liguori | } LegacyUSBFactory; |
208 | 806b6024 | Gerd Hoffmann | |
209 | 62aed765 | Anthony Liguori | static GSList *legacy_usb_factory;
|
210 | 62aed765 | Anthony Liguori | |
211 | ba02430f | Anthony Liguori | void usb_legacy_register(const char *typename, const char *usbdevice_name, |
212 | 3741715c | Jan Kiszka | USBDevice *(*usbdevice_init)(USBBus *bus, |
213 | 3741715c | Jan Kiszka | const char *params)) |
214 | 806b6024 | Gerd Hoffmann | { |
215 | 62aed765 | Anthony Liguori | if (usbdevice_name) {
|
216 | 62aed765 | Anthony Liguori | LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
217 | ba02430f | Anthony Liguori | f->name = typename; |
218 | 62aed765 | Anthony Liguori | f->usbdevice_name = usbdevice_name; |
219 | 62aed765 | Anthony Liguori | f->usbdevice_init = usbdevice_init; |
220 | 62aed765 | Anthony Liguori | legacy_usb_factory = g_slist_append(legacy_usb_factory, f); |
221 | 806b6024 | Gerd Hoffmann | } |
222 | 806b6024 | Gerd Hoffmann | } |
223 | 806b6024 | Gerd Hoffmann | |
224 | a5d2f727 | Gerd Hoffmann | USBDevice *usb_create(USBBus *bus, const char *name) |
225 | 806b6024 | Gerd Hoffmann | { |
226 | 806b6024 | Gerd Hoffmann | DeviceState *dev; |
227 | 806b6024 | Gerd Hoffmann | |
228 | 806b6024 | Gerd Hoffmann | dev = qdev_create(&bus->qbus, name); |
229 | 62aed765 | Anthony Liguori | return USB_DEVICE(dev);
|
230 | 806b6024 | Gerd Hoffmann | } |
231 | a5d2f727 | Gerd Hoffmann | |
232 | a5d2f727 | Gerd Hoffmann | USBDevice *usb_create_simple(USBBus *bus, const char *name) |
233 | a5d2f727 | Gerd Hoffmann | { |
234 | a5d2f727 | Gerd Hoffmann | USBDevice *dev = usb_create(bus, name); |
235 | 2af2a1b8 | Gerd Hoffmann | int rc;
|
236 | 2af2a1b8 | Gerd Hoffmann | |
237 | d44168ff | Paul Brook | if (!dev) {
|
238 | be62a2eb | Markus Armbruster | error_report("Failed to create USB device '%s'", name);
|
239 | 2af2a1b8 | Gerd Hoffmann | return NULL; |
240 | 2af2a1b8 | Gerd Hoffmann | } |
241 | 2af2a1b8 | Gerd Hoffmann | rc = qdev_init(&dev->qdev); |
242 | 2af2a1b8 | Gerd Hoffmann | if (rc < 0) { |
243 | be62a2eb | Markus Armbruster | error_report("Failed to initialize USB device '%s'", name);
|
244 | 2af2a1b8 | Gerd Hoffmann | return NULL; |
245 | d44168ff | Paul Brook | } |
246 | a5d2f727 | Gerd Hoffmann | return dev;
|
247 | a5d2f727 | Gerd Hoffmann | } |
248 | a5d2f727 | Gerd Hoffmann | |
249 | 090ac642 | Hans de Goede | static void usb_fill_port(USBPort *port, void *opaque, int index, |
250 | 090ac642 | Hans de Goede | USBPortOps *ops, int speedmask)
|
251 | a5d2f727 | Gerd Hoffmann | { |
252 | a5d2f727 | Gerd Hoffmann | port->opaque = opaque; |
253 | a5d2f727 | Gerd Hoffmann | port->index = index; |
254 | 0d86d2be | Gerd Hoffmann | port->ops = ops; |
255 | 843d4e0c | Gerd Hoffmann | port->speedmask = speedmask; |
256 | 3631e6c8 | Hans de Goede | usb_port_location(port, NULL, index + 1); |
257 | 090ac642 | Hans de Goede | } |
258 | 090ac642 | Hans de Goede | |
259 | 090ac642 | Hans de Goede | void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, |
260 | 090ac642 | Hans de Goede | USBPortOps *ops, int speedmask)
|
261 | 090ac642 | Hans de Goede | { |
262 | 090ac642 | Hans de Goede | usb_fill_port(port, opaque, index, ops, speedmask); |
263 | 72cf2d4f | Blue Swirl | QTAILQ_INSERT_TAIL(&bus->free, port, next); |
264 | a5d2f727 | Gerd Hoffmann | bus->nfree++; |
265 | a5d2f727 | Gerd Hoffmann | } |
266 | a5d2f727 | Gerd Hoffmann | |
267 | ae60fea9 | Hans de Goede | int usb_register_companion(const char *masterbus, USBPort *ports[], |
268 | ae60fea9 | Hans de Goede | uint32_t portcount, uint32_t firstport, |
269 | ae60fea9 | Hans de Goede | void *opaque, USBPortOps *ops, int speedmask) |
270 | ae60fea9 | Hans de Goede | { |
271 | ae60fea9 | Hans de Goede | USBBus *bus; |
272 | ae60fea9 | Hans de Goede | int i;
|
273 | ae60fea9 | Hans de Goede | |
274 | ae60fea9 | Hans de Goede | QTAILQ_FOREACH(bus, &busses, next) { |
275 | ae60fea9 | Hans de Goede | if (strcmp(bus->qbus.name, masterbus) == 0) { |
276 | ae60fea9 | Hans de Goede | break;
|
277 | ae60fea9 | Hans de Goede | } |
278 | ae60fea9 | Hans de Goede | } |
279 | ae60fea9 | Hans de Goede | |
280 | ae60fea9 | Hans de Goede | if (!bus || !bus->ops->register_companion) {
|
281 | ae60fea9 | Hans de Goede | qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
|
282 | ae60fea9 | Hans de Goede | "an USB masterbus");
|
283 | ae60fea9 | Hans de Goede | if (bus) {
|
284 | ae60fea9 | Hans de Goede | error_printf_unless_qmp( |
285 | ae60fea9 | Hans de Goede | "USB bus '%s' does not allow companion controllers\n",
|
286 | ae60fea9 | Hans de Goede | masterbus); |
287 | ae60fea9 | Hans de Goede | } |
288 | ae60fea9 | Hans de Goede | return -1; |
289 | ae60fea9 | Hans de Goede | } |
290 | ae60fea9 | Hans de Goede | |
291 | ae60fea9 | Hans de Goede | for (i = 0; i < portcount; i++) { |
292 | ae60fea9 | Hans de Goede | usb_fill_port(ports[i], opaque, i, ops, speedmask); |
293 | ae60fea9 | Hans de Goede | } |
294 | ae60fea9 | Hans de Goede | |
295 | ae60fea9 | Hans de Goede | return bus->ops->register_companion(bus, ports, portcount, firstport);
|
296 | ae60fea9 | Hans de Goede | } |
297 | ae60fea9 | Hans de Goede | |
298 | c7a2196a | Gerd Hoffmann | void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) |
299 | c7a2196a | Gerd Hoffmann | { |
300 | c7a2196a | Gerd Hoffmann | if (upstream) {
|
301 | c7a2196a | Gerd Hoffmann | snprintf(downstream->path, sizeof(downstream->path), "%s.%d", |
302 | c7a2196a | Gerd Hoffmann | upstream->path, portnr); |
303 | c7a2196a | Gerd Hoffmann | } else {
|
304 | c7a2196a | Gerd Hoffmann | snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); |
305 | c7a2196a | Gerd Hoffmann | } |
306 | c7a2196a | Gerd Hoffmann | } |
307 | c7a2196a | Gerd Hoffmann | |
308 | a8e662b5 | Gerd Hoffmann | void usb_unregister_port(USBBus *bus, USBPort *port)
|
309 | a8e662b5 | Gerd Hoffmann | { |
310 | a8e662b5 | Gerd Hoffmann | if (port->dev)
|
311 | a8e662b5 | Gerd Hoffmann | qdev_free(&port->dev->qdev); |
312 | a8e662b5 | Gerd Hoffmann | QTAILQ_REMOVE(&bus->free, port, next); |
313 | a8e662b5 | Gerd Hoffmann | bus->nfree--; |
314 | a8e662b5 | Gerd Hoffmann | } |
315 | a8e662b5 | Gerd Hoffmann | |
316 | 891fb2cd | Gerd Hoffmann | int usb_claim_port(USBDevice *dev)
|
317 | a5d2f727 | Gerd Hoffmann | { |
318 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
319 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
320 | a5d2f727 | Gerd Hoffmann | |
321 | 891fb2cd | Gerd Hoffmann | assert(dev->port == NULL);
|
322 | 891fb2cd | Gerd Hoffmann | |
323 | 5f69076b | Gerd Hoffmann | if (dev->port_path) {
|
324 | 5f69076b | Gerd Hoffmann | QTAILQ_FOREACH(port, &bus->free, next) { |
325 | 5f69076b | Gerd Hoffmann | if (strcmp(port->path, dev->port_path) == 0) { |
326 | 5f69076b | Gerd Hoffmann | break;
|
327 | 5f69076b | Gerd Hoffmann | } |
328 | 5f69076b | Gerd Hoffmann | } |
329 | 5f69076b | Gerd Hoffmann | if (port == NULL) { |
330 | be62a2eb | Markus Armbruster | error_report("Error: usb port %s (bus %s) not found (in use?)",
|
331 | 891fb2cd | Gerd Hoffmann | dev->port_path, bus->qbus.name); |
332 | fa19bf83 | Hans de Goede | return -1; |
333 | 5f69076b | Gerd Hoffmann | } |
334 | 5f69076b | Gerd Hoffmann | } else {
|
335 | f79f2bfc | Anthony Liguori | if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { |
336 | 891fb2cd | Gerd Hoffmann | /* Create a new hub and chain it on */
|
337 | 891fb2cd | Gerd Hoffmann | usb_create_simple(bus, "usb-hub");
|
338 | 891fb2cd | Gerd Hoffmann | } |
339 | 891fb2cd | Gerd Hoffmann | if (bus->nfree == 0) { |
340 | 891fb2cd | Gerd Hoffmann | error_report("Error: tried to attach usb device %s to a bus "
|
341 | be62a2eb | Markus Armbruster | "with no free ports", dev->product_desc);
|
342 | 891fb2cd | Gerd Hoffmann | return -1; |
343 | 891fb2cd | Gerd Hoffmann | } |
344 | 5f69076b | Gerd Hoffmann | port = QTAILQ_FIRST(&bus->free); |
345 | 5f69076b | Gerd Hoffmann | } |
346 | 891fb2cd | Gerd Hoffmann | trace_usb_port_claim(bus->busnr, port->path); |
347 | a5d2f727 | Gerd Hoffmann | |
348 | 72cf2d4f | Blue Swirl | QTAILQ_REMOVE(&bus->free, port, next); |
349 | a5d2f727 | Gerd Hoffmann | bus->nfree--; |
350 | a5d2f727 | Gerd Hoffmann | |
351 | 891fb2cd | Gerd Hoffmann | dev->port = port; |
352 | 891fb2cd | Gerd Hoffmann | port->dev = dev; |
353 | a5d2f727 | Gerd Hoffmann | |
354 | 72cf2d4f | Blue Swirl | QTAILQ_INSERT_TAIL(&bus->used, port, next); |
355 | a5d2f727 | Gerd Hoffmann | bus->nused++; |
356 | fa19bf83 | Hans de Goede | return 0; |
357 | a5d2f727 | Gerd Hoffmann | } |
358 | a5d2f727 | Gerd Hoffmann | |
359 | 891fb2cd | Gerd Hoffmann | void usb_release_port(USBDevice *dev)
|
360 | a5d2f727 | Gerd Hoffmann | { |
361 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
362 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
363 | a5d2f727 | Gerd Hoffmann | |
364 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
365 | 891fb2cd | Gerd Hoffmann | trace_usb_port_release(bus->busnr, port->path); |
366 | 891fb2cd | Gerd Hoffmann | |
367 | 891fb2cd | Gerd Hoffmann | QTAILQ_REMOVE(&bus->used, port, next); |
368 | 891fb2cd | Gerd Hoffmann | bus->nused--; |
369 | 891fb2cd | Gerd Hoffmann | |
370 | 891fb2cd | Gerd Hoffmann | dev->port = NULL;
|
371 | 891fb2cd | Gerd Hoffmann | port->dev = NULL;
|
372 | 891fb2cd | Gerd Hoffmann | |
373 | 891fb2cd | Gerd Hoffmann | QTAILQ_INSERT_TAIL(&bus->free, port, next); |
374 | 891fb2cd | Gerd Hoffmann | bus->nfree++; |
375 | a5d2f727 | Gerd Hoffmann | } |
376 | a5d2f727 | Gerd Hoffmann | |
377 | 891fb2cd | Gerd Hoffmann | int usb_device_attach(USBDevice *dev)
|
378 | a8e662b5 | Gerd Hoffmann | { |
379 | a8e662b5 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
380 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
381 | a8e662b5 | Gerd Hoffmann | |
382 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
383 | 891fb2cd | Gerd Hoffmann | assert(!dev->attached); |
384 | 891fb2cd | Gerd Hoffmann | trace_usb_port_attach(bus->busnr, port->path); |
385 | 891fb2cd | Gerd Hoffmann | |
386 | 891fb2cd | Gerd Hoffmann | if (!(port->speedmask & dev->speedmask)) {
|
387 | 891fb2cd | Gerd Hoffmann | error_report("Warning: speed mismatch trying to attach "
|
388 | be62a2eb | Markus Armbruster | "usb device %s to bus %s",
|
389 | 891fb2cd | Gerd Hoffmann | dev->product_desc, bus->qbus.name); |
390 | a8e662b5 | Gerd Hoffmann | return -1; |
391 | a8e662b5 | Gerd Hoffmann | } |
392 | a8e662b5 | Gerd Hoffmann | |
393 | 891fb2cd | Gerd Hoffmann | dev->attached++; |
394 | 891fb2cd | Gerd Hoffmann | usb_attach(port); |
395 | a8e662b5 | Gerd Hoffmann | |
396 | 891fb2cd | Gerd Hoffmann | return 0; |
397 | 891fb2cd | Gerd Hoffmann | } |
398 | 891fb2cd | Gerd Hoffmann | |
399 | 891fb2cd | Gerd Hoffmann | int usb_device_detach(USBDevice *dev)
|
400 | 891fb2cd | Gerd Hoffmann | { |
401 | 891fb2cd | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
402 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
403 | a8e662b5 | Gerd Hoffmann | |
404 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
405 | 891fb2cd | Gerd Hoffmann | assert(dev->attached); |
406 | 891fb2cd | Gerd Hoffmann | trace_usb_port_detach(bus->busnr, port->path); |
407 | a8e662b5 | Gerd Hoffmann | |
408 | 891fb2cd | Gerd Hoffmann | usb_detach(port); |
409 | 891fb2cd | Gerd Hoffmann | dev->attached--; |
410 | a8e662b5 | Gerd Hoffmann | return 0; |
411 | a8e662b5 | Gerd Hoffmann | } |
412 | a8e662b5 | Gerd Hoffmann | |
413 | a5d2f727 | Gerd Hoffmann | int usb_device_delete_addr(int busnr, int addr) |
414 | a5d2f727 | Gerd Hoffmann | { |
415 | a5d2f727 | Gerd Hoffmann | USBBus *bus; |
416 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
417 | a5d2f727 | Gerd Hoffmann | USBDevice *dev; |
418 | a5d2f727 | Gerd Hoffmann | |
419 | a5d2f727 | Gerd Hoffmann | bus = usb_bus_find(busnr); |
420 | a5d2f727 | Gerd Hoffmann | if (!bus)
|
421 | a5d2f727 | Gerd Hoffmann | return -1; |
422 | a5d2f727 | Gerd Hoffmann | |
423 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(port, &bus->used, next) { |
424 | a5d2f727 | Gerd Hoffmann | if (port->dev->addr == addr)
|
425 | a5d2f727 | Gerd Hoffmann | break;
|
426 | a5d2f727 | Gerd Hoffmann | } |
427 | a5d2f727 | Gerd Hoffmann | if (!port)
|
428 | a5d2f727 | Gerd Hoffmann | return -1; |
429 | a5d2f727 | Gerd Hoffmann | dev = port->dev; |
430 | a5d2f727 | Gerd Hoffmann | |
431 | a8e662b5 | Gerd Hoffmann | qdev_free(&dev->qdev); |
432 | a5d2f727 | Gerd Hoffmann | return 0; |
433 | a5d2f727 | Gerd Hoffmann | } |
434 | a5d2f727 | Gerd Hoffmann | |
435 | a5d2f727 | Gerd Hoffmann | static const char *usb_speed(unsigned int speed) |
436 | a5d2f727 | Gerd Hoffmann | { |
437 | a5d2f727 | Gerd Hoffmann | static const char *txt[] = { |
438 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_LOW ] = "1.5",
|
439 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_FULL ] = "12",
|
440 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_HIGH ] = "480",
|
441 | 290d26d2 | Hans de Goede | [ USB_SPEED_SUPER ] = "5000",
|
442 | a5d2f727 | Gerd Hoffmann | }; |
443 | a5d2f727 | Gerd Hoffmann | if (speed >= ARRAY_SIZE(txt))
|
444 | a5d2f727 | Gerd Hoffmann | return "?"; |
445 | a5d2f727 | Gerd Hoffmann | return txt[speed];
|
446 | a5d2f727 | Gerd Hoffmann | } |
447 | a5d2f727 | Gerd Hoffmann | |
448 | a5d2f727 | Gerd Hoffmann | static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
449 | a5d2f727 | Gerd Hoffmann | { |
450 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
451 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
452 | a5d2f727 | Gerd Hoffmann | |
453 | c7a2196a | Gerd Hoffmann | monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
|
454 | 66a6593a | Gerd Hoffmann | indent, "", bus->busnr, dev->addr,
|
455 | c7a2196a | Gerd Hoffmann | dev->port ? dev->port->path : "-",
|
456 | 0fe6d12e | Markus Armbruster | usb_speed(dev->speed), dev->product_desc, |
457 | 66a6593a | Gerd Hoffmann | dev->attached ? ", attached" : ""); |
458 | a5d2f727 | Gerd Hoffmann | } |
459 | a5d2f727 | Gerd Hoffmann | |
460 | c7a2196a | Gerd Hoffmann | static char *usb_get_dev_path(DeviceState *qdev) |
461 | c7a2196a | Gerd Hoffmann | { |
462 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
463 | 7267c094 | Anthony Liguori | return g_strdup(dev->port->path);
|
464 | c7a2196a | Gerd Hoffmann | } |
465 | c7a2196a | Gerd Hoffmann | |
466 | 70d31cb2 | Gerd Hoffmann | static char *usb_get_fw_dev_path(DeviceState *qdev) |
467 | 70d31cb2 | Gerd Hoffmann | { |
468 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
469 | 70d31cb2 | Gerd Hoffmann | char *fw_path, *in;
|
470 | ea87e95f | Blue Swirl | ssize_t pos = 0, fw_len;
|
471 | 70d31cb2 | Gerd Hoffmann | long nr;
|
472 | 70d31cb2 | Gerd Hoffmann | |
473 | ea87e95f | Blue Swirl | fw_len = 32 + strlen(dev->port->path) * 6; |
474 | 7267c094 | Anthony Liguori | fw_path = g_malloc(fw_len); |
475 | 70d31cb2 | Gerd Hoffmann | in = dev->port->path; |
476 | ea87e95f | Blue Swirl | while (fw_len - pos > 0) { |
477 | 70d31cb2 | Gerd Hoffmann | nr = strtol(in, &in, 10);
|
478 | 70d31cb2 | Gerd Hoffmann | if (in[0] == '.') { |
479 | 70d31cb2 | Gerd Hoffmann | /* some hub between root port and device */
|
480 | ea87e95f | Blue Swirl | pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
|
481 | 70d31cb2 | Gerd Hoffmann | in++; |
482 | 70d31cb2 | Gerd Hoffmann | } else {
|
483 | 70d31cb2 | Gerd Hoffmann | /* the device itself */
|
484 | ea87e95f | Blue Swirl | pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
|
485 | ea87e95f | Blue Swirl | qdev_fw_name(qdev), nr); |
486 | 70d31cb2 | Gerd Hoffmann | break;
|
487 | 70d31cb2 | Gerd Hoffmann | } |
488 | 70d31cb2 | Gerd Hoffmann | } |
489 | 70d31cb2 | Gerd Hoffmann | return fw_path;
|
490 | 70d31cb2 | Gerd Hoffmann | } |
491 | 70d31cb2 | Gerd Hoffmann | |
492 | a5d2f727 | Gerd Hoffmann | void usb_info(Monitor *mon)
|
493 | a5d2f727 | Gerd Hoffmann | { |
494 | a5d2f727 | Gerd Hoffmann | USBBus *bus; |
495 | a5d2f727 | Gerd Hoffmann | USBDevice *dev; |
496 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
497 | a5d2f727 | Gerd Hoffmann | |
498 | 72cf2d4f | Blue Swirl | if (QTAILQ_EMPTY(&busses)) {
|
499 | a5d2f727 | Gerd Hoffmann | monitor_printf(mon, "USB support not enabled\n");
|
500 | a5d2f727 | Gerd Hoffmann | return;
|
501 | a5d2f727 | Gerd Hoffmann | } |
502 | a5d2f727 | Gerd Hoffmann | |
503 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(bus, &busses, next) { |
504 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(port, &bus->used, next) { |
505 | a5d2f727 | Gerd Hoffmann | dev = port->dev; |
506 | a5d2f727 | Gerd Hoffmann | if (!dev)
|
507 | a5d2f727 | Gerd Hoffmann | continue;
|
508 | c7a2196a | Gerd Hoffmann | monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
|
509 | c7a2196a | Gerd Hoffmann | bus->busnr, dev->addr, port->path, usb_speed(dev->speed), |
510 | 0fe6d12e | Markus Armbruster | dev->product_desc); |
511 | a5d2f727 | Gerd Hoffmann | } |
512 | a5d2f727 | Gerd Hoffmann | } |
513 | a5d2f727 | Gerd Hoffmann | } |
514 | a5d2f727 | Gerd Hoffmann | |
515 | 0958b4cc | Gerd Hoffmann | /* handle legacy -usbdevice cmd line option */
|
516 | 0958b4cc | Gerd Hoffmann | USBDevice *usbdevice_create(const char *cmdline) |
517 | 0958b4cc | Gerd Hoffmann | { |
518 | 0958b4cc | Gerd Hoffmann | USBBus *bus = usb_bus_find(-1 /* any */); |
519 | 62aed765 | Anthony Liguori | LegacyUSBFactory *f = NULL;
|
520 | 62aed765 | Anthony Liguori | GSList *i; |
521 | 702f3e0f | Jan Kiszka | char driver[32]; |
522 | 702f3e0f | Jan Kiszka | const char *params; |
523 | 0958b4cc | Gerd Hoffmann | int len;
|
524 | 0958b4cc | Gerd Hoffmann | |
525 | 0958b4cc | Gerd Hoffmann | params = strchr(cmdline,':');
|
526 | 0958b4cc | Gerd Hoffmann | if (params) {
|
527 | 0958b4cc | Gerd Hoffmann | params++; |
528 | 0958b4cc | Gerd Hoffmann | len = params - cmdline; |
529 | 0958b4cc | Gerd Hoffmann | if (len > sizeof(driver)) |
530 | 0958b4cc | Gerd Hoffmann | len = sizeof(driver);
|
531 | 0958b4cc | Gerd Hoffmann | pstrcpy(driver, len, cmdline); |
532 | 0958b4cc | Gerd Hoffmann | } else {
|
533 | 702f3e0f | Jan Kiszka | params = "";
|
534 | 0958b4cc | Gerd Hoffmann | pstrcpy(driver, sizeof(driver), cmdline);
|
535 | 0958b4cc | Gerd Hoffmann | } |
536 | 0958b4cc | Gerd Hoffmann | |
537 | 62aed765 | Anthony Liguori | for (i = legacy_usb_factory; i; i = i->next) {
|
538 | 62aed765 | Anthony Liguori | f = i->data; |
539 | 62aed765 | Anthony Liguori | if (strcmp(f->usbdevice_name, driver) == 0) { |
540 | 62aed765 | Anthony Liguori | break;
|
541 | 62aed765 | Anthony Liguori | } |
542 | 0958b4cc | Gerd Hoffmann | } |
543 | 62aed765 | Anthony Liguori | if (i == NULL) { |
544 | 0958b4cc | Gerd Hoffmann | #if 0
|
545 | 0958b4cc | Gerd Hoffmann | /* no error because some drivers are not converted (yet) */
|
546 | 1ecda02b | Markus Armbruster | error_report("usbdevice %s not found", driver);
|
547 | 0958b4cc | Gerd Hoffmann | #endif
|
548 | 0958b4cc | Gerd Hoffmann | return NULL; |
549 | 0958b4cc | Gerd Hoffmann | } |
550 | 0958b4cc | Gerd Hoffmann | |
551 | 62aed765 | Anthony Liguori | if (!f->usbdevice_init) {
|
552 | 98f22dc1 | TeLeMan | if (*params) {
|
553 | 1ecda02b | Markus Armbruster | error_report("usbdevice %s accepts no params", driver);
|
554 | 0958b4cc | Gerd Hoffmann | return NULL; |
555 | 0958b4cc | Gerd Hoffmann | } |
556 | 62aed765 | Anthony Liguori | return usb_create_simple(bus, f->name);
|
557 | 0958b4cc | Gerd Hoffmann | } |
558 | 3741715c | Jan Kiszka | return f->usbdevice_init(bus, params);
|
559 | 0958b4cc | Gerd Hoffmann | } |
560 | 62aed765 | Anthony Liguori | |
561 | 39bffca2 | Anthony Liguori | static void usb_device_class_init(ObjectClass *klass, void *data) |
562 | 39bffca2 | Anthony Liguori | { |
563 | 39bffca2 | Anthony Liguori | DeviceClass *k = DEVICE_CLASS(klass); |
564 | 39bffca2 | Anthony Liguori | k->bus_info = &usb_bus_info; |
565 | 39bffca2 | Anthony Liguori | k->init = usb_qdev_init; |
566 | 39bffca2 | Anthony Liguori | k->unplug = qdev_simple_unplug_cb; |
567 | 39bffca2 | Anthony Liguori | k->exit = usb_qdev_exit; |
568 | 39bffca2 | Anthony Liguori | } |
569 | 39bffca2 | Anthony Liguori | |
570 | 62aed765 | Anthony Liguori | static TypeInfo usb_device_type_info = {
|
571 | 62aed765 | Anthony Liguori | .name = TYPE_USB_DEVICE, |
572 | 62aed765 | Anthony Liguori | .parent = TYPE_DEVICE, |
573 | 62aed765 | Anthony Liguori | .instance_size = sizeof(USBDevice),
|
574 | 62aed765 | Anthony Liguori | .abstract = true,
|
575 | 62aed765 | Anthony Liguori | .class_size = sizeof(USBDeviceClass),
|
576 | 39bffca2 | Anthony Liguori | .class_init = usb_device_class_init, |
577 | 62aed765 | Anthony Liguori | }; |
578 | 62aed765 | Anthony Liguori | |
579 | 83f7d43a | Andreas Färber | static void usb_register_types(void) |
580 | 62aed765 | Anthony Liguori | { |
581 | 62aed765 | Anthony Liguori | type_register_static(&usb_device_type_info); |
582 | 62aed765 | Anthony Liguori | } |
583 | 62aed765 | Anthony Liguori | |
584 | 83f7d43a | Andreas Färber | type_init(usb_register_types) |