source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/hw/usb-hub.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 16.8 KB
Line 
1/*
2 * QEMU USB HUB emulation
3 *
4 * Copyright (c) 2005 Fabrice Bellard
5 *
6 * 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 */
24#include "vl.h"
25
26//#define DEBUG
27
28#define MAX_PORTS 8
29
30typedef struct USBHubPort {
31    USBPort port;
32    uint16_t wPortStatus;
33    uint16_t wPortChange;
34} USBHubPort;
35
36typedef struct USBHubState {
37    USBDevice dev;
38    int nb_ports;
39    USBHubPort ports[MAX_PORTS];
40} USBHubState;
41
42#define ClearHubFeature         (0x2000 | USB_REQ_CLEAR_FEATURE)
43#define ClearPortFeature        (0x2300 | USB_REQ_CLEAR_FEATURE)
44#define GetHubDescriptor        (0xa000 | USB_REQ_GET_DESCRIPTOR)
45#define GetHubStatus            (0xa000 | USB_REQ_GET_STATUS)
46#define GetPortStatus           (0xa300 | USB_REQ_GET_STATUS)
47#define SetHubFeature           (0x2000 | USB_REQ_SET_FEATURE)
48#define SetPortFeature          (0x2300 | USB_REQ_SET_FEATURE)
49
50#define PORT_STAT_CONNECTION    0x0001
51#define PORT_STAT_ENABLE        0x0002
52#define PORT_STAT_SUSPEND       0x0004
53#define PORT_STAT_OVERCURRENT   0x0008
54#define PORT_STAT_RESET         0x0010
55#define PORT_STAT_POWER         0x0100
56#define PORT_STAT_LOW_SPEED     0x0200
57#define PORT_STAT_HIGH_SPEED    0x0400
58#define PORT_STAT_TEST          0x0800
59#define PORT_STAT_INDICATOR     0x1000
60
61#define PORT_STAT_C_CONNECTION  0x0001
62#define PORT_STAT_C_ENABLE      0x0002
63#define PORT_STAT_C_SUSPEND     0x0004
64#define PORT_STAT_C_OVERCURRENT 0x0008
65#define PORT_STAT_C_RESET       0x0010
66
67#define PORT_CONNECTION         0
68#define PORT_ENABLE             1
69#define PORT_SUSPEND            2
70#define PORT_OVERCURRENT        3
71#define PORT_RESET              4
72#define PORT_POWER              8
73#define PORT_LOWSPEED           9
74#define PORT_HIGHSPEED          10
75#define PORT_C_CONNECTION       16
76#define PORT_C_ENABLE           17
77#define PORT_C_SUSPEND          18
78#define PORT_C_OVERCURRENT      19
79#define PORT_C_RESET            20
80#define PORT_TEST               21
81#define PORT_INDICATOR          22
82
83/* same as Linux kernel root hubs */
84
85static const uint8_t qemu_hub_dev_descriptor[] = {
86        0x12,       /*  u8 bLength; */
87        0x01,       /*  u8 bDescriptorType; Device */
88        0x10, 0x01, /*  u16 bcdUSB; v1.1 */
89
90        0x09,       /*  u8  bDeviceClass; HUB_CLASSCODE */
91        0x00,       /*  u8  bDeviceSubClass; */
92        0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
93        0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
94
95        0x00, 0x00, /*  u16 idVendor; */
96        0x00, 0x00, /*  u16 idProduct; */
97        0x01, 0x01, /*  u16 bcdDevice */
98
99        0x03,       /*  u8  iManufacturer; */
100        0x02,       /*  u8  iProduct; */
101        0x01,       /*  u8  iSerialNumber; */
102        0x01        /*  u8  bNumConfigurations; */
103};
104
105/* XXX: patch interrupt size */
106static const uint8_t qemu_hub_config_descriptor[] = {
107
108        /* one configuration */
109        0x09,       /*  u8  bLength; */
110        0x02,       /*  u8  bDescriptorType; Configuration */
111        0x19, 0x00, /*  u16 wTotalLength; */
112        0x01,       /*  u8  bNumInterfaces; (1) */
113        0x01,       /*  u8  bConfigurationValue; */
114        0x00,       /*  u8  iConfiguration; */
115        0xc0,       /*  u8  bmAttributes;
116                                 Bit 7: must be set,
117                                     6: Self-powered,
118                                     5: Remote wakeup,
119                                     4..0: resvd */
120        0x00,       /*  u8  MaxPower; */
121     
122        /* USB 1.1:
123         * USB 2.0, single TT organization (mandatory):
124         *      one interface, protocol 0
125         *
126         * USB 2.0, multiple TT organization (optional):
127         *      two interfaces, protocols 1 (like single TT)
128         *      and 2 (multiple TT mode) ... config is
129         *      sometimes settable
130         *      NOT IMPLEMENTED
131         */
132
133        /* one interface */
134        0x09,       /*  u8  if_bLength; */
135        0x04,       /*  u8  if_bDescriptorType; Interface */
136        0x00,       /*  u8  if_bInterfaceNumber; */
137        0x00,       /*  u8  if_bAlternateSetting; */
138        0x01,       /*  u8  if_bNumEndpoints; */
139        0x09,       /*  u8  if_bInterfaceClass; HUB_CLASSCODE */
140        0x00,       /*  u8  if_bInterfaceSubClass; */
141        0x00,       /*  u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
142        0x00,       /*  u8  if_iInterface; */
143     
144        /* one endpoint (status change endpoint) */
145        0x07,       /*  u8  ep_bLength; */
146        0x05,       /*  u8  ep_bDescriptorType; Endpoint */
147        0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
148        0x03,       /*  u8  ep_bmAttributes; Interrupt */
149        0x02, 0x00, /*  u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
150        0xff        /*  u8  ep_bInterval; (255ms -- usb 2.0 spec) */
151};
152
153static const uint8_t qemu_hub_hub_descriptor[] =
154{
155        0x00,                   /*  u8  bLength; patched in later */
156        0x29,                   /*  u8  bDescriptorType; Hub-descriptor */
157        0x00,                   /*  u8  bNbrPorts; (patched later) */
158        0x0a,                   /* u16  wHubCharacteristics; */
159        0x00,                   /*   (per-port OC, no power switching) */
160        0x01,                   /*  u8  bPwrOn2pwrGood; 2ms */
161        0x00                    /*  u8  bHubContrCurrent; 0 mA */
162
163        /* DeviceRemovable and PortPwrCtrlMask patched in later */
164};
165
166static void usb_hub_attach(USBPort *port1, USBDevice *dev)
167{
168    USBHubState *s = port1->opaque;
169    USBHubPort *port = &s->ports[port1->index];
170   
171    if (dev) {
172        if (port->port.dev)
173            usb_attach(port1, NULL);
174       
175        port->wPortStatus |= PORT_STAT_CONNECTION;
176        port->wPortChange |= PORT_STAT_C_CONNECTION;
177        if (dev->speed == USB_SPEED_LOW)
178            port->wPortStatus |= PORT_STAT_LOW_SPEED;
179        else
180            port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
181        port->port.dev = dev;
182        /* send the attach message */
183        dev->handle_packet(dev, 
184                           USB_MSG_ATTACH, 0, 0, NULL, 0);
185    } else {
186        dev = port->port.dev;
187        if (dev) {
188            port->wPortStatus &= ~PORT_STAT_CONNECTION;
189            port->wPortChange |= PORT_STAT_C_CONNECTION;
190            if (port->wPortStatus & PORT_STAT_ENABLE) {
191                port->wPortStatus &= ~PORT_STAT_ENABLE;
192                port->wPortChange |= PORT_STAT_C_ENABLE;
193            }
194            /* send the detach message */
195            dev->handle_packet(dev, 
196                               USB_MSG_DETACH, 0, 0, NULL, 0);
197            port->port.dev = NULL;
198        }
199    }
200}
201
202static void usb_hub_handle_reset(USBDevice *dev)
203{
204    /* XXX: do it */
205}
206
207static int usb_hub_handle_control(USBDevice *dev, int request, int value,
208                                  int index, int length, uint8_t *data)
209{
210    USBHubState *s = (USBHubState *)dev;
211    int ret;
212
213    switch(request) {
214    case DeviceRequest | USB_REQ_GET_STATUS:
215        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
216            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
217        data[1] = 0x00;
218        ret = 2;
219        break;
220    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
221        if (value == USB_DEVICE_REMOTE_WAKEUP) {
222            dev->remote_wakeup = 0;
223        } else {
224            goto fail;
225        }
226        ret = 0;
227        break;
228    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
229        if (value == 0 && index != 0x81) { /* clear ep halt */
230            goto fail;
231        }
232        ret = 0;
233        break;
234    case DeviceOutRequest | USB_REQ_SET_FEATURE:
235        if (value == USB_DEVICE_REMOTE_WAKEUP) {
236            dev->remote_wakeup = 1;
237        } else {
238            goto fail;
239        }
240        ret = 0;
241        break;
242    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
243        dev->addr = value;
244        ret = 0;
245        break;
246    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
247        switch(value >> 8) {
248        case USB_DT_DEVICE:
249            memcpy(data, qemu_hub_dev_descriptor, 
250                   sizeof(qemu_hub_dev_descriptor));
251            ret = sizeof(qemu_hub_dev_descriptor);
252            break;
253        case USB_DT_CONFIG:
254            memcpy(data, qemu_hub_config_descriptor, 
255                   sizeof(qemu_hub_config_descriptor));
256
257            /* status change endpoint size based on number
258             * of ports */
259            data[22] = (s->nb_ports + 1 + 7) / 8;
260
261            ret = sizeof(qemu_hub_config_descriptor);
262            break;
263        case USB_DT_STRING:
264            switch(value & 0xff) {
265            case 0:
266                /* language ids */
267                data[0] = 4;
268                data[1] = 3;
269                data[2] = 0x09;
270                data[3] = 0x04;
271                ret = 4;
272                break;
273            case 1:
274                /* serial number */
275                ret = set_usb_string(data, "314159");
276                break;
277            case 2:
278                /* product description */
279                ret = set_usb_string(data, "QEMU USB Hub");
280                break;
281            case 3:
282                /* vendor description */
283                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
284                break;
285            default:
286                goto fail;
287            }
288            break;
289        default:
290            goto fail;
291        }
292        break;
293    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
294        data[0] = 1;
295        ret = 1;
296        break;
297    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
298        ret = 0;
299        break;
300    case DeviceRequest | USB_REQ_GET_INTERFACE:
301        data[0] = 0;
302        ret = 1;
303        break;
304    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
305        ret = 0;
306        break;
307        /* usb specific requests */
308    case GetHubStatus:
309        data[0] = 0;
310        data[1] = 0;
311        data[2] = 0;
312        data[3] = 0;
313        ret = 4;
314        break;
315    case GetPortStatus:
316        {
317            unsigned int n = index - 1;
318            USBHubPort *port;
319            if (n >= s->nb_ports)
320                goto fail;
321            port = &s->ports[n];
322            data[0] = port->wPortStatus;
323            data[1] = port->wPortStatus >> 8;
324            data[2] = port->wPortChange;
325            data[3] = port->wPortChange >> 8;
326            ret = 4;
327        }
328        break;
329    case SetHubFeature:
330    case ClearHubFeature:
331        if (value == 0 || value == 1) {
332        } else {
333            goto fail;
334        }
335        ret = 0;
336        break;
337    case SetPortFeature:
338        {
339            unsigned int n = index - 1;
340            USBHubPort *port;
341            USBDevice *dev;
342            if (n >= s->nb_ports)
343                goto fail;
344            port = &s->ports[n];
345            dev = port->port.dev;
346            switch(value) {
347            case PORT_SUSPEND:
348                port->wPortStatus |= PORT_STAT_SUSPEND;
349                break;
350            case PORT_RESET:
351                if (dev) {
352                    dev->handle_packet(dev, 
353                                       USB_MSG_RESET, 0, 0, NULL, 0);
354                    port->wPortChange |= PORT_STAT_C_RESET;
355                    /* set enable bit */
356                    port->wPortStatus |= PORT_STAT_ENABLE;
357                }
358                break;
359            case PORT_POWER:
360                break;
361            default:
362                goto fail;
363            }
364            ret = 0;
365        }
366        break;
367    case ClearPortFeature:
368        {
369            unsigned int n = index - 1;
370            USBHubPort *port;
371            USBDevice *dev;
372            if (n >= s->nb_ports)
373                goto fail;
374            port = &s->ports[n];
375            dev = port->port.dev;
376            switch(value) {
377            case PORT_ENABLE:
378                port->wPortStatus &= ~PORT_STAT_ENABLE;
379                break;
380            case PORT_C_ENABLE:
381                port->wPortChange &= ~PORT_STAT_C_ENABLE;
382                break;
383            case PORT_SUSPEND:
384                port->wPortStatus &= ~PORT_STAT_SUSPEND;
385                break;
386            case PORT_C_SUSPEND:
387                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
388                break;
389            case PORT_C_CONNECTION:
390                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
391                break;
392            case PORT_C_OVERCURRENT:
393                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
394                break;
395            case PORT_C_RESET:
396                port->wPortChange &= ~PORT_STAT_C_RESET;
397                break;
398            default:
399                goto fail;
400            }
401            ret = 0;
402        }
403        break;
404    case GetHubDescriptor:
405        {
406            unsigned int n, limit, var_hub_size = 0;
407            memcpy(data, qemu_hub_hub_descriptor, 
408                   sizeof(qemu_hub_hub_descriptor));
409            data[2] = s->nb_ports;
410
411            /* fill DeviceRemovable bits */
412            limit = ((s->nb_ports + 1 + 7) / 8) + 7;
413            for (n = 7; n < limit; n++) {
414                data[n] = 0x00;
415                var_hub_size++;
416            }
417
418            /* fill PortPwrCtrlMask bits */
419            limit = limit + ((s->nb_ports + 7) / 8);
420            for (;n < limit; n++) {
421                data[n] = 0xff;
422                var_hub_size++;
423            }
424
425            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
426            data[0] = ret;
427            break;
428        }
429    default:
430    fail:
431        ret = USB_RET_STALL;
432        break;
433    }
434    return ret;
435}
436
437static int usb_hub_handle_data(USBDevice *dev, int pid, 
438                               uint8_t devep, uint8_t *data, int len)
439{
440    USBHubState *s = (USBHubState *)dev;
441    int ret;
442
443    switch(pid) {
444    case USB_TOKEN_IN:
445        if (devep == 1) {
446            USBHubPort *port;
447            unsigned int status;
448            int i, n;
449            n = (s->nb_ports + 1 + 7) / 8;
450            if (len == 1) { /* FreeBSD workaround */
451                n = 1;
452            } else if (n > len) {
453                return USB_RET_BABBLE;
454            }
455            status = 0;
456            for(i = 0; i < s->nb_ports; i++) {
457                port = &s->ports[i];
458                if (port->wPortChange)
459                    status |= (1 << (i + 1));
460            }
461            if (status != 0) {
462                for(i = 0; i < n; i++) {
463                    data[i] = status >> (8 * i);
464                }
465                ret = n;
466            } else {
467                ret = USB_RET_NAK; /* usb11 11.13.1 */
468            }
469        } else {
470            goto fail;
471        }
472        break;
473    case USB_TOKEN_OUT:
474    default:
475    fail:
476        ret = USB_RET_STALL;
477        break;
478    }
479    return ret;
480}
481
482static int usb_hub_broadcast_packet(USBHubState *s, int pid, 
483                                    uint8_t devaddr, uint8_t devep,
484                                    uint8_t *data, int len)
485{
486    USBHubPort *port;
487    USBDevice *dev;
488    int i, ret;
489
490    for(i = 0; i < s->nb_ports; i++) {
491        port = &s->ports[i];
492        dev = port->port.dev;
493        if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
494            ret = dev->handle_packet(dev, pid, 
495                                     devaddr, devep,
496                                     data, len);
497            if (ret != USB_RET_NODEV) {
498                return ret;
499            }
500        }
501    }
502    return USB_RET_NODEV;
503}
504
505static int usb_hub_handle_packet(USBDevice *dev, int pid, 
506                                 uint8_t devaddr, uint8_t devep,
507                                 uint8_t *data, int len)
508{
509    USBHubState *s = (USBHubState *)dev;
510
511#if defined(DEBUG) && 0
512    printf("usb_hub: pid=0x%x\n", pid);
513#endif
514    if (dev->state == USB_STATE_DEFAULT &&
515        dev->addr != 0 &&
516        devaddr != dev->addr &&
517        (pid == USB_TOKEN_SETUP || 
518         pid == USB_TOKEN_OUT || 
519         pid == USB_TOKEN_IN)) {
520        /* broadcast the packet to the devices */
521        return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len);
522    }
523    return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
524}
525
526static void usb_hub_handle_destroy(USBDevice *dev)
527{
528    USBHubState *s = (USBHubState *)dev;
529
530    qemu_free(s);
531}
532
533USBDevice *usb_hub_init(int nb_ports)
534{
535    USBHubState *s;
536    USBHubPort *port;
537    int i;
538
539    if (nb_ports > MAX_PORTS)
540        return NULL;
541    s = qemu_mallocz(sizeof(USBHubState));
542    if (!s)
543        return NULL;
544    s->dev.speed = USB_SPEED_FULL;
545    s->dev.handle_packet = usb_hub_handle_packet;
546
547    /* generic USB device init */
548    s->dev.handle_reset = usb_hub_handle_reset;
549    s->dev.handle_control = usb_hub_handle_control;
550    s->dev.handle_data = usb_hub_handle_data;
551    s->dev.handle_destroy = usb_hub_handle_destroy;
552
553    pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Hub");
554
555    s->nb_ports = nb_ports;
556    for(i = 0; i < s->nb_ports; i++) {
557        port = &s->ports[i];
558        qemu_register_usb_port(&port->port, s, i, usb_hub_attach);
559        port->wPortStatus = PORT_STAT_POWER;
560        port->wPortChange = 0;
561    }
562    return (USBDevice *)s;
563}
Note: See TracBrowser for help on using the repository browser.