root / hw / usb-bus.c @ 1f51470d
History | View | Annotate | Download (14.9 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 | 62aed765 | Anthony Liguori | USBDevice *(*usbdevice_init)(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 | ba02430f | Anthony Liguori | USBDevice *(*usbdevice_init)(const char *params)) |
213 | 806b6024 | Gerd Hoffmann | { |
214 | 62aed765 | Anthony Liguori | if (usbdevice_name) {
|
215 | 62aed765 | Anthony Liguori | LegacyUSBFactory *f = g_malloc0(sizeof(*f));
|
216 | ba02430f | Anthony Liguori | f->name = typename; |
217 | 62aed765 | Anthony Liguori | f->usbdevice_name = usbdevice_name; |
218 | 62aed765 | Anthony Liguori | f->usbdevice_init = usbdevice_init; |
219 | 62aed765 | Anthony Liguori | legacy_usb_factory = g_slist_append(legacy_usb_factory, f); |
220 | 806b6024 | Gerd Hoffmann | } |
221 | 806b6024 | Gerd Hoffmann | } |
222 | 806b6024 | Gerd Hoffmann | |
223 | a5d2f727 | Gerd Hoffmann | USBDevice *usb_create(USBBus *bus, const char *name) |
224 | 806b6024 | Gerd Hoffmann | { |
225 | 806b6024 | Gerd Hoffmann | DeviceState *dev; |
226 | 806b6024 | Gerd Hoffmann | |
227 | 806b6024 | Gerd Hoffmann | #if 1 |
228 | 806b6024 | Gerd Hoffmann | /* temporary stopgap until all usb is properly qdev-ified */
|
229 | 806b6024 | Gerd Hoffmann | if (!bus) {
|
230 | 806b6024 | Gerd Hoffmann | bus = usb_bus_find(-1);
|
231 | 806b6024 | Gerd Hoffmann | if (!bus)
|
232 | 806b6024 | Gerd Hoffmann | return NULL; |
233 | be62a2eb | Markus Armbruster | error_report("%s: no bus specified, using \"%s\" for \"%s\"",
|
234 | 806b6024 | Gerd Hoffmann | __FUNCTION__, bus->qbus.name, name); |
235 | 806b6024 | Gerd Hoffmann | } |
236 | 806b6024 | Gerd Hoffmann | #endif
|
237 | 806b6024 | Gerd Hoffmann | |
238 | 806b6024 | Gerd Hoffmann | dev = qdev_create(&bus->qbus, name); |
239 | 62aed765 | Anthony Liguori | return USB_DEVICE(dev);
|
240 | 806b6024 | Gerd Hoffmann | } |
241 | a5d2f727 | Gerd Hoffmann | |
242 | a5d2f727 | Gerd Hoffmann | USBDevice *usb_create_simple(USBBus *bus, const char *name) |
243 | a5d2f727 | Gerd Hoffmann | { |
244 | a5d2f727 | Gerd Hoffmann | USBDevice *dev = usb_create(bus, name); |
245 | 2af2a1b8 | Gerd Hoffmann | int rc;
|
246 | 2af2a1b8 | Gerd Hoffmann | |
247 | d44168ff | Paul Brook | if (!dev) {
|
248 | be62a2eb | Markus Armbruster | error_report("Failed to create USB device '%s'", name);
|
249 | 2af2a1b8 | Gerd Hoffmann | return NULL; |
250 | 2af2a1b8 | Gerd Hoffmann | } |
251 | 2af2a1b8 | Gerd Hoffmann | rc = qdev_init(&dev->qdev); |
252 | 2af2a1b8 | Gerd Hoffmann | if (rc < 0) { |
253 | be62a2eb | Markus Armbruster | error_report("Failed to initialize USB device '%s'", name);
|
254 | 2af2a1b8 | Gerd Hoffmann | return NULL; |
255 | d44168ff | Paul Brook | } |
256 | a5d2f727 | Gerd Hoffmann | return dev;
|
257 | a5d2f727 | Gerd Hoffmann | } |
258 | a5d2f727 | Gerd Hoffmann | |
259 | 090ac642 | Hans de Goede | static void usb_fill_port(USBPort *port, void *opaque, int index, |
260 | 090ac642 | Hans de Goede | USBPortOps *ops, int speedmask)
|
261 | a5d2f727 | Gerd Hoffmann | { |
262 | a5d2f727 | Gerd Hoffmann | port->opaque = opaque; |
263 | a5d2f727 | Gerd Hoffmann | port->index = index; |
264 | 0d86d2be | Gerd Hoffmann | port->ops = ops; |
265 | 843d4e0c | Gerd Hoffmann | port->speedmask = speedmask; |
266 | 3631e6c8 | Hans de Goede | usb_port_location(port, NULL, index + 1); |
267 | 090ac642 | Hans de Goede | } |
268 | 090ac642 | Hans de Goede | |
269 | 090ac642 | Hans de Goede | void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, |
270 | 090ac642 | Hans de Goede | USBPortOps *ops, int speedmask)
|
271 | 090ac642 | Hans de Goede | { |
272 | 090ac642 | Hans de Goede | usb_fill_port(port, opaque, index, ops, speedmask); |
273 | 72cf2d4f | Blue Swirl | QTAILQ_INSERT_TAIL(&bus->free, port, next); |
274 | a5d2f727 | Gerd Hoffmann | bus->nfree++; |
275 | a5d2f727 | Gerd Hoffmann | } |
276 | a5d2f727 | Gerd Hoffmann | |
277 | ae60fea9 | Hans de Goede | int usb_register_companion(const char *masterbus, USBPort *ports[], |
278 | ae60fea9 | Hans de Goede | uint32_t portcount, uint32_t firstport, |
279 | ae60fea9 | Hans de Goede | void *opaque, USBPortOps *ops, int speedmask) |
280 | ae60fea9 | Hans de Goede | { |
281 | ae60fea9 | Hans de Goede | USBBus *bus; |
282 | ae60fea9 | Hans de Goede | int i;
|
283 | ae60fea9 | Hans de Goede | |
284 | ae60fea9 | Hans de Goede | QTAILQ_FOREACH(bus, &busses, next) { |
285 | ae60fea9 | Hans de Goede | if (strcmp(bus->qbus.name, masterbus) == 0) { |
286 | ae60fea9 | Hans de Goede | break;
|
287 | ae60fea9 | Hans de Goede | } |
288 | ae60fea9 | Hans de Goede | } |
289 | ae60fea9 | Hans de Goede | |
290 | ae60fea9 | Hans de Goede | if (!bus || !bus->ops->register_companion) {
|
291 | ae60fea9 | Hans de Goede | qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
|
292 | ae60fea9 | Hans de Goede | "an USB masterbus");
|
293 | ae60fea9 | Hans de Goede | if (bus) {
|
294 | ae60fea9 | Hans de Goede | error_printf_unless_qmp( |
295 | ae60fea9 | Hans de Goede | "USB bus '%s' does not allow companion controllers\n",
|
296 | ae60fea9 | Hans de Goede | masterbus); |
297 | ae60fea9 | Hans de Goede | } |
298 | ae60fea9 | Hans de Goede | return -1; |
299 | ae60fea9 | Hans de Goede | } |
300 | ae60fea9 | Hans de Goede | |
301 | ae60fea9 | Hans de Goede | for (i = 0; i < portcount; i++) { |
302 | ae60fea9 | Hans de Goede | usb_fill_port(ports[i], opaque, i, ops, speedmask); |
303 | ae60fea9 | Hans de Goede | } |
304 | ae60fea9 | Hans de Goede | |
305 | ae60fea9 | Hans de Goede | return bus->ops->register_companion(bus, ports, portcount, firstport);
|
306 | ae60fea9 | Hans de Goede | } |
307 | ae60fea9 | Hans de Goede | |
308 | c7a2196a | Gerd Hoffmann | void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) |
309 | c7a2196a | Gerd Hoffmann | { |
310 | c7a2196a | Gerd Hoffmann | if (upstream) {
|
311 | c7a2196a | Gerd Hoffmann | snprintf(downstream->path, sizeof(downstream->path), "%s.%d", |
312 | c7a2196a | Gerd Hoffmann | upstream->path, portnr); |
313 | c7a2196a | Gerd Hoffmann | } else {
|
314 | c7a2196a | Gerd Hoffmann | snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); |
315 | c7a2196a | Gerd Hoffmann | } |
316 | c7a2196a | Gerd Hoffmann | } |
317 | c7a2196a | Gerd Hoffmann | |
318 | a8e662b5 | Gerd Hoffmann | void usb_unregister_port(USBBus *bus, USBPort *port)
|
319 | a8e662b5 | Gerd Hoffmann | { |
320 | a8e662b5 | Gerd Hoffmann | if (port->dev)
|
321 | a8e662b5 | Gerd Hoffmann | qdev_free(&port->dev->qdev); |
322 | a8e662b5 | Gerd Hoffmann | QTAILQ_REMOVE(&bus->free, port, next); |
323 | a8e662b5 | Gerd Hoffmann | bus->nfree--; |
324 | a8e662b5 | Gerd Hoffmann | } |
325 | a8e662b5 | Gerd Hoffmann | |
326 | 891fb2cd | Gerd Hoffmann | int usb_claim_port(USBDevice *dev)
|
327 | a5d2f727 | Gerd Hoffmann | { |
328 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
329 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
330 | a5d2f727 | Gerd Hoffmann | |
331 | 891fb2cd | Gerd Hoffmann | assert(dev->port == NULL);
|
332 | 891fb2cd | Gerd Hoffmann | |
333 | 5f69076b | Gerd Hoffmann | if (dev->port_path) {
|
334 | 5f69076b | Gerd Hoffmann | QTAILQ_FOREACH(port, &bus->free, next) { |
335 | 5f69076b | Gerd Hoffmann | if (strcmp(port->path, dev->port_path) == 0) { |
336 | 5f69076b | Gerd Hoffmann | break;
|
337 | 5f69076b | Gerd Hoffmann | } |
338 | 5f69076b | Gerd Hoffmann | } |
339 | 5f69076b | Gerd Hoffmann | if (port == NULL) { |
340 | be62a2eb | Markus Armbruster | error_report("Error: usb port %s (bus %s) not found (in use?)",
|
341 | 891fb2cd | Gerd Hoffmann | dev->port_path, bus->qbus.name); |
342 | fa19bf83 | Hans de Goede | return -1; |
343 | 5f69076b | Gerd Hoffmann | } |
344 | 5f69076b | Gerd Hoffmann | } else {
|
345 | f79f2bfc | Anthony Liguori | if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { |
346 | 891fb2cd | Gerd Hoffmann | /* Create a new hub and chain it on */
|
347 | 891fb2cd | Gerd Hoffmann | usb_create_simple(bus, "usb-hub");
|
348 | 891fb2cd | Gerd Hoffmann | } |
349 | 891fb2cd | Gerd Hoffmann | if (bus->nfree == 0) { |
350 | 891fb2cd | Gerd Hoffmann | error_report("Error: tried to attach usb device %s to a bus "
|
351 | be62a2eb | Markus Armbruster | "with no free ports", dev->product_desc);
|
352 | 891fb2cd | Gerd Hoffmann | return -1; |
353 | 891fb2cd | Gerd Hoffmann | } |
354 | 5f69076b | Gerd Hoffmann | port = QTAILQ_FIRST(&bus->free); |
355 | 5f69076b | Gerd Hoffmann | } |
356 | 891fb2cd | Gerd Hoffmann | trace_usb_port_claim(bus->busnr, port->path); |
357 | a5d2f727 | Gerd Hoffmann | |
358 | 72cf2d4f | Blue Swirl | QTAILQ_REMOVE(&bus->free, port, next); |
359 | a5d2f727 | Gerd Hoffmann | bus->nfree--; |
360 | a5d2f727 | Gerd Hoffmann | |
361 | 891fb2cd | Gerd Hoffmann | dev->port = port; |
362 | 891fb2cd | Gerd Hoffmann | port->dev = dev; |
363 | a5d2f727 | Gerd Hoffmann | |
364 | 72cf2d4f | Blue Swirl | QTAILQ_INSERT_TAIL(&bus->used, port, next); |
365 | a5d2f727 | Gerd Hoffmann | bus->nused++; |
366 | fa19bf83 | Hans de Goede | return 0; |
367 | a5d2f727 | Gerd Hoffmann | } |
368 | a5d2f727 | Gerd Hoffmann | |
369 | 891fb2cd | Gerd Hoffmann | void usb_release_port(USBDevice *dev)
|
370 | a5d2f727 | Gerd Hoffmann | { |
371 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
372 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
373 | a5d2f727 | Gerd Hoffmann | |
374 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
375 | 891fb2cd | Gerd Hoffmann | trace_usb_port_release(bus->busnr, port->path); |
376 | 891fb2cd | Gerd Hoffmann | |
377 | 891fb2cd | Gerd Hoffmann | QTAILQ_REMOVE(&bus->used, port, next); |
378 | 891fb2cd | Gerd Hoffmann | bus->nused--; |
379 | 891fb2cd | Gerd Hoffmann | |
380 | 891fb2cd | Gerd Hoffmann | dev->port = NULL;
|
381 | 891fb2cd | Gerd Hoffmann | port->dev = NULL;
|
382 | 891fb2cd | Gerd Hoffmann | |
383 | 891fb2cd | Gerd Hoffmann | QTAILQ_INSERT_TAIL(&bus->free, port, next); |
384 | 891fb2cd | Gerd Hoffmann | bus->nfree++; |
385 | a5d2f727 | Gerd Hoffmann | } |
386 | a5d2f727 | Gerd Hoffmann | |
387 | 891fb2cd | Gerd Hoffmann | int usb_device_attach(USBDevice *dev)
|
388 | a8e662b5 | Gerd Hoffmann | { |
389 | a8e662b5 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
390 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
391 | a8e662b5 | Gerd Hoffmann | |
392 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
393 | 891fb2cd | Gerd Hoffmann | assert(!dev->attached); |
394 | 891fb2cd | Gerd Hoffmann | trace_usb_port_attach(bus->busnr, port->path); |
395 | 891fb2cd | Gerd Hoffmann | |
396 | 891fb2cd | Gerd Hoffmann | if (!(port->speedmask & dev->speedmask)) {
|
397 | 891fb2cd | Gerd Hoffmann | error_report("Warning: speed mismatch trying to attach "
|
398 | be62a2eb | Markus Armbruster | "usb device %s to bus %s",
|
399 | 891fb2cd | Gerd Hoffmann | dev->product_desc, bus->qbus.name); |
400 | a8e662b5 | Gerd Hoffmann | return -1; |
401 | a8e662b5 | Gerd Hoffmann | } |
402 | a8e662b5 | Gerd Hoffmann | |
403 | 891fb2cd | Gerd Hoffmann | dev->attached++; |
404 | 891fb2cd | Gerd Hoffmann | usb_attach(port); |
405 | a8e662b5 | Gerd Hoffmann | |
406 | 891fb2cd | Gerd Hoffmann | return 0; |
407 | 891fb2cd | Gerd Hoffmann | } |
408 | 891fb2cd | Gerd Hoffmann | |
409 | 891fb2cd | Gerd Hoffmann | int usb_device_detach(USBDevice *dev)
|
410 | 891fb2cd | Gerd Hoffmann | { |
411 | 891fb2cd | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
412 | 891fb2cd | Gerd Hoffmann | USBPort *port = dev->port; |
413 | a8e662b5 | Gerd Hoffmann | |
414 | 891fb2cd | Gerd Hoffmann | assert(port != NULL);
|
415 | 891fb2cd | Gerd Hoffmann | assert(dev->attached); |
416 | 891fb2cd | Gerd Hoffmann | trace_usb_port_detach(bus->busnr, port->path); |
417 | a8e662b5 | Gerd Hoffmann | |
418 | 891fb2cd | Gerd Hoffmann | usb_detach(port); |
419 | 891fb2cd | Gerd Hoffmann | dev->attached--; |
420 | a8e662b5 | Gerd Hoffmann | return 0; |
421 | a8e662b5 | Gerd Hoffmann | } |
422 | a8e662b5 | Gerd Hoffmann | |
423 | a5d2f727 | Gerd Hoffmann | int usb_device_delete_addr(int busnr, int addr) |
424 | a5d2f727 | Gerd Hoffmann | { |
425 | a5d2f727 | Gerd Hoffmann | USBBus *bus; |
426 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
427 | a5d2f727 | Gerd Hoffmann | USBDevice *dev; |
428 | a5d2f727 | Gerd Hoffmann | |
429 | a5d2f727 | Gerd Hoffmann | bus = usb_bus_find(busnr); |
430 | a5d2f727 | Gerd Hoffmann | if (!bus)
|
431 | a5d2f727 | Gerd Hoffmann | return -1; |
432 | a5d2f727 | Gerd Hoffmann | |
433 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(port, &bus->used, next) { |
434 | a5d2f727 | Gerd Hoffmann | if (port->dev->addr == addr)
|
435 | a5d2f727 | Gerd Hoffmann | break;
|
436 | a5d2f727 | Gerd Hoffmann | } |
437 | a5d2f727 | Gerd Hoffmann | if (!port)
|
438 | a5d2f727 | Gerd Hoffmann | return -1; |
439 | a5d2f727 | Gerd Hoffmann | dev = port->dev; |
440 | a5d2f727 | Gerd Hoffmann | |
441 | a8e662b5 | Gerd Hoffmann | qdev_free(&dev->qdev); |
442 | a5d2f727 | Gerd Hoffmann | return 0; |
443 | a5d2f727 | Gerd Hoffmann | } |
444 | a5d2f727 | Gerd Hoffmann | |
445 | a5d2f727 | Gerd Hoffmann | static const char *usb_speed(unsigned int speed) |
446 | a5d2f727 | Gerd Hoffmann | { |
447 | a5d2f727 | Gerd Hoffmann | static const char *txt[] = { |
448 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_LOW ] = "1.5",
|
449 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_FULL ] = "12",
|
450 | a5d2f727 | Gerd Hoffmann | [ USB_SPEED_HIGH ] = "480",
|
451 | 290d26d2 | Hans de Goede | [ USB_SPEED_SUPER ] = "5000",
|
452 | a5d2f727 | Gerd Hoffmann | }; |
453 | a5d2f727 | Gerd Hoffmann | if (speed >= ARRAY_SIZE(txt))
|
454 | a5d2f727 | Gerd Hoffmann | return "?"; |
455 | a5d2f727 | Gerd Hoffmann | return txt[speed];
|
456 | a5d2f727 | Gerd Hoffmann | } |
457 | a5d2f727 | Gerd Hoffmann | |
458 | a5d2f727 | Gerd Hoffmann | static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
459 | a5d2f727 | Gerd Hoffmann | { |
460 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
461 | a5d2f727 | Gerd Hoffmann | USBBus *bus = usb_bus_from_device(dev); |
462 | a5d2f727 | Gerd Hoffmann | |
463 | c7a2196a | Gerd Hoffmann | monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
|
464 | 66a6593a | Gerd Hoffmann | indent, "", bus->busnr, dev->addr,
|
465 | c7a2196a | Gerd Hoffmann | dev->port ? dev->port->path : "-",
|
466 | 0fe6d12e | Markus Armbruster | usb_speed(dev->speed), dev->product_desc, |
467 | 66a6593a | Gerd Hoffmann | dev->attached ? ", attached" : ""); |
468 | a5d2f727 | Gerd Hoffmann | } |
469 | a5d2f727 | Gerd Hoffmann | |
470 | c7a2196a | Gerd Hoffmann | static char *usb_get_dev_path(DeviceState *qdev) |
471 | c7a2196a | Gerd Hoffmann | { |
472 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
473 | 7267c094 | Anthony Liguori | return g_strdup(dev->port->path);
|
474 | c7a2196a | Gerd Hoffmann | } |
475 | c7a2196a | Gerd Hoffmann | |
476 | 70d31cb2 | Gerd Hoffmann | static char *usb_get_fw_dev_path(DeviceState *qdev) |
477 | 70d31cb2 | Gerd Hoffmann | { |
478 | 62aed765 | Anthony Liguori | USBDevice *dev = USB_DEVICE(qdev); |
479 | 70d31cb2 | Gerd Hoffmann | char *fw_path, *in;
|
480 | ea87e95f | Blue Swirl | ssize_t pos = 0, fw_len;
|
481 | 70d31cb2 | Gerd Hoffmann | long nr;
|
482 | 70d31cb2 | Gerd Hoffmann | |
483 | ea87e95f | Blue Swirl | fw_len = 32 + strlen(dev->port->path) * 6; |
484 | 7267c094 | Anthony Liguori | fw_path = g_malloc(fw_len); |
485 | 70d31cb2 | Gerd Hoffmann | in = dev->port->path; |
486 | ea87e95f | Blue Swirl | while (fw_len - pos > 0) { |
487 | 70d31cb2 | Gerd Hoffmann | nr = strtol(in, &in, 10);
|
488 | 70d31cb2 | Gerd Hoffmann | if (in[0] == '.') { |
489 | 70d31cb2 | Gerd Hoffmann | /* some hub between root port and device */
|
490 | ea87e95f | Blue Swirl | pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
|
491 | 70d31cb2 | Gerd Hoffmann | in++; |
492 | 70d31cb2 | Gerd Hoffmann | } else {
|
493 | 70d31cb2 | Gerd Hoffmann | /* the device itself */
|
494 | ea87e95f | Blue Swirl | pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
|
495 | ea87e95f | Blue Swirl | qdev_fw_name(qdev), nr); |
496 | 70d31cb2 | Gerd Hoffmann | break;
|
497 | 70d31cb2 | Gerd Hoffmann | } |
498 | 70d31cb2 | Gerd Hoffmann | } |
499 | 70d31cb2 | Gerd Hoffmann | return fw_path;
|
500 | 70d31cb2 | Gerd Hoffmann | } |
501 | 70d31cb2 | Gerd Hoffmann | |
502 | a5d2f727 | Gerd Hoffmann | void usb_info(Monitor *mon)
|
503 | a5d2f727 | Gerd Hoffmann | { |
504 | a5d2f727 | Gerd Hoffmann | USBBus *bus; |
505 | a5d2f727 | Gerd Hoffmann | USBDevice *dev; |
506 | a5d2f727 | Gerd Hoffmann | USBPort *port; |
507 | a5d2f727 | Gerd Hoffmann | |
508 | 72cf2d4f | Blue Swirl | if (QTAILQ_EMPTY(&busses)) {
|
509 | a5d2f727 | Gerd Hoffmann | monitor_printf(mon, "USB support not enabled\n");
|
510 | a5d2f727 | Gerd Hoffmann | return;
|
511 | a5d2f727 | Gerd Hoffmann | } |
512 | a5d2f727 | Gerd Hoffmann | |
513 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(bus, &busses, next) { |
514 | 72cf2d4f | Blue Swirl | QTAILQ_FOREACH(port, &bus->used, next) { |
515 | a5d2f727 | Gerd Hoffmann | dev = port->dev; |
516 | a5d2f727 | Gerd Hoffmann | if (!dev)
|
517 | a5d2f727 | Gerd Hoffmann | continue;
|
518 | c7a2196a | Gerd Hoffmann | monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
|
519 | c7a2196a | Gerd Hoffmann | bus->busnr, dev->addr, port->path, usb_speed(dev->speed), |
520 | 0fe6d12e | Markus Armbruster | dev->product_desc); |
521 | a5d2f727 | Gerd Hoffmann | } |
522 | a5d2f727 | Gerd Hoffmann | } |
523 | a5d2f727 | Gerd Hoffmann | } |
524 | a5d2f727 | Gerd Hoffmann | |
525 | 0958b4cc | Gerd Hoffmann | /* handle legacy -usbdevice cmd line option */
|
526 | 0958b4cc | Gerd Hoffmann | USBDevice *usbdevice_create(const char *cmdline) |
527 | 0958b4cc | Gerd Hoffmann | { |
528 | 0958b4cc | Gerd Hoffmann | USBBus *bus = usb_bus_find(-1 /* any */); |
529 | 62aed765 | Anthony Liguori | LegacyUSBFactory *f = NULL;
|
530 | 62aed765 | Anthony Liguori | GSList *i; |
531 | 702f3e0f | Jan Kiszka | char driver[32]; |
532 | 702f3e0f | Jan Kiszka | const char *params; |
533 | 0958b4cc | Gerd Hoffmann | int len;
|
534 | 0958b4cc | Gerd Hoffmann | |
535 | 0958b4cc | Gerd Hoffmann | params = strchr(cmdline,':');
|
536 | 0958b4cc | Gerd Hoffmann | if (params) {
|
537 | 0958b4cc | Gerd Hoffmann | params++; |
538 | 0958b4cc | Gerd Hoffmann | len = params - cmdline; |
539 | 0958b4cc | Gerd Hoffmann | if (len > sizeof(driver)) |
540 | 0958b4cc | Gerd Hoffmann | len = sizeof(driver);
|
541 | 0958b4cc | Gerd Hoffmann | pstrcpy(driver, len, cmdline); |
542 | 0958b4cc | Gerd Hoffmann | } else {
|
543 | 702f3e0f | Jan Kiszka | params = "";
|
544 | 0958b4cc | Gerd Hoffmann | pstrcpy(driver, sizeof(driver), cmdline);
|
545 | 0958b4cc | Gerd Hoffmann | } |
546 | 0958b4cc | Gerd Hoffmann | |
547 | 62aed765 | Anthony Liguori | for (i = legacy_usb_factory; i; i = i->next) {
|
548 | 62aed765 | Anthony Liguori | f = i->data; |
549 | 62aed765 | Anthony Liguori | if (strcmp(f->usbdevice_name, driver) == 0) { |
550 | 62aed765 | Anthony Liguori | break;
|
551 | 62aed765 | Anthony Liguori | } |
552 | 0958b4cc | Gerd Hoffmann | } |
553 | 62aed765 | Anthony Liguori | if (i == NULL) { |
554 | 0958b4cc | Gerd Hoffmann | #if 0
|
555 | 0958b4cc | Gerd Hoffmann | /* no error because some drivers are not converted (yet) */
|
556 | 1ecda02b | Markus Armbruster | error_report("usbdevice %s not found", driver);
|
557 | 0958b4cc | Gerd Hoffmann | #endif
|
558 | 0958b4cc | Gerd Hoffmann | return NULL; |
559 | 0958b4cc | Gerd Hoffmann | } |
560 | 0958b4cc | Gerd Hoffmann | |
561 | 62aed765 | Anthony Liguori | if (!f->usbdevice_init) {
|
562 | 98f22dc1 | TeLeMan | if (*params) {
|
563 | 1ecda02b | Markus Armbruster | error_report("usbdevice %s accepts no params", driver);
|
564 | 0958b4cc | Gerd Hoffmann | return NULL; |
565 | 0958b4cc | Gerd Hoffmann | } |
566 | 62aed765 | Anthony Liguori | return usb_create_simple(bus, f->name);
|
567 | 0958b4cc | Gerd Hoffmann | } |
568 | 62aed765 | Anthony Liguori | return f->usbdevice_init(params);
|
569 | 0958b4cc | Gerd Hoffmann | } |
570 | 62aed765 | Anthony Liguori | |
571 | 39bffca2 | Anthony Liguori | static void usb_device_class_init(ObjectClass *klass, void *data) |
572 | 39bffca2 | Anthony Liguori | { |
573 | 39bffca2 | Anthony Liguori | DeviceClass *k = DEVICE_CLASS(klass); |
574 | 39bffca2 | Anthony Liguori | k->bus_info = &usb_bus_info; |
575 | 39bffca2 | Anthony Liguori | k->init = usb_qdev_init; |
576 | 39bffca2 | Anthony Liguori | k->unplug = qdev_simple_unplug_cb; |
577 | 39bffca2 | Anthony Liguori | k->exit = usb_qdev_exit; |
578 | 39bffca2 | Anthony Liguori | } |
579 | 39bffca2 | Anthony Liguori | |
580 | 62aed765 | Anthony Liguori | static TypeInfo usb_device_type_info = {
|
581 | 62aed765 | Anthony Liguori | .name = TYPE_USB_DEVICE, |
582 | 62aed765 | Anthony Liguori | .parent = TYPE_DEVICE, |
583 | 62aed765 | Anthony Liguori | .instance_size = sizeof(USBDevice),
|
584 | 62aed765 | Anthony Liguori | .abstract = true,
|
585 | 62aed765 | Anthony Liguori | .class_size = sizeof(USBDeviceClass),
|
586 | 39bffca2 | Anthony Liguori | .class_init = usb_device_class_init, |
587 | 62aed765 | Anthony Liguori | }; |
588 | 62aed765 | Anthony Liguori | |
589 | 83f7d43a | Andreas Färber | static void usb_register_types(void) |
590 | 62aed765 | Anthony Liguori | { |
591 | 62aed765 | Anthony Liguori | type_register_static(&usb_device_type_info); |
592 | 62aed765 | Anthony Liguori | } |
593 | 62aed765 | Anthony Liguori | |
594 | 83f7d43a | Andreas Färber | type_init(usb_register_types) |