xref: /openbmc/qemu/hw/usb/dev-hub.c (revision 868a4203)
1f1ae32a1SGerd Hoffmann /*
2f1ae32a1SGerd Hoffmann  * QEMU USB HUB emulation
3f1ae32a1SGerd Hoffmann  *
4f1ae32a1SGerd Hoffmann  * Copyright (c) 2005 Fabrice Bellard
5f1ae32a1SGerd Hoffmann  *
6f1ae32a1SGerd Hoffmann  * Permission is hereby granted, free of charge, to any person obtaining a copy
7f1ae32a1SGerd Hoffmann  * of this software and associated documentation files (the "Software"), to deal
8f1ae32a1SGerd Hoffmann  * in the Software without restriction, including without limitation the rights
9f1ae32a1SGerd Hoffmann  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10f1ae32a1SGerd Hoffmann  * copies of the Software, and to permit persons to whom the Software is
11f1ae32a1SGerd Hoffmann  * furnished to do so, subject to the following conditions:
12f1ae32a1SGerd Hoffmann  *
13f1ae32a1SGerd Hoffmann  * The above copyright notice and this permission notice shall be included in
14f1ae32a1SGerd Hoffmann  * all copies or substantial portions of the Software.
15f1ae32a1SGerd Hoffmann  *
16f1ae32a1SGerd Hoffmann  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17f1ae32a1SGerd Hoffmann  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18f1ae32a1SGerd Hoffmann  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19f1ae32a1SGerd Hoffmann  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20f1ae32a1SGerd Hoffmann  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21f1ae32a1SGerd Hoffmann  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22f1ae32a1SGerd Hoffmann  * THE SOFTWARE.
23f1ae32a1SGerd Hoffmann  */
24e532b2e0SPeter Maydell #include "qemu/osdep.h"
25da34e65cSMarkus Armbruster #include "qapi/error.h"
26f1ae32a1SGerd Hoffmann #include "qemu-common.h"
27529f8f9fSGerd Hoffmann #include "trace.h"
28f1ae32a1SGerd Hoffmann #include "hw/usb.h"
29463581a8SMichael S. Tsirkin #include "desc.h"
30c24e4aacSGerd Hoffmann #include "qemu/error-report.h"
31f1ae32a1SGerd Hoffmann 
329d84bb00SGerd Hoffmann #define MAX_PORTS 8
33f1ae32a1SGerd Hoffmann 
34f1ae32a1SGerd Hoffmann typedef struct USBHubPort {
35f1ae32a1SGerd Hoffmann     USBPort port;
36f1ae32a1SGerd Hoffmann     uint16_t wPortStatus;
37f1ae32a1SGerd Hoffmann     uint16_t wPortChange;
38f1ae32a1SGerd Hoffmann } USBHubPort;
39f1ae32a1SGerd Hoffmann 
40f1ae32a1SGerd Hoffmann typedef struct USBHubState {
41f1ae32a1SGerd Hoffmann     USBDevice dev;
42f1ae32a1SGerd Hoffmann     USBEndpoint *intr;
439d84bb00SGerd Hoffmann     uint32_t num_ports;
449d84bb00SGerd Hoffmann     USBHubPort ports[MAX_PORTS];
45f1ae32a1SGerd Hoffmann } USBHubState;
46f1ae32a1SGerd Hoffmann 
47e81b13adSGonglei #define TYPE_USB_HUB "usb-hub"
48e81b13adSGonglei #define USB_HUB(obj) OBJECT_CHECK(USBHubState, (obj), TYPE_USB_HUB)
49e81b13adSGonglei 
50f1ae32a1SGerd Hoffmann #define ClearHubFeature		(0x2000 | USB_REQ_CLEAR_FEATURE)
51f1ae32a1SGerd Hoffmann #define ClearPortFeature	(0x2300 | USB_REQ_CLEAR_FEATURE)
52f1ae32a1SGerd Hoffmann #define GetHubDescriptor	(0xa000 | USB_REQ_GET_DESCRIPTOR)
53f1ae32a1SGerd Hoffmann #define GetHubStatus		(0xa000 | USB_REQ_GET_STATUS)
54f1ae32a1SGerd Hoffmann #define GetPortStatus		(0xa300 | USB_REQ_GET_STATUS)
55f1ae32a1SGerd Hoffmann #define SetHubFeature		(0x2000 | USB_REQ_SET_FEATURE)
56f1ae32a1SGerd Hoffmann #define SetPortFeature		(0x2300 | USB_REQ_SET_FEATURE)
57f1ae32a1SGerd Hoffmann 
58f1ae32a1SGerd Hoffmann #define PORT_STAT_CONNECTION	0x0001
59f1ae32a1SGerd Hoffmann #define PORT_STAT_ENABLE	0x0002
60f1ae32a1SGerd Hoffmann #define PORT_STAT_SUSPEND	0x0004
61f1ae32a1SGerd Hoffmann #define PORT_STAT_OVERCURRENT	0x0008
62f1ae32a1SGerd Hoffmann #define PORT_STAT_RESET		0x0010
63f1ae32a1SGerd Hoffmann #define PORT_STAT_POWER		0x0100
64f1ae32a1SGerd Hoffmann #define PORT_STAT_LOW_SPEED	0x0200
65f1ae32a1SGerd Hoffmann #define PORT_STAT_HIGH_SPEED    0x0400
66f1ae32a1SGerd Hoffmann #define PORT_STAT_TEST          0x0800
67f1ae32a1SGerd Hoffmann #define PORT_STAT_INDICATOR     0x1000
68f1ae32a1SGerd Hoffmann 
69f1ae32a1SGerd Hoffmann #define PORT_STAT_C_CONNECTION	0x0001
70f1ae32a1SGerd Hoffmann #define PORT_STAT_C_ENABLE	0x0002
71f1ae32a1SGerd Hoffmann #define PORT_STAT_C_SUSPEND	0x0004
72f1ae32a1SGerd Hoffmann #define PORT_STAT_C_OVERCURRENT	0x0008
73f1ae32a1SGerd Hoffmann #define PORT_STAT_C_RESET	0x0010
74f1ae32a1SGerd Hoffmann 
75f1ae32a1SGerd Hoffmann #define PORT_CONNECTION	        0
76f1ae32a1SGerd Hoffmann #define PORT_ENABLE		1
77f1ae32a1SGerd Hoffmann #define PORT_SUSPEND		2
78f1ae32a1SGerd Hoffmann #define PORT_OVERCURRENT	3
79f1ae32a1SGerd Hoffmann #define PORT_RESET		4
80f1ae32a1SGerd Hoffmann #define PORT_POWER		8
81f1ae32a1SGerd Hoffmann #define PORT_LOWSPEED		9
82f1ae32a1SGerd Hoffmann #define PORT_HIGHSPEED		10
83f1ae32a1SGerd Hoffmann #define PORT_C_CONNECTION	16
84f1ae32a1SGerd Hoffmann #define PORT_C_ENABLE		17
85f1ae32a1SGerd Hoffmann #define PORT_C_SUSPEND		18
86f1ae32a1SGerd Hoffmann #define PORT_C_OVERCURRENT	19
87f1ae32a1SGerd Hoffmann #define PORT_C_RESET		20
88f1ae32a1SGerd Hoffmann #define PORT_TEST               21
89f1ae32a1SGerd Hoffmann #define PORT_INDICATOR          22
90f1ae32a1SGerd Hoffmann 
91f1ae32a1SGerd Hoffmann /* same as Linux kernel root hubs */
92f1ae32a1SGerd Hoffmann 
93f1ae32a1SGerd Hoffmann enum {
94f1ae32a1SGerd Hoffmann     STR_MANUFACTURER = 1,
95f1ae32a1SGerd Hoffmann     STR_PRODUCT,
96f1ae32a1SGerd Hoffmann     STR_SERIALNUMBER,
97f1ae32a1SGerd Hoffmann };
98f1ae32a1SGerd Hoffmann 
99f1ae32a1SGerd Hoffmann static const USBDescStrings desc_strings = {
10093bfef4cSCrístian Viana     [STR_MANUFACTURER] = "QEMU",
101f1ae32a1SGerd Hoffmann     [STR_PRODUCT]      = "QEMU USB Hub",
102f1ae32a1SGerd Hoffmann     [STR_SERIALNUMBER] = "314159",
103f1ae32a1SGerd Hoffmann };
104f1ae32a1SGerd Hoffmann 
105f1ae32a1SGerd Hoffmann static const USBDescIface desc_iface_hub = {
106f1ae32a1SGerd Hoffmann     .bInterfaceNumber              = 0,
107f1ae32a1SGerd Hoffmann     .bNumEndpoints                 = 1,
108f1ae32a1SGerd Hoffmann     .bInterfaceClass               = USB_CLASS_HUB,
109f1ae32a1SGerd Hoffmann     .eps = (USBDescEndpoint[]) {
110f1ae32a1SGerd Hoffmann         {
111f1ae32a1SGerd Hoffmann             .bEndpointAddress      = USB_DIR_IN | 0x01,
112f1ae32a1SGerd Hoffmann             .bmAttributes          = USB_ENDPOINT_XFER_INT,
1139d84bb00SGerd Hoffmann             .wMaxPacketSize        = 1 + DIV_ROUND_UP(MAX_PORTS, 8),
114f1ae32a1SGerd Hoffmann             .bInterval             = 0xff,
115f1ae32a1SGerd Hoffmann         },
116f1ae32a1SGerd Hoffmann     }
117f1ae32a1SGerd Hoffmann };
118f1ae32a1SGerd Hoffmann 
119f1ae32a1SGerd Hoffmann static const USBDescDevice desc_device_hub = {
120f1ae32a1SGerd Hoffmann     .bcdUSB                        = 0x0110,
121f1ae32a1SGerd Hoffmann     .bDeviceClass                  = USB_CLASS_HUB,
122f1ae32a1SGerd Hoffmann     .bMaxPacketSize0               = 8,
123f1ae32a1SGerd Hoffmann     .bNumConfigurations            = 1,
124f1ae32a1SGerd Hoffmann     .confs = (USBDescConfig[]) {
125f1ae32a1SGerd Hoffmann         {
126f1ae32a1SGerd Hoffmann             .bNumInterfaces        = 1,
127f1ae32a1SGerd Hoffmann             .bConfigurationValue   = 1,
128bd93976aSPantelis Koukousoulas             .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER |
129bd93976aSPantelis Koukousoulas                                      USB_CFG_ATT_WAKEUP,
130f1ae32a1SGerd Hoffmann             .nif = 1,
131f1ae32a1SGerd Hoffmann             .ifs = &desc_iface_hub,
132f1ae32a1SGerd Hoffmann         },
133f1ae32a1SGerd Hoffmann     },
134f1ae32a1SGerd Hoffmann };
135f1ae32a1SGerd Hoffmann 
136f1ae32a1SGerd Hoffmann static const USBDesc desc_hub = {
137f1ae32a1SGerd Hoffmann     .id = {
138f1ae32a1SGerd Hoffmann         .idVendor          = 0x0409,
139f1ae32a1SGerd Hoffmann         .idProduct         = 0x55aa,
140f1ae32a1SGerd Hoffmann         .bcdDevice         = 0x0101,
141f1ae32a1SGerd Hoffmann         .iManufacturer     = STR_MANUFACTURER,
142f1ae32a1SGerd Hoffmann         .iProduct          = STR_PRODUCT,
143f1ae32a1SGerd Hoffmann         .iSerialNumber     = STR_SERIALNUMBER,
144f1ae32a1SGerd Hoffmann     },
145f1ae32a1SGerd Hoffmann     .full = &desc_device_hub,
146f1ae32a1SGerd Hoffmann     .str  = desc_strings,
147f1ae32a1SGerd Hoffmann };
148f1ae32a1SGerd Hoffmann 
149f1ae32a1SGerd Hoffmann static const uint8_t qemu_hub_hub_descriptor[] =
150f1ae32a1SGerd Hoffmann {
151f1ae32a1SGerd Hoffmann         0x00,			/*  u8  bLength; patched in later */
152f1ae32a1SGerd Hoffmann         0x29,			/*  u8  bDescriptorType; Hub-descriptor */
153f1ae32a1SGerd Hoffmann         0x00,			/*  u8  bNbrPorts; (patched later) */
154f1ae32a1SGerd Hoffmann         0x0a,			/* u16  wHubCharacteristics; */
155f1ae32a1SGerd Hoffmann         0x00,			/*   (per-port OC, no power switching) */
156f1ae32a1SGerd Hoffmann         0x01,			/*  u8  bPwrOn2pwrGood; 2ms */
157f1ae32a1SGerd Hoffmann         0x00			/*  u8  bHubContrCurrent; 0 mA */
158f1ae32a1SGerd Hoffmann 
159f1ae32a1SGerd Hoffmann         /* DeviceRemovable and PortPwrCtrlMask patched in later */
160f1ae32a1SGerd Hoffmann };
161f1ae32a1SGerd Hoffmann 
162*868a4203SGerd Hoffmann static bool usb_hub_port_change(USBHubPort *port, uint16_t status)
163*868a4203SGerd Hoffmann {
164*868a4203SGerd Hoffmann     bool notify = false;
165*868a4203SGerd Hoffmann 
166*868a4203SGerd Hoffmann     if (status & 0x1f) {
167*868a4203SGerd Hoffmann         port->wPortChange |= status;
168*868a4203SGerd Hoffmann         notify = true;
169*868a4203SGerd Hoffmann     }
170*868a4203SGerd Hoffmann     return notify;
171*868a4203SGerd Hoffmann }
172*868a4203SGerd Hoffmann 
173*868a4203SGerd Hoffmann static bool usb_hub_port_set(USBHubPort *port, uint16_t status)
174*868a4203SGerd Hoffmann {
175*868a4203SGerd Hoffmann     if (port->wPortStatus & status) {
176*868a4203SGerd Hoffmann         return false;
177*868a4203SGerd Hoffmann     }
178*868a4203SGerd Hoffmann     port->wPortStatus |= status;
179*868a4203SGerd Hoffmann     return usb_hub_port_change(port, status);
180*868a4203SGerd Hoffmann }
181*868a4203SGerd Hoffmann 
182*868a4203SGerd Hoffmann static bool usb_hub_port_clear(USBHubPort *port, uint16_t status)
183*868a4203SGerd Hoffmann {
184*868a4203SGerd Hoffmann     if (!(port->wPortStatus & status)) {
185*868a4203SGerd Hoffmann         return false;
186*868a4203SGerd Hoffmann     }
187*868a4203SGerd Hoffmann     port->wPortStatus &= ~status;
188*868a4203SGerd Hoffmann     return usb_hub_port_change(port, status);
189*868a4203SGerd Hoffmann }
190*868a4203SGerd Hoffmann 
191f1ae32a1SGerd Hoffmann static void usb_hub_attach(USBPort *port1)
192f1ae32a1SGerd Hoffmann {
193f1ae32a1SGerd Hoffmann     USBHubState *s = port1->opaque;
194f1ae32a1SGerd Hoffmann     USBHubPort *port = &s->ports[port1->index];
195f1ae32a1SGerd Hoffmann 
196529f8f9fSGerd Hoffmann     trace_usb_hub_attach(s->dev.addr, port1->index + 1);
197*868a4203SGerd Hoffmann     usb_hub_port_set(port, PORT_STAT_CONNECTION);
198f1ae32a1SGerd Hoffmann     if (port->port.dev->speed == USB_SPEED_LOW) {
199*868a4203SGerd Hoffmann         usb_hub_port_set(port, PORT_STAT_LOW_SPEED);
200f1ae32a1SGerd Hoffmann     } else {
201*868a4203SGerd Hoffmann         usb_hub_port_clear(port, PORT_STAT_LOW_SPEED);
202f1ae32a1SGerd Hoffmann     }
2038550a02dSGerd Hoffmann     usb_wakeup(s->intr, 0);
204f1ae32a1SGerd Hoffmann }
205f1ae32a1SGerd Hoffmann 
206f1ae32a1SGerd Hoffmann static void usb_hub_detach(USBPort *port1)
207f1ae32a1SGerd Hoffmann {
208f1ae32a1SGerd Hoffmann     USBHubState *s = port1->opaque;
209f1ae32a1SGerd Hoffmann     USBHubPort *port = &s->ports[port1->index];
210f1ae32a1SGerd Hoffmann 
211529f8f9fSGerd Hoffmann     trace_usb_hub_detach(s->dev.addr, port1->index + 1);
2128550a02dSGerd Hoffmann     usb_wakeup(s->intr, 0);
213f1ae32a1SGerd Hoffmann 
214f1ae32a1SGerd Hoffmann     /* Let upstream know the device on this port is gone */
215f1ae32a1SGerd Hoffmann     s->dev.port->ops->child_detach(s->dev.port, port1->dev);
216f1ae32a1SGerd Hoffmann 
217*868a4203SGerd Hoffmann     usb_hub_port_clear(port, PORT_STAT_CONNECTION);
218*868a4203SGerd Hoffmann     usb_hub_port_clear(port, PORT_STAT_ENABLE);
219*868a4203SGerd Hoffmann     usb_hub_port_clear(port, PORT_STAT_SUSPEND);
2208550a02dSGerd Hoffmann     usb_wakeup(s->intr, 0);
221f1ae32a1SGerd Hoffmann }
222f1ae32a1SGerd Hoffmann 
223f1ae32a1SGerd Hoffmann static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
224f1ae32a1SGerd Hoffmann {
225f1ae32a1SGerd Hoffmann     USBHubState *s = port1->opaque;
226f1ae32a1SGerd Hoffmann 
227f1ae32a1SGerd Hoffmann     /* Pass along upstream */
228f1ae32a1SGerd Hoffmann     s->dev.port->ops->child_detach(s->dev.port, child);
229f1ae32a1SGerd Hoffmann }
230f1ae32a1SGerd Hoffmann 
231f1ae32a1SGerd Hoffmann static void usb_hub_wakeup(USBPort *port1)
232f1ae32a1SGerd Hoffmann {
233f1ae32a1SGerd Hoffmann     USBHubState *s = port1->opaque;
234f1ae32a1SGerd Hoffmann     USBHubPort *port = &s->ports[port1->index];
235f1ae32a1SGerd Hoffmann 
236*868a4203SGerd Hoffmann     if (usb_hub_port_clear(port, PORT_STAT_SUSPEND)) {
2378550a02dSGerd Hoffmann         usb_wakeup(s->intr, 0);
238f1ae32a1SGerd Hoffmann     }
239f1ae32a1SGerd Hoffmann }
240f1ae32a1SGerd Hoffmann 
241f1ae32a1SGerd Hoffmann static void usb_hub_complete(USBPort *port, USBPacket *packet)
242f1ae32a1SGerd Hoffmann {
243f1ae32a1SGerd Hoffmann     USBHubState *s = port->opaque;
244f1ae32a1SGerd Hoffmann 
245f1ae32a1SGerd Hoffmann     /*
246f1ae32a1SGerd Hoffmann      * Just pass it along upstream for now.
247f1ae32a1SGerd Hoffmann      *
248f1ae32a1SGerd Hoffmann      * If we ever implement usb 2.0 split transactions this will
249f1ae32a1SGerd Hoffmann      * become a little more complicated ...
250f1ae32a1SGerd Hoffmann      *
251f1ae32a1SGerd Hoffmann      * Can't use usb_packet_complete() here because packet->owner is
252f1ae32a1SGerd Hoffmann      * cleared already, go call the ->complete() callback directly
253f1ae32a1SGerd Hoffmann      * instead.
254f1ae32a1SGerd Hoffmann      */
255f1ae32a1SGerd Hoffmann     s->dev.port->ops->complete(s->dev.port, packet);
256f1ae32a1SGerd Hoffmann }
257f1ae32a1SGerd Hoffmann 
258f1ae32a1SGerd Hoffmann static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
259f1ae32a1SGerd Hoffmann {
260e81b13adSGonglei     USBHubState *s = USB_HUB(dev);
261f1ae32a1SGerd Hoffmann     USBHubPort *port;
262f1ae32a1SGerd Hoffmann     USBDevice *downstream;
263f1ae32a1SGerd Hoffmann     int i;
264f1ae32a1SGerd Hoffmann 
2659d84bb00SGerd Hoffmann     for (i = 0; i < s->num_ports; i++) {
266f1ae32a1SGerd Hoffmann         port = &s->ports[i];
267f1ae32a1SGerd Hoffmann         if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
268f1ae32a1SGerd Hoffmann             continue;
269f1ae32a1SGerd Hoffmann         }
270f1ae32a1SGerd Hoffmann         downstream = usb_find_device(&port->port, addr);
271f1ae32a1SGerd Hoffmann         if (downstream != NULL) {
272f1ae32a1SGerd Hoffmann             return downstream;
273f1ae32a1SGerd Hoffmann         }
274f1ae32a1SGerd Hoffmann     }
275f1ae32a1SGerd Hoffmann     return NULL;
276f1ae32a1SGerd Hoffmann }
277f1ae32a1SGerd Hoffmann 
278f1ae32a1SGerd Hoffmann static void usb_hub_handle_reset(USBDevice *dev)
279f1ae32a1SGerd Hoffmann {
280e81b13adSGonglei     USBHubState *s = USB_HUB(dev);
281f1ae32a1SGerd Hoffmann     USBHubPort *port;
282f1ae32a1SGerd Hoffmann     int i;
283f1ae32a1SGerd Hoffmann 
284529f8f9fSGerd Hoffmann     trace_usb_hub_reset(s->dev.addr);
2859d84bb00SGerd Hoffmann     for (i = 0; i < s->num_ports; i++) {
286f1ae32a1SGerd Hoffmann         port = s->ports + i;
287*868a4203SGerd Hoffmann         port->wPortStatus = 0;
288f1ae32a1SGerd Hoffmann         port->wPortChange = 0;
289*868a4203SGerd Hoffmann         usb_hub_port_set(port, PORT_STAT_POWER);
290f1ae32a1SGerd Hoffmann         if (port->port.dev && port->port.dev->attached) {
291*868a4203SGerd Hoffmann             usb_hub_port_set(port, PORT_STAT_CONNECTION);
292f1ae32a1SGerd Hoffmann             if (port->port.dev->speed == USB_SPEED_LOW) {
293*868a4203SGerd Hoffmann                 usb_hub_port_set(port, PORT_STAT_LOW_SPEED);
294f1ae32a1SGerd Hoffmann             }
295f1ae32a1SGerd Hoffmann         }
296f1ae32a1SGerd Hoffmann     }
297f1ae32a1SGerd Hoffmann }
298f1ae32a1SGerd Hoffmann 
299529f8f9fSGerd Hoffmann static const char *feature_name(int feature)
300529f8f9fSGerd Hoffmann {
301529f8f9fSGerd Hoffmann     static const char *name[] = {
302529f8f9fSGerd Hoffmann         [PORT_CONNECTION]    = "connection",
303529f8f9fSGerd Hoffmann         [PORT_ENABLE]        = "enable",
304529f8f9fSGerd Hoffmann         [PORT_SUSPEND]       = "suspend",
305529f8f9fSGerd Hoffmann         [PORT_OVERCURRENT]   = "overcurrent",
306529f8f9fSGerd Hoffmann         [PORT_RESET]         = "reset",
307529f8f9fSGerd Hoffmann         [PORT_POWER]         = "power",
308529f8f9fSGerd Hoffmann         [PORT_LOWSPEED]      = "lowspeed",
309529f8f9fSGerd Hoffmann         [PORT_HIGHSPEED]     = "highspeed",
310bdb88a8eSGerd Hoffmann         [PORT_C_CONNECTION]  = "change-connection",
311bdb88a8eSGerd Hoffmann         [PORT_C_ENABLE]      = "change-enable",
312bdb88a8eSGerd Hoffmann         [PORT_C_SUSPEND]     = "change-suspend",
313bdb88a8eSGerd Hoffmann         [PORT_C_OVERCURRENT] = "change-overcurrent",
314bdb88a8eSGerd Hoffmann         [PORT_C_RESET]       = "change-reset",
315529f8f9fSGerd Hoffmann         [PORT_TEST]          = "test",
316529f8f9fSGerd Hoffmann         [PORT_INDICATOR]     = "indicator",
317529f8f9fSGerd Hoffmann     };
318529f8f9fSGerd Hoffmann     if (feature < 0 || feature >= ARRAY_SIZE(name)) {
319529f8f9fSGerd Hoffmann         return "?";
320529f8f9fSGerd Hoffmann     }
321529f8f9fSGerd Hoffmann     return name[feature] ?: "?";
322529f8f9fSGerd Hoffmann }
323529f8f9fSGerd Hoffmann 
3249a77a0f5SHans de Goede static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
325f1ae32a1SGerd Hoffmann                int request, int value, int index, int length, uint8_t *data)
326f1ae32a1SGerd Hoffmann {
327f1ae32a1SGerd Hoffmann     USBHubState *s = (USBHubState *)dev;
328f1ae32a1SGerd Hoffmann     int ret;
329f1ae32a1SGerd Hoffmann 
330529f8f9fSGerd Hoffmann     trace_usb_hub_control(s->dev.addr, request, value, index, length);
331529f8f9fSGerd Hoffmann 
332f1ae32a1SGerd Hoffmann     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
333f1ae32a1SGerd Hoffmann     if (ret >= 0) {
3349a77a0f5SHans de Goede         return;
335f1ae32a1SGerd Hoffmann     }
336f1ae32a1SGerd Hoffmann 
337f1ae32a1SGerd Hoffmann     switch(request) {
338f1ae32a1SGerd Hoffmann     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
339f1ae32a1SGerd Hoffmann         if (value == 0 && index != 0x81) { /* clear ep halt */
340f1ae32a1SGerd Hoffmann             goto fail;
341f1ae32a1SGerd Hoffmann         }
342f1ae32a1SGerd Hoffmann         break;
343f1ae32a1SGerd Hoffmann         /* usb specific requests */
344f1ae32a1SGerd Hoffmann     case GetHubStatus:
345f1ae32a1SGerd Hoffmann         data[0] = 0;
346f1ae32a1SGerd Hoffmann         data[1] = 0;
347f1ae32a1SGerd Hoffmann         data[2] = 0;
348f1ae32a1SGerd Hoffmann         data[3] = 0;
3499a77a0f5SHans de Goede         p->actual_length = 4;
350f1ae32a1SGerd Hoffmann         break;
351f1ae32a1SGerd Hoffmann     case GetPortStatus:
352f1ae32a1SGerd Hoffmann         {
353f1ae32a1SGerd Hoffmann             unsigned int n = index - 1;
354f1ae32a1SGerd Hoffmann             USBHubPort *port;
3559d84bb00SGerd Hoffmann             if (n >= s->num_ports) {
356f1ae32a1SGerd Hoffmann                 goto fail;
357f1ae32a1SGerd Hoffmann             }
358f1ae32a1SGerd Hoffmann             port = &s->ports[n];
359529f8f9fSGerd Hoffmann             trace_usb_hub_get_port_status(s->dev.addr, index,
360529f8f9fSGerd Hoffmann                                           port->wPortStatus,
361529f8f9fSGerd Hoffmann                                           port->wPortChange);
362f1ae32a1SGerd Hoffmann             data[0] = port->wPortStatus;
363f1ae32a1SGerd Hoffmann             data[1] = port->wPortStatus >> 8;
364f1ae32a1SGerd Hoffmann             data[2] = port->wPortChange;
365f1ae32a1SGerd Hoffmann             data[3] = port->wPortChange >> 8;
3669a77a0f5SHans de Goede             p->actual_length = 4;
367f1ae32a1SGerd Hoffmann         }
368f1ae32a1SGerd Hoffmann         break;
369f1ae32a1SGerd Hoffmann     case SetHubFeature:
370f1ae32a1SGerd Hoffmann     case ClearHubFeature:
3719a77a0f5SHans de Goede         if (value != 0 && value != 1) {
372f1ae32a1SGerd Hoffmann             goto fail;
373f1ae32a1SGerd Hoffmann         }
374f1ae32a1SGerd Hoffmann         break;
375f1ae32a1SGerd Hoffmann     case SetPortFeature:
376f1ae32a1SGerd Hoffmann         {
377f1ae32a1SGerd Hoffmann             unsigned int n = index - 1;
378f1ae32a1SGerd Hoffmann             USBHubPort *port;
379f1ae32a1SGerd Hoffmann             USBDevice *dev;
380529f8f9fSGerd Hoffmann 
381529f8f9fSGerd Hoffmann             trace_usb_hub_set_port_feature(s->dev.addr, index,
382529f8f9fSGerd Hoffmann                                            feature_name(value));
383529f8f9fSGerd Hoffmann 
3849d84bb00SGerd Hoffmann             if (n >= s->num_ports) {
385f1ae32a1SGerd Hoffmann                 goto fail;
386f1ae32a1SGerd Hoffmann             }
387f1ae32a1SGerd Hoffmann             port = &s->ports[n];
388f1ae32a1SGerd Hoffmann             dev = port->port.dev;
389f1ae32a1SGerd Hoffmann             switch(value) {
390f1ae32a1SGerd Hoffmann             case PORT_SUSPEND:
391f1ae32a1SGerd Hoffmann                 port->wPortStatus |= PORT_STAT_SUSPEND;
392f1ae32a1SGerd Hoffmann                 break;
393f1ae32a1SGerd Hoffmann             case PORT_RESET:
394*868a4203SGerd Hoffmann                 usb_hub_port_set(port, PORT_STAT_RESET);
395*868a4203SGerd Hoffmann                 usb_hub_port_clear(port, PORT_STAT_RESET);
396f1ae32a1SGerd Hoffmann                 if (dev && dev->attached) {
397f1ae32a1SGerd Hoffmann                     usb_device_reset(dev);
398*868a4203SGerd Hoffmann                     usb_hub_port_set(port, PORT_STAT_ENABLE);
399f1ae32a1SGerd Hoffmann                 }
400*868a4203SGerd Hoffmann                 usb_wakeup(s->intr, 0);
401f1ae32a1SGerd Hoffmann                 break;
402f1ae32a1SGerd Hoffmann             case PORT_POWER:
403f1ae32a1SGerd Hoffmann                 break;
404f1ae32a1SGerd Hoffmann             default:
405f1ae32a1SGerd Hoffmann                 goto fail;
406f1ae32a1SGerd Hoffmann             }
407f1ae32a1SGerd Hoffmann         }
408f1ae32a1SGerd Hoffmann         break;
409f1ae32a1SGerd Hoffmann     case ClearPortFeature:
410f1ae32a1SGerd Hoffmann         {
411f1ae32a1SGerd Hoffmann             unsigned int n = index - 1;
412f1ae32a1SGerd Hoffmann             USBHubPort *port;
413f1ae32a1SGerd Hoffmann 
414529f8f9fSGerd Hoffmann             trace_usb_hub_clear_port_feature(s->dev.addr, index,
415529f8f9fSGerd Hoffmann                                              feature_name(value));
416529f8f9fSGerd Hoffmann 
4179d84bb00SGerd Hoffmann             if (n >= s->num_ports) {
418f1ae32a1SGerd Hoffmann                 goto fail;
419f1ae32a1SGerd Hoffmann             }
420f1ae32a1SGerd Hoffmann             port = &s->ports[n];
421f1ae32a1SGerd Hoffmann             switch(value) {
422f1ae32a1SGerd Hoffmann             case PORT_ENABLE:
423f1ae32a1SGerd Hoffmann                 port->wPortStatus &= ~PORT_STAT_ENABLE;
424f1ae32a1SGerd Hoffmann                 break;
425f1ae32a1SGerd Hoffmann             case PORT_C_ENABLE:
426f1ae32a1SGerd Hoffmann                 port->wPortChange &= ~PORT_STAT_C_ENABLE;
427f1ae32a1SGerd Hoffmann                 break;
428f1ae32a1SGerd Hoffmann             case PORT_SUSPEND:
429*868a4203SGerd Hoffmann                 usb_hub_port_clear(port, PORT_STAT_SUSPEND);
430f1ae32a1SGerd Hoffmann                 break;
431f1ae32a1SGerd Hoffmann             case PORT_C_SUSPEND:
432f1ae32a1SGerd Hoffmann                 port->wPortChange &= ~PORT_STAT_C_SUSPEND;
433f1ae32a1SGerd Hoffmann                 break;
434f1ae32a1SGerd Hoffmann             case PORT_C_CONNECTION:
435f1ae32a1SGerd Hoffmann                 port->wPortChange &= ~PORT_STAT_C_CONNECTION;
436f1ae32a1SGerd Hoffmann                 break;
437f1ae32a1SGerd Hoffmann             case PORT_C_OVERCURRENT:
438f1ae32a1SGerd Hoffmann                 port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
439f1ae32a1SGerd Hoffmann                 break;
440f1ae32a1SGerd Hoffmann             case PORT_C_RESET:
441f1ae32a1SGerd Hoffmann                 port->wPortChange &= ~PORT_STAT_C_RESET;
442f1ae32a1SGerd Hoffmann                 break;
443f1ae32a1SGerd Hoffmann             default:
444f1ae32a1SGerd Hoffmann                 goto fail;
445f1ae32a1SGerd Hoffmann             }
446f1ae32a1SGerd Hoffmann         }
447f1ae32a1SGerd Hoffmann         break;
448f1ae32a1SGerd Hoffmann     case GetHubDescriptor:
449f1ae32a1SGerd Hoffmann         {
450f1ae32a1SGerd Hoffmann             unsigned int n, limit, var_hub_size = 0;
451f1ae32a1SGerd Hoffmann             memcpy(data, qemu_hub_hub_descriptor,
452f1ae32a1SGerd Hoffmann                    sizeof(qemu_hub_hub_descriptor));
4539d84bb00SGerd Hoffmann             data[2] = s->num_ports;
454f1ae32a1SGerd Hoffmann 
455f1ae32a1SGerd Hoffmann             /* fill DeviceRemovable bits */
4569d84bb00SGerd Hoffmann             limit = DIV_ROUND_UP(s->num_ports + 1, 8) + 7;
457f1ae32a1SGerd Hoffmann             for (n = 7; n < limit; n++) {
458f1ae32a1SGerd Hoffmann                 data[n] = 0x00;
459f1ae32a1SGerd Hoffmann                 var_hub_size++;
460f1ae32a1SGerd Hoffmann             }
461f1ae32a1SGerd Hoffmann 
462f1ae32a1SGerd Hoffmann             /* fill PortPwrCtrlMask bits */
4639d84bb00SGerd Hoffmann             limit = limit + DIV_ROUND_UP(s->num_ports, 8);
464f1ae32a1SGerd Hoffmann             for (;n < limit; n++) {
465f1ae32a1SGerd Hoffmann                 data[n] = 0xff;
466f1ae32a1SGerd Hoffmann                 var_hub_size++;
467f1ae32a1SGerd Hoffmann             }
468f1ae32a1SGerd Hoffmann 
4699a77a0f5SHans de Goede             p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
4709a77a0f5SHans de Goede             data[0] = p->actual_length;
471f1ae32a1SGerd Hoffmann             break;
472f1ae32a1SGerd Hoffmann         }
473f1ae32a1SGerd Hoffmann     default:
474f1ae32a1SGerd Hoffmann     fail:
4759a77a0f5SHans de Goede         p->status = USB_RET_STALL;
476f1ae32a1SGerd Hoffmann         break;
477f1ae32a1SGerd Hoffmann     }
478f1ae32a1SGerd Hoffmann }
479f1ae32a1SGerd Hoffmann 
4809a77a0f5SHans de Goede static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
481f1ae32a1SGerd Hoffmann {
482f1ae32a1SGerd Hoffmann     USBHubState *s = (USBHubState *)dev;
483f1ae32a1SGerd Hoffmann 
484f1ae32a1SGerd Hoffmann     switch(p->pid) {
485f1ae32a1SGerd Hoffmann     case USB_TOKEN_IN:
486f1ae32a1SGerd Hoffmann         if (p->ep->nr == 1) {
487f1ae32a1SGerd Hoffmann             USBHubPort *port;
488f1ae32a1SGerd Hoffmann             unsigned int status;
489f1ae32a1SGerd Hoffmann             uint8_t buf[4];
490f1ae32a1SGerd Hoffmann             int i, n;
4919d84bb00SGerd Hoffmann             n = DIV_ROUND_UP(s->num_ports + 1, 8);
492f1ae32a1SGerd Hoffmann             if (p->iov.size == 1) { /* FreeBSD workaround */
493f1ae32a1SGerd Hoffmann                 n = 1;
494f1ae32a1SGerd Hoffmann             } else if (n > p->iov.size) {
4959a77a0f5SHans de Goede                 p->status = USB_RET_BABBLE;
4969a77a0f5SHans de Goede                 return;
497f1ae32a1SGerd Hoffmann             }
498f1ae32a1SGerd Hoffmann             status = 0;
4999d84bb00SGerd Hoffmann             for (i = 0; i < s->num_ports; i++) {
500f1ae32a1SGerd Hoffmann                 port = &s->ports[i];
501bdebd6eeSGerd Hoffmann                 if (port->wPortChange)
502f1ae32a1SGerd Hoffmann                     status |= (1 << (i + 1));
503f1ae32a1SGerd Hoffmann             }
504f1ae32a1SGerd Hoffmann             if (status != 0) {
505b8cbc137SGerd Hoffmann                 trace_usb_hub_status_report(s->dev.addr, status);
506f1ae32a1SGerd Hoffmann                 for(i = 0; i < n; i++) {
507f1ae32a1SGerd Hoffmann                     buf[i] = status >> (8 * i);
508f1ae32a1SGerd Hoffmann                 }
509f1ae32a1SGerd Hoffmann                 usb_packet_copy(p, buf, n);
510f1ae32a1SGerd Hoffmann             } else {
5119a77a0f5SHans de Goede                 p->status = USB_RET_NAK; /* usb11 11.13.1 */
512f1ae32a1SGerd Hoffmann             }
513f1ae32a1SGerd Hoffmann         } else {
514f1ae32a1SGerd Hoffmann             goto fail;
515f1ae32a1SGerd Hoffmann         }
516f1ae32a1SGerd Hoffmann         break;
517f1ae32a1SGerd Hoffmann     case USB_TOKEN_OUT:
518f1ae32a1SGerd Hoffmann     default:
519f1ae32a1SGerd Hoffmann     fail:
5209a77a0f5SHans de Goede         p->status = USB_RET_STALL;
521f1ae32a1SGerd Hoffmann         break;
522f1ae32a1SGerd Hoffmann     }
523f1ae32a1SGerd Hoffmann }
524f1ae32a1SGerd Hoffmann 
525c4fe9700SMarc-André Lureau static void usb_hub_unrealize(USBDevice *dev, Error **errp)
526f1ae32a1SGerd Hoffmann {
527f1ae32a1SGerd Hoffmann     USBHubState *s = (USBHubState *)dev;
528f1ae32a1SGerd Hoffmann     int i;
529f1ae32a1SGerd Hoffmann 
5309d84bb00SGerd Hoffmann     for (i = 0; i < s->num_ports; i++) {
531f1ae32a1SGerd Hoffmann         usb_unregister_port(usb_bus_from_device(dev),
532f1ae32a1SGerd Hoffmann                             &s->ports[i].port);
533f1ae32a1SGerd Hoffmann     }
534f1ae32a1SGerd Hoffmann }
535f1ae32a1SGerd Hoffmann 
536f1ae32a1SGerd Hoffmann static USBPortOps usb_hub_port_ops = {
537f1ae32a1SGerd Hoffmann     .attach = usb_hub_attach,
538f1ae32a1SGerd Hoffmann     .detach = usb_hub_detach,
539f1ae32a1SGerd Hoffmann     .child_detach = usb_hub_child_detach,
540f1ae32a1SGerd Hoffmann     .wakeup = usb_hub_wakeup,
541f1ae32a1SGerd Hoffmann     .complete = usb_hub_complete,
542f1ae32a1SGerd Hoffmann };
543f1ae32a1SGerd Hoffmann 
544f3f8c459SGonglei static void usb_hub_realize(USBDevice *dev, Error **errp)
545f1ae32a1SGerd Hoffmann {
546e81b13adSGonglei     USBHubState *s = USB_HUB(dev);
547f1ae32a1SGerd Hoffmann     USBHubPort *port;
548f1ae32a1SGerd Hoffmann     int i;
549f1ae32a1SGerd Hoffmann 
5509d84bb00SGerd Hoffmann     if (s->num_ports < 1 || s->num_ports > MAX_PORTS) {
5519d84bb00SGerd Hoffmann         error_setg(errp, "num_ports (%d) out of range (1..%d)",
5529d84bb00SGerd Hoffmann                    s->num_ports, MAX_PORTS);
5539d84bb00SGerd Hoffmann         return;
5549d84bb00SGerd Hoffmann     }
5559d84bb00SGerd Hoffmann 
556c24e4aacSGerd Hoffmann     if (dev->port->hubcount == 5) {
557f3f8c459SGonglei         error_setg(errp, "usb hub chain too deep");
558f3f8c459SGonglei         return;
559c24e4aacSGerd Hoffmann     }
560c24e4aacSGerd Hoffmann 
5619d55d1adSGerd Hoffmann     usb_desc_create_serial(dev);
562f1ae32a1SGerd Hoffmann     usb_desc_init(dev);
563f1ae32a1SGerd Hoffmann     s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
5649d84bb00SGerd Hoffmann     for (i = 0; i < s->num_ports; i++) {
565f1ae32a1SGerd Hoffmann         port = &s->ports[i];
566f1ae32a1SGerd Hoffmann         usb_register_port(usb_bus_from_device(dev),
567f1ae32a1SGerd Hoffmann                           &port->port, s, i, &usb_hub_port_ops,
568f1ae32a1SGerd Hoffmann                           USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
569f1ae32a1SGerd Hoffmann         usb_port_location(&port->port, dev->port, i+1);
570f1ae32a1SGerd Hoffmann     }
571f1ae32a1SGerd Hoffmann     usb_hub_handle_reset(dev);
572f1ae32a1SGerd Hoffmann }
573f1ae32a1SGerd Hoffmann 
574f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_hub_port = {
575f1ae32a1SGerd Hoffmann     .name = "usb-hub-port",
576f1ae32a1SGerd Hoffmann     .version_id = 1,
577f1ae32a1SGerd Hoffmann     .minimum_version_id = 1,
578f1ae32a1SGerd Hoffmann     .fields = (VMStateField[]) {
579f1ae32a1SGerd Hoffmann         VMSTATE_UINT16(wPortStatus, USBHubPort),
580f1ae32a1SGerd Hoffmann         VMSTATE_UINT16(wPortChange, USBHubPort),
581f1ae32a1SGerd Hoffmann         VMSTATE_END_OF_LIST()
582f1ae32a1SGerd Hoffmann     }
583f1ae32a1SGerd Hoffmann };
584f1ae32a1SGerd Hoffmann 
585f1ae32a1SGerd Hoffmann static const VMStateDescription vmstate_usb_hub = {
586f1ae32a1SGerd Hoffmann     .name = "usb-hub",
587f1ae32a1SGerd Hoffmann     .version_id = 1,
588f1ae32a1SGerd Hoffmann     .minimum_version_id = 1,
589f1ae32a1SGerd Hoffmann     .fields = (VMStateField[]) {
590f1ae32a1SGerd Hoffmann         VMSTATE_USB_DEVICE(dev, USBHubState),
5919d84bb00SGerd Hoffmann         VMSTATE_STRUCT_ARRAY(ports, USBHubState, MAX_PORTS, 0,
592f1ae32a1SGerd Hoffmann                              vmstate_usb_hub_port, USBHubPort),
593f1ae32a1SGerd Hoffmann         VMSTATE_END_OF_LIST()
594f1ae32a1SGerd Hoffmann     }
595f1ae32a1SGerd Hoffmann };
596f1ae32a1SGerd Hoffmann 
5979d84bb00SGerd Hoffmann static Property usb_hub_properties[] = {
5989d84bb00SGerd Hoffmann     DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8),
5999d84bb00SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
6009d84bb00SGerd Hoffmann };
6019d84bb00SGerd Hoffmann 
602f1ae32a1SGerd Hoffmann static void usb_hub_class_initfn(ObjectClass *klass, void *data)
603f1ae32a1SGerd Hoffmann {
604f1ae32a1SGerd Hoffmann     DeviceClass *dc = DEVICE_CLASS(klass);
605f1ae32a1SGerd Hoffmann     USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
606f1ae32a1SGerd Hoffmann 
607f3f8c459SGonglei     uc->realize        = usb_hub_realize;
608f1ae32a1SGerd Hoffmann     uc->product_desc   = "QEMU USB Hub";
609f1ae32a1SGerd Hoffmann     uc->usb_desc       = &desc_hub;
610f1ae32a1SGerd Hoffmann     uc->find_device    = usb_hub_find_device;
611f1ae32a1SGerd Hoffmann     uc->handle_reset   = usb_hub_handle_reset;
612f1ae32a1SGerd Hoffmann     uc->handle_control = usb_hub_handle_control;
613f1ae32a1SGerd Hoffmann     uc->handle_data    = usb_hub_handle_data;
614c4fe9700SMarc-André Lureau     uc->unrealize      = usb_hub_unrealize;
615125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
616f1ae32a1SGerd Hoffmann     dc->fw_name = "hub";
617f1ae32a1SGerd Hoffmann     dc->vmsd = &vmstate_usb_hub;
6189d84bb00SGerd Hoffmann     dc->props = usb_hub_properties;
619f1ae32a1SGerd Hoffmann }
620f1ae32a1SGerd Hoffmann 
6218c43a6f0SAndreas Färber static const TypeInfo hub_info = {
622e81b13adSGonglei     .name          = TYPE_USB_HUB,
623f1ae32a1SGerd Hoffmann     .parent        = TYPE_USB_DEVICE,
624f1ae32a1SGerd Hoffmann     .instance_size = sizeof(USBHubState),
625f1ae32a1SGerd Hoffmann     .class_init    = usb_hub_class_initfn,
626f1ae32a1SGerd Hoffmann };
627f1ae32a1SGerd Hoffmann 
628f1ae32a1SGerd Hoffmann static void usb_hub_register_types(void)
629f1ae32a1SGerd Hoffmann {
630f1ae32a1SGerd Hoffmann     type_register_static(&hub_info);
631f1ae32a1SGerd Hoffmann }
632f1ae32a1SGerd Hoffmann 
633f1ae32a1SGerd Hoffmann type_init(usb_hub_register_types)
634