blob: c8c685550541843ad2d703770fd0a31523eb8619 [file] [log] [blame]
bellard72899af2006-04-24 21:18:20 +00001/*
2 * QEMU USB HUB emulation
3 *
4 * Copyright (c) 2005 Fabrice Bellard
ths5fafdf22007-09-16 21:08:06 +00005 *
bellard72899af2006-04-24 21:18:20 +00006 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "qemu-common.h"
Gerd Hoffmann529f8f92012-03-23 15:42:58 +010025#include "trace.h"
Gerd Hoffmannf1ae32a2012-03-07 14:55:18 +010026#include "hw/usb.h"
27#include "hw/usb/desc.h"
Gerd Hoffmannc24e4aa2013-03-20 11:40:02 +010028#include "qemu/error-report.h"
bellard72899af2006-04-24 21:18:20 +000029
Gerd Hoffmann062651c2010-11-26 13:13:22 +010030#define NUM_PORTS 8
bellard72899af2006-04-24 21:18:20 +000031
32typedef struct USBHubPort {
33 USBPort port;
34 uint16_t wPortStatus;
35 uint16_t wPortChange;
36} USBHubPort;
37
38typedef struct USBHubState {
39 USBDevice dev;
Gerd Hoffmann7567b512012-01-17 13:25:46 +010040 USBEndpoint *intr;
Gerd Hoffmann062651c2010-11-26 13:13:22 +010041 USBHubPort ports[NUM_PORTS];
bellard72899af2006-04-24 21:18:20 +000042} USBHubState;
43
Gongleie81b13a2015-05-06 20:55:27 +080044#define TYPE_USB_HUB "usb-hub"
45#define USB_HUB(obj) OBJECT_CHECK(USBHubState, (obj), TYPE_USB_HUB)
46
bellard72899af2006-04-24 21:18:20 +000047#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
48#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
49#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
50#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
51#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
52#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
53#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
54
55#define PORT_STAT_CONNECTION 0x0001
56#define PORT_STAT_ENABLE 0x0002
57#define PORT_STAT_SUSPEND 0x0004
58#define PORT_STAT_OVERCURRENT 0x0008
59#define PORT_STAT_RESET 0x0010
60#define PORT_STAT_POWER 0x0100
61#define PORT_STAT_LOW_SPEED 0x0200
62#define PORT_STAT_HIGH_SPEED 0x0400
63#define PORT_STAT_TEST 0x0800
64#define PORT_STAT_INDICATOR 0x1000
65
66#define PORT_STAT_C_CONNECTION 0x0001
67#define PORT_STAT_C_ENABLE 0x0002
68#define PORT_STAT_C_SUSPEND 0x0004
69#define PORT_STAT_C_OVERCURRENT 0x0008
70#define PORT_STAT_C_RESET 0x0010
71
72#define PORT_CONNECTION 0
73#define PORT_ENABLE 1
74#define PORT_SUSPEND 2
75#define PORT_OVERCURRENT 3
76#define PORT_RESET 4
77#define PORT_POWER 8
78#define PORT_LOWSPEED 9
79#define PORT_HIGHSPEED 10
80#define PORT_C_CONNECTION 16
81#define PORT_C_ENABLE 17
82#define PORT_C_SUSPEND 18
83#define PORT_C_OVERCURRENT 19
84#define PORT_C_RESET 20
85#define PORT_TEST 21
86#define PORT_INDICATOR 22
87
88/* same as Linux kernel root hubs */
89
Gerd Hoffmann062651c2010-11-26 13:13:22 +010090enum {
91 STR_MANUFACTURER = 1,
92 STR_PRODUCT,
93 STR_SERIALNUMBER,
94};
95
96static const USBDescStrings desc_strings = {
Crístian Viana93bfef42012-05-30 00:35:51 -030097 [STR_MANUFACTURER] = "QEMU",
Gerd Hoffmann062651c2010-11-26 13:13:22 +010098 [STR_PRODUCT] = "QEMU USB Hub",
99 [STR_SERIALNUMBER] = "314159",
100};
101
102static const USBDescIface desc_iface_hub = {
103 .bInterfaceNumber = 0,
104 .bNumEndpoints = 1,
105 .bInterfaceClass = USB_CLASS_HUB,
106 .eps = (USBDescEndpoint[]) {
107 {
108 .bEndpointAddress = USB_DIR_IN | 0x01,
109 .bmAttributes = USB_ENDPOINT_XFER_INT,
110 .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8,
111 .bInterval = 0xff,
112 },
113 }
114};
115
116static const USBDescDevice desc_device_hub = {
117 .bcdUSB = 0x0110,
118 .bDeviceClass = USB_CLASS_HUB,
119 .bMaxPacketSize0 = 8,
120 .bNumConfigurations = 1,
121 .confs = (USBDescConfig[]) {
122 {
123 .bNumInterfaces = 1,
124 .bConfigurationValue = 1,
Pantelis Koukousoulasbd939762013-12-16 09:42:49 +0200125 .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER |
126 USB_CFG_ATT_WAKEUP,
Brad Hardsadd75082011-04-03 15:33:19 +1000127 .nif = 1,
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100128 .ifs = &desc_iface_hub,
129 },
130 },
131};
132
133static const USBDesc desc_hub = {
134 .id = {
Roy Tamdb803582011-09-15 11:25:47 +0800135 .idVendor = 0x0409,
136 .idProduct = 0x55aa,
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100137 .bcdDevice = 0x0101,
138 .iManufacturer = STR_MANUFACTURER,
139 .iProduct = STR_PRODUCT,
140 .iSerialNumber = STR_SERIALNUMBER,
141 },
142 .full = &desc_device_hub,
143 .str = desc_strings,
144};
145
bellard72899af2006-04-24 21:18:20 +0000146static const uint8_t qemu_hub_hub_descriptor[] =
147{
bellard67f36562006-05-05 20:05:35 +0000148 0x00, /* u8 bLength; patched in later */
bellard72899af2006-04-24 21:18:20 +0000149 0x29, /* u8 bDescriptorType; Hub-descriptor */
150 0x00, /* u8 bNbrPorts; (patched later) */
151 0x0a, /* u16 wHubCharacteristics; */
152 0x00, /* (per-port OC, no power switching) */
153 0x01, /* u8 bPwrOn2pwrGood; 2ms */
bellard985d1742006-04-30 21:53:59 +0000154 0x00 /* u8 bHubContrCurrent; 0 mA */
155
156 /* DeviceRemovable and PortPwrCtrlMask patched in later */
bellard72899af2006-04-24 21:18:20 +0000157};
158
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100159static void usb_hub_attach(USBPort *port1)
bellard72899af2006-04-24 21:18:20 +0000160{
161 USBHubState *s = port1->opaque;
162 USBHubPort *port = &s->ports[port1->index];
ths3b46e622007-09-17 08:09:54 +0000163
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100164 trace_usb_hub_attach(s->dev.addr, port1->index + 1);
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100165 port->wPortStatus |= PORT_STAT_CONNECTION;
166 port->wPortChange |= PORT_STAT_C_CONNECTION;
167 if (port->port.dev->speed == USB_SPEED_LOW) {
168 port->wPortStatus |= PORT_STAT_LOW_SPEED;
bellard72899af2006-04-24 21:18:20 +0000169 } else {
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100170 port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
171 }
Gerd Hoffmann8550a022013-01-29 12:44:35 +0100172 usb_wakeup(s->intr, 0);
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100173}
174
175static void usb_hub_detach(USBPort *port1)
176{
177 USBHubState *s = port1->opaque;
178 USBHubPort *port = &s->ports[port1->index];
179
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100180 trace_usb_hub_detach(s->dev.addr, port1->index + 1);
Gerd Hoffmann8550a022013-01-29 12:44:35 +0100181 usb_wakeup(s->intr, 0);
Gerd Hoffmannbe35cbb2011-11-22 13:20:14 +0100182
Hans de Goede4706ab62011-06-24 12:31:11 +0200183 /* Let upstream know the device on this port is gone */
184 s->dev.port->ops->child_detach(s->dev.port, port1->dev);
185
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100186 port->wPortStatus &= ~PORT_STAT_CONNECTION;
187 port->wPortChange |= PORT_STAT_C_CONNECTION;
188 if (port->wPortStatus & PORT_STAT_ENABLE) {
189 port->wPortStatus &= ~PORT_STAT_ENABLE;
190 port->wPortChange |= PORT_STAT_C_ENABLE;
bellard72899af2006-04-24 21:18:20 +0000191 }
Gerd Hoffmann8550a022013-01-29 12:44:35 +0100192 usb_wakeup(s->intr, 0);
bellard72899af2006-04-24 21:18:20 +0000193}
194
Hans de Goede4706ab62011-06-24 12:31:11 +0200195static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
196{
197 USBHubState *s = port1->opaque;
198
199 /* Pass along upstream */
200 s->dev.port->ops->child_detach(s->dev.port, child);
201}
202
Hans de Goeded47e59b2011-06-21 11:52:28 +0200203static void usb_hub_wakeup(USBPort *port1)
Gerd Hoffmann34239c72010-12-15 12:26:59 +0100204{
Hans de Goeded47e59b2011-06-21 11:52:28 +0200205 USBHubState *s = port1->opaque;
206 USBHubPort *port = &s->ports[port1->index];
Gerd Hoffmann34239c72010-12-15 12:26:59 +0100207
208 if (port->wPortStatus & PORT_STAT_SUSPEND) {
209 port->wPortChange |= PORT_STAT_C_SUSPEND;
Gerd Hoffmann8550a022013-01-29 12:44:35 +0100210 usb_wakeup(s->intr, 0);
Gerd Hoffmann34239c72010-12-15 12:26:59 +0100211 }
212}
213
Hans de Goeded47e59b2011-06-21 11:52:28 +0200214static void usb_hub_complete(USBPort *port, USBPacket *packet)
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100215{
Hans de Goeded47e59b2011-06-21 11:52:28 +0200216 USBHubState *s = port->opaque;
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100217
218 /*
219 * Just pass it along upstream for now.
220 *
Gerd Hoffmann80cf7cf2011-10-13 12:52:47 +0200221 * If we ever implement usb 2.0 split transactions this will
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100222 * become a little more complicated ...
Gerd Hoffmann80cf7cf2011-10-13 12:52:47 +0200223 *
224 * Can't use usb_packet_complete() here because packet->owner is
225 * cleared already, go call the ->complete() callback directly
226 * instead.
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100227 */
Gerd Hoffmann80cf7cf2011-10-13 12:52:47 +0200228 s->dev.port->ops->complete(s->dev.port, packet);
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100229}
230
Gerd Hoffmann06c75082012-01-10 17:08:13 +0100231static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
232{
Gongleie81b13a2015-05-06 20:55:27 +0800233 USBHubState *s = USB_HUB(dev);
Gerd Hoffmann06c75082012-01-10 17:08:13 +0100234 USBHubPort *port;
235 USBDevice *downstream;
236 int i;
237
238 for (i = 0; i < NUM_PORTS; i++) {
239 port = &s->ports[i];
240 if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
241 continue;
242 }
243 downstream = usb_find_device(&port->port, addr);
244 if (downstream != NULL) {
245 return downstream;
246 }
247 }
248 return NULL;
249}
250
bellard059809e2006-07-19 18:06:15 +0000251static void usb_hub_handle_reset(USBDevice *dev)
bellard72899af2006-04-24 21:18:20 +0000252{
Gongleie81b13a2015-05-06 20:55:27 +0800253 USBHubState *s = USB_HUB(dev);
Gerd Hoffmann20d183b2011-11-23 13:31:08 +0100254 USBHubPort *port;
255 int i;
256
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100257 trace_usb_hub_reset(s->dev.addr);
Gerd Hoffmann20d183b2011-11-23 13:31:08 +0100258 for (i = 0; i < NUM_PORTS; i++) {
259 port = s->ports + i;
260 port->wPortStatus = PORT_STAT_POWER;
261 port->wPortChange = 0;
262 if (port->port.dev && port->port.dev->attached) {
263 port->wPortStatus |= PORT_STAT_CONNECTION;
264 port->wPortChange |= PORT_STAT_C_CONNECTION;
265 if (port->port.dev->speed == USB_SPEED_LOW) {
266 port->wPortStatus |= PORT_STAT_LOW_SPEED;
267 }
268 }
269 }
bellard72899af2006-04-24 21:18:20 +0000270}
271
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100272static const char *feature_name(int feature)
273{
274 static const char *name[] = {
275 [PORT_CONNECTION] = "connection",
276 [PORT_ENABLE] = "enable",
277 [PORT_SUSPEND] = "suspend",
278 [PORT_OVERCURRENT] = "overcurrent",
279 [PORT_RESET] = "reset",
280 [PORT_POWER] = "power",
281 [PORT_LOWSPEED] = "lowspeed",
282 [PORT_HIGHSPEED] = "highspeed",
283 [PORT_C_CONNECTION] = "change connection",
284 [PORT_C_ENABLE] = "change enable",
285 [PORT_C_SUSPEND] = "change suspend",
286 [PORT_C_OVERCURRENT] = "change overcurrent",
287 [PORT_C_RESET] = "change reset",
288 [PORT_TEST] = "test",
289 [PORT_INDICATOR] = "indicator",
290 };
291 if (feature < 0 || feature >= ARRAY_SIZE(name)) {
292 return "?";
293 }
294 return name[feature] ?: "?";
295}
296
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100297static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
Hans de Goede007fd622011-02-02 16:33:13 +0100298 int request, int value, int index, int length, uint8_t *data)
bellard72899af2006-04-24 21:18:20 +0000299{
300 USBHubState *s = (USBHubState *)dev;
301 int ret;
302
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100303 trace_usb_hub_control(s->dev.addr, request, value, index, length);
304
Hans de Goede007fd622011-02-02 16:33:13 +0100305 ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100306 if (ret >= 0) {
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100307 return;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100308 }
309
bellard72899af2006-04-24 21:18:20 +0000310 switch(request) {
bellard985d1742006-04-30 21:53:59 +0000311 case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
312 if (value == 0 && index != 0x81) { /* clear ep halt */
313 goto fail;
314 }
bellard985d1742006-04-30 21:53:59 +0000315 break;
bellard72899af2006-04-24 21:18:20 +0000316 /* usb specific requests */
317 case GetHubStatus:
318 data[0] = 0;
319 data[1] = 0;
320 data[2] = 0;
321 data[3] = 0;
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100322 p->actual_length = 4;
bellard72899af2006-04-24 21:18:20 +0000323 break;
324 case GetPortStatus:
325 {
326 unsigned int n = index - 1;
327 USBHubPort *port;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100328 if (n >= NUM_PORTS) {
bellard72899af2006-04-24 21:18:20 +0000329 goto fail;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100330 }
bellard72899af2006-04-24 21:18:20 +0000331 port = &s->ports[n];
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100332 trace_usb_hub_get_port_status(s->dev.addr, index,
333 port->wPortStatus,
334 port->wPortChange);
bellard72899af2006-04-24 21:18:20 +0000335 data[0] = port->wPortStatus;
336 data[1] = port->wPortStatus >> 8;
337 data[2] = port->wPortChange;
338 data[3] = port->wPortChange >> 8;
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100339 p->actual_length = 4;
bellard72899af2006-04-24 21:18:20 +0000340 }
341 break;
342 case SetHubFeature:
343 case ClearHubFeature:
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100344 if (value != 0 && value != 1) {
bellard72899af2006-04-24 21:18:20 +0000345 goto fail;
346 }
bellard72899af2006-04-24 21:18:20 +0000347 break;
348 case SetPortFeature:
349 {
350 unsigned int n = index - 1;
351 USBHubPort *port;
352 USBDevice *dev;
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100353
354 trace_usb_hub_set_port_feature(s->dev.addr, index,
355 feature_name(value));
356
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100357 if (n >= NUM_PORTS) {
bellard72899af2006-04-24 21:18:20 +0000358 goto fail;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100359 }
bellard72899af2006-04-24 21:18:20 +0000360 port = &s->ports[n];
361 dev = port->port.dev;
362 switch(value) {
363 case PORT_SUSPEND:
364 port->wPortStatus |= PORT_STAT_SUSPEND;
365 break;
366 case PORT_RESET:
Gerd Hoffmann3393bc12011-09-15 09:20:02 +0200367 if (dev && dev->attached) {
Gerd Hoffmannd28f4e22012-01-06 15:23:10 +0100368 usb_device_reset(dev);
bellard72899af2006-04-24 21:18:20 +0000369 port->wPortChange |= PORT_STAT_C_RESET;
370 /* set enable bit */
bellard72899af2006-04-24 21:18:20 +0000371 port->wPortStatus |= PORT_STAT_ENABLE;
Gerd Hoffmann8550a022013-01-29 12:44:35 +0100372 usb_wakeup(s->intr, 0);
bellard72899af2006-04-24 21:18:20 +0000373 }
374 break;
375 case PORT_POWER:
376 break;
377 default:
378 goto fail;
379 }
bellard72899af2006-04-24 21:18:20 +0000380 }
381 break;
382 case ClearPortFeature:
383 {
384 unsigned int n = index - 1;
385 USBHubPort *port;
Blue Swirld4c4e6f2010-04-25 18:23:04 +0000386
Gerd Hoffmann529f8f92012-03-23 15:42:58 +0100387 trace_usb_hub_clear_port_feature(s->dev.addr, index,
388 feature_name(value));
389
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100390 if (n >= NUM_PORTS) {
bellard72899af2006-04-24 21:18:20 +0000391 goto fail;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100392 }
bellard72899af2006-04-24 21:18:20 +0000393 port = &s->ports[n];
bellard72899af2006-04-24 21:18:20 +0000394 switch(value) {
395 case PORT_ENABLE:
396 port->wPortStatus &= ~PORT_STAT_ENABLE;
397 break;
398 case PORT_C_ENABLE:
399 port->wPortChange &= ~PORT_STAT_C_ENABLE;
400 break;
401 case PORT_SUSPEND:
402 port->wPortStatus &= ~PORT_STAT_SUSPEND;
403 break;
404 case PORT_C_SUSPEND:
405 port->wPortChange &= ~PORT_STAT_C_SUSPEND;
406 break;
407 case PORT_C_CONNECTION:
408 port->wPortChange &= ~PORT_STAT_C_CONNECTION;
409 break;
410 case PORT_C_OVERCURRENT:
411 port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
412 break;
413 case PORT_C_RESET:
414 port->wPortChange &= ~PORT_STAT_C_RESET;
415 break;
416 default:
417 goto fail;
418 }
bellard72899af2006-04-24 21:18:20 +0000419 }
420 break;
421 case GetHubDescriptor:
bellard985d1742006-04-30 21:53:59 +0000422 {
423 unsigned int n, limit, var_hub_size = 0;
ths5fafdf22007-09-16 21:08:06 +0000424 memcpy(data, qemu_hub_hub_descriptor,
bellard985d1742006-04-30 21:53:59 +0000425 sizeof(qemu_hub_hub_descriptor));
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100426 data[2] = NUM_PORTS;
bellard985d1742006-04-30 21:53:59 +0000427
428 /* fill DeviceRemovable bits */
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100429 limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
bellard985d1742006-04-30 21:53:59 +0000430 for (n = 7; n < limit; n++) {
431 data[n] = 0x00;
432 var_hub_size++;
433 }
434
435 /* fill PortPwrCtrlMask bits */
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100436 limit = limit + ((NUM_PORTS + 7) / 8);
bellard985d1742006-04-30 21:53:59 +0000437 for (;n < limit; n++) {
438 data[n] = 0xff;
439 var_hub_size++;
440 }
441
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100442 p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
443 data[0] = p->actual_length;
bellard985d1742006-04-30 21:53:59 +0000444 break;
445 }
bellard72899af2006-04-24 21:18:20 +0000446 default:
447 fail:
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100448 p->status = USB_RET_STALL;
bellard72899af2006-04-24 21:18:20 +0000449 break;
450 }
bellard72899af2006-04-24 21:18:20 +0000451}
452
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100453static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
bellard72899af2006-04-24 21:18:20 +0000454{
455 USBHubState *s = (USBHubState *)dev;
bellard72899af2006-04-24 21:18:20 +0000456
pbrook4d611c92006-08-12 01:04:27 +0000457 switch(p->pid) {
bellard72899af2006-04-24 21:18:20 +0000458 case USB_TOKEN_IN:
Gerd Hoffmann079d0b72012-01-12 13:23:01 +0100459 if (p->ep->nr == 1) {
bellard72899af2006-04-24 21:18:20 +0000460 USBHubPort *port;
461 unsigned int status;
Gerd Hoffmann4f4321c2011-07-12 15:22:25 +0200462 uint8_t buf[4];
bellard72899af2006-04-24 21:18:20 +0000463 int i, n;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100464 n = (NUM_PORTS + 1 + 7) / 8;
Gerd Hoffmann4f4321c2011-07-12 15:22:25 +0200465 if (p->iov.size == 1) { /* FreeBSD workaround */
bellard985d1742006-04-30 21:53:59 +0000466 n = 1;
Gerd Hoffmann4f4321c2011-07-12 15:22:25 +0200467 } else if (n > p->iov.size) {
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100468 p->status = USB_RET_BABBLE;
469 return;
bellard985d1742006-04-30 21:53:59 +0000470 }
bellard72899af2006-04-24 21:18:20 +0000471 status = 0;
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100472 for(i = 0; i < NUM_PORTS; i++) {
bellard72899af2006-04-24 21:18:20 +0000473 port = &s->ports[i];
Gerd Hoffmannbdebd6e2013-08-27 17:00:04 +0200474 if (port->wPortChange)
bellard72899af2006-04-24 21:18:20 +0000475 status |= (1 << (i + 1));
476 }
477 if (status != 0) {
Gerd Hoffmannb8cbc132013-08-27 16:59:37 +0200478 trace_usb_hub_status_report(s->dev.addr, status);
bellard72899af2006-04-24 21:18:20 +0000479 for(i = 0; i < n; i++) {
Gerd Hoffmann4f4321c2011-07-12 15:22:25 +0200480 buf[i] = status >> (8 * i);
bellard72899af2006-04-24 21:18:20 +0000481 }
Gerd Hoffmann4f4321c2011-07-12 15:22:25 +0200482 usb_packet_copy(p, buf, n);
bellard72899af2006-04-24 21:18:20 +0000483 } else {
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100484 p->status = USB_RET_NAK; /* usb11 11.13.1 */
bellard72899af2006-04-24 21:18:20 +0000485 }
486 } else {
487 goto fail;
488 }
489 break;
490 case USB_TOKEN_OUT:
491 default:
492 fail:
Hans de Goede9a77a0f2012-11-01 17:15:01 +0100493 p->status = USB_RET_STALL;
bellard72899af2006-04-24 21:18:20 +0000494 break;
495 }
bellard72899af2006-04-24 21:18:20 +0000496}
497
bellard059809e2006-07-19 18:06:15 +0000498static void usb_hub_handle_destroy(USBDevice *dev)
499{
500 USBHubState *s = (USBHubState *)dev;
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200501 int i;
bellard059809e2006-07-19 18:06:15 +0000502
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100503 for (i = 0; i < NUM_PORTS; i++) {
Gerd Hoffmanna8e662b2009-09-25 21:42:39 +0200504 usb_unregister_port(usb_bus_from_device(dev),
505 &s->ports[i].port);
506 }
bellard059809e2006-07-19 18:06:15 +0000507}
508
Gerd Hoffmann0d86d2b2010-12-01 11:08:44 +0100509static USBPortOps usb_hub_port_ops = {
510 .attach = usb_hub_attach,
Gerd Hoffmann618c1692010-12-01 11:27:05 +0100511 .detach = usb_hub_detach,
Hans de Goede4706ab62011-06-24 12:31:11 +0200512 .child_detach = usb_hub_child_detach,
Gerd Hoffmann34239c72010-12-15 12:26:59 +0100513 .wakeup = usb_hub_wakeup,
Gerd Hoffmann13a9a0d2010-12-16 17:03:44 +0100514 .complete = usb_hub_complete,
Gerd Hoffmann0d86d2b2010-12-01 11:08:44 +0100515};
516
Gongleif3f8c452014-09-19 14:48:28 +0800517static void usb_hub_realize(USBDevice *dev, Error **errp)
bellard72899af2006-04-24 21:18:20 +0000518{
Gongleie81b13a2015-05-06 20:55:27 +0800519 USBHubState *s = USB_HUB(dev);
bellard72899af2006-04-24 21:18:20 +0000520 USBHubPort *port;
521 int i;
522
Gerd Hoffmannc24e4aa2013-03-20 11:40:02 +0100523 if (dev->port->hubcount == 5) {
Gongleif3f8c452014-09-19 14:48:28 +0800524 error_setg(errp, "usb hub chain too deep");
525 return;
Gerd Hoffmannc24e4aa2013-03-20 11:40:02 +0100526 }
527
Gerd Hoffmann9d55d1a2012-04-20 12:33:30 +0200528 usb_desc_create_serial(dev);
Gerd Hoffmanna980a062010-11-26 20:20:41 +0100529 usb_desc_init(dev);
Gerd Hoffmann7567b512012-01-17 13:25:46 +0100530 s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
Gerd Hoffmann062651c2010-11-26 13:13:22 +0100531 for (i = 0; i < NUM_PORTS; i++) {
bellard72899af2006-04-24 21:18:20 +0000532 port = &s->ports[i];
Gerd Hoffmanna5d2f722009-08-31 14:24:00 +0200533 usb_register_port(usb_bus_from_device(dev),
Gerd Hoffmannace13182011-01-12 11:34:50 +0100534 &port->port, s, i, &usb_hub_port_ops,
Gerd Hoffmann843d4e02010-12-03 17:30:13 +0100535 USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
Gerd Hoffmann891fb2c2011-09-01 13:56:37 +0200536 usb_port_location(&port->port, dev->port, i+1);
bellard72899af2006-04-24 21:18:20 +0000537 }
Gerd Hoffmann20d183b2011-11-23 13:31:08 +0100538 usb_hub_handle_reset(dev);
bellard72899af2006-04-24 21:18:20 +0000539}
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200540
Gerd Hoffmannd1550092010-12-15 12:45:24 +0100541static const VMStateDescription vmstate_usb_hub_port = {
542 .name = "usb-hub-port",
543 .version_id = 1,
544 .minimum_version_id = 1,
Juan Quintela6e3d6522014-04-16 13:31:26 +0200545 .fields = (VMStateField[]) {
Gerd Hoffmannd1550092010-12-15 12:45:24 +0100546 VMSTATE_UINT16(wPortStatus, USBHubPort),
547 VMSTATE_UINT16(wPortChange, USBHubPort),
548 VMSTATE_END_OF_LIST()
549 }
550};
551
552static const VMStateDescription vmstate_usb_hub = {
553 .name = "usb-hub",
554 .version_id = 1,
555 .minimum_version_id = 1,
Juan Quintela6e3d6522014-04-16 13:31:26 +0200556 .fields = (VMStateField[]) {
Gerd Hoffmannd1550092010-12-15 12:45:24 +0100557 VMSTATE_USB_DEVICE(dev, USBHubState),
558 VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0,
559 vmstate_usb_hub_port, USBHubPort),
560 VMSTATE_END_OF_LIST()
561 }
562};
563
Anthony Liguori39bffca2011-12-07 21:34:16 -0600564static void usb_hub_class_initfn(ObjectClass *klass, void *data)
Anthony Liguori62aed762011-12-15 14:53:10 -0600565{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600566 DeviceClass *dc = DEVICE_CLASS(klass);
Anthony Liguori62aed762011-12-15 14:53:10 -0600567 USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
568
Gongleif3f8c452014-09-19 14:48:28 +0800569 uc->realize = usb_hub_realize;
Anthony Liguori62aed762011-12-15 14:53:10 -0600570 uc->product_desc = "QEMU USB Hub";
571 uc->usb_desc = &desc_hub;
Gerd Hoffmann06c75082012-01-10 17:08:13 +0100572 uc->find_device = usb_hub_find_device;
Anthony Liguori62aed762011-12-15 14:53:10 -0600573 uc->handle_reset = usb_hub_handle_reset;
574 uc->handle_control = usb_hub_handle_control;
575 uc->handle_data = usb_hub_handle_data;
576 uc->handle_destroy = usb_hub_handle_destroy;
Marcel Apfelbaum125ee0e2013-07-29 17:17:45 +0300577 set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
Anthony Liguori39bffca2011-12-07 21:34:16 -0600578 dc->fw_name = "hub";
579 dc->vmsd = &vmstate_usb_hub;
Anthony Liguori62aed762011-12-15 14:53:10 -0600580}
581
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100582static const TypeInfo hub_info = {
Gongleie81b13a2015-05-06 20:55:27 +0800583 .name = TYPE_USB_HUB,
Anthony Liguori39bffca2011-12-07 21:34:16 -0600584 .parent = TYPE_USB_DEVICE,
585 .instance_size = sizeof(USBHubState),
586 .class_init = usb_hub_class_initfn,
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200587};
588
Andreas Färber83f7d432012-02-09 15:20:55 +0100589static void usb_hub_register_types(void)
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200590{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600591 type_register_static(&hub_info);
Gerd Hoffmann806b6022009-08-31 14:23:59 +0200592}
Andreas Färber83f7d432012-02-09 15:20:55 +0100593
594type_init(usb_hub_register_types)