Statistics
| Branch: | Revision:

root / hw / usb-hub.c @ 0986ac3b

History | View | Annotate | Download (16.7 kB)

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

    
30
typedef struct USBHubPort {
31
    USBPort port;
32
    uint16_t wPortStatus;
33
    uint16_t wPortChange;
34
} USBHubPort;
35

    
36
typedef 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

    
85
static 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 */
106
static 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

    
153
static 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

    
166
static 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

    
202
static void usb_hub_handle_reset(USBDevice *dev, int destroy)
203
{
204
    /* XXX: do it */
205
    if (destroy)
206
        qemu_free(dev);
207
}
208

    
209
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
210
                                  int index, int length, uint8_t *data)
211
{
212
    USBHubState *s = (USBHubState *)dev;
213
    int ret;
214

    
215
    switch(request) {
216
    case DeviceRequest | USB_REQ_GET_STATUS:
217
        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
218
            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
219
        data[1] = 0x00;
220
        ret = 2;
221
        break;
222
    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
223
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
224
            dev->remote_wakeup = 0;
225
        } else {
226
            goto fail;
227
        }
228
        ret = 0;
229
        break;
230
    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
231
        if (value == 0 && index != 0x81) { /* clear ep halt */
232
            goto fail;
233
        }
234
        ret = 0;
235
        break;
236
    case DeviceOutRequest | USB_REQ_SET_FEATURE:
237
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
238
            dev->remote_wakeup = 1;
239
        } else {
240
            goto fail;
241
        }
242
        ret = 0;
243
        break;
244
    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
245
        dev->addr = value;
246
        ret = 0;
247
        break;
248
    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
249
        switch(value >> 8) {
250
        case USB_DT_DEVICE:
251
            memcpy(data, qemu_hub_dev_descriptor, 
252
                   sizeof(qemu_hub_dev_descriptor));
253
            ret = sizeof(qemu_hub_dev_descriptor);
254
            break;
255
        case USB_DT_CONFIG:
256
            memcpy(data, qemu_hub_config_descriptor, 
257
                   sizeof(qemu_hub_config_descriptor));
258

    
259
            /* status change endpoint size based on number
260
             * of ports */
261
            data[22] = (s->nb_ports + 1 + 7) / 8;
262

    
263
            ret = sizeof(qemu_hub_config_descriptor);
264
            break;
265
        case USB_DT_STRING:
266
            switch(value & 0xff) {
267
            case 0:
268
                /* language ids */
269
                data[0] = 4;
270
                data[1] = 3;
271
                data[2] = 0x09;
272
                data[3] = 0x04;
273
                ret = 4;
274
                break;
275
            case 1:
276
                /* serial number */
277
                ret = set_usb_string(data, "314159");
278
                break;
279
            case 2:
280
                /* product description */
281
                ret = set_usb_string(data, "QEMU USB Hub");
282
                break;
283
            case 3:
284
                /* vendor description */
285
                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
286
                break;
287
            default:
288
                goto fail;
289
            }
290
            break;
291
        default:
292
            goto fail;
293
        }
294
        break;
295
    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
296
        data[0] = 1;
297
        ret = 1;
298
        break;
299
    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
300
        ret = 0;
301
        break;
302
    case DeviceRequest | USB_REQ_GET_INTERFACE:
303
        data[0] = 0;
304
        ret = 1;
305
        break;
306
    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
307
        ret = 0;
308
        break;
309
        /* usb specific requests */
310
    case GetHubStatus:
311
        data[0] = 0;
312
        data[1] = 0;
313
        data[2] = 0;
314
        data[3] = 0;
315
        ret = 4;
316
        break;
317
    case GetPortStatus:
318
        {
319
            unsigned int n = index - 1;
320
            USBHubPort *port;
321
            if (n >= s->nb_ports)
322
                goto fail;
323
            port = &s->ports[n];
324
            data[0] = port->wPortStatus;
325
            data[1] = port->wPortStatus >> 8;
326
            data[2] = port->wPortChange;
327
            data[3] = port->wPortChange >> 8;
328
            ret = 4;
329
        }
330
        break;
331
    case SetHubFeature:
332
    case ClearHubFeature:
333
        if (value == 0 || value == 1) {
334
        } else {
335
            goto fail;
336
        }
337
        ret = 0;
338
        break;
339
    case SetPortFeature:
340
        {
341
            unsigned int n = index - 1;
342
            USBHubPort *port;
343
            USBDevice *dev;
344
            if (n >= s->nb_ports)
345
                goto fail;
346
            port = &s->ports[n];
347
            dev = port->port.dev;
348
            switch(value) {
349
            case PORT_SUSPEND:
350
                port->wPortStatus |= PORT_STAT_SUSPEND;
351
                break;
352
            case PORT_RESET:
353
                if (dev) {
354
                    dev->handle_packet(dev, 
355
                                       USB_MSG_RESET, 0, 0, NULL, 0);
356
                    port->wPortChange |= PORT_STAT_C_RESET;
357
                    /* set enable bit */
358
                    port->wPortStatus |= PORT_STAT_ENABLE;
359
                }
360
                break;
361
            case PORT_POWER:
362
                break;
363
            default:
364
                goto fail;
365
            }
366
            ret = 0;
367
        }
368
        break;
369
    case ClearPortFeature:
370
        {
371
            unsigned int n = index - 1;
372
            USBHubPort *port;
373
            USBDevice *dev;
374
            if (n >= s->nb_ports)
375
                goto fail;
376
            port = &s->ports[n];
377
            dev = port->port.dev;
378
            switch(value) {
379
            case PORT_ENABLE:
380
                port->wPortStatus &= ~PORT_STAT_ENABLE;
381
                break;
382
            case PORT_C_ENABLE:
383
                port->wPortChange &= ~PORT_STAT_C_ENABLE;
384
                break;
385
            case PORT_SUSPEND:
386
                port->wPortStatus &= ~PORT_STAT_SUSPEND;
387
                break;
388
            case PORT_C_SUSPEND:
389
                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
390
                break;
391
            case PORT_C_CONNECTION:
392
                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
393
                break;
394
            case PORT_C_OVERCURRENT:
395
                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
396
                break;
397
            case PORT_C_RESET:
398
                port->wPortChange &= ~PORT_STAT_C_RESET;
399
                break;
400
            default:
401
                goto fail;
402
            }
403
            ret = 0;
404
        }
405
        break;
406
    case GetHubDescriptor:
407
        {
408
            unsigned int n, limit, var_hub_size = 0;
409
            memcpy(data, qemu_hub_hub_descriptor, 
410
                   sizeof(qemu_hub_hub_descriptor));
411
            data[2] = s->nb_ports;
412

    
413
            /* fill DeviceRemovable bits */
414
            limit = ((s->nb_ports + 1 + 7) / 8) + 7;
415
            for (n = 7; n < limit; n++) {
416
                data[n] = 0x00;
417
                var_hub_size++;
418
            }
419

    
420
            /* fill PortPwrCtrlMask bits */
421
            limit = limit + ((s->nb_ports + 7) / 8);
422
            for (;n < limit; n++) {
423
                data[n] = 0xff;
424
                var_hub_size++;
425
            }
426

    
427
            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
428
            data[0] = ret;
429
            break;
430
        }
431
    default:
432
    fail:
433
        ret = USB_RET_STALL;
434
        break;
435
    }
436
    return ret;
437
}
438

    
439
static int usb_hub_handle_data(USBDevice *dev, int pid, 
440
                               uint8_t devep, uint8_t *data, int len)
441
{
442
    USBHubState *s = (USBHubState *)dev;
443
    int ret;
444

    
445
    switch(pid) {
446
    case USB_TOKEN_IN:
447
        if (devep == 1) {
448
            USBHubPort *port;
449
            unsigned int status;
450
            int i, n;
451
            n = (s->nb_ports + 1 + 7) / 8;
452
            if (len == 1) { /* FreeBSD workaround */
453
                n = 1;
454
            } else if (n > len) {
455
                return USB_RET_BABBLE;
456
            }
457
            status = 0;
458
            for(i = 0; i < s->nb_ports; i++) {
459
                port = &s->ports[i];
460
                if (port->wPortChange)
461
                    status |= (1 << (i + 1));
462
            }
463
            if (status != 0) {
464
                for(i = 0; i < n; i++) {
465
                    data[i] = status >> (8 * i);
466
                }
467
                ret = n;
468
            } else {
469
                ret = USB_RET_NAK; /* usb11 11.13.1 */
470
            }
471
        } else {
472
            goto fail;
473
        }
474
        break;
475
    case USB_TOKEN_OUT:
476
    default:
477
    fail:
478
        ret = USB_RET_STALL;
479
        break;
480
    }
481
    return ret;
482
}
483

    
484
static int usb_hub_broadcast_packet(USBHubState *s, int pid, 
485
                                    uint8_t devaddr, uint8_t devep,
486
                                    uint8_t *data, int len)
487
{
488
    USBHubPort *port;
489
    USBDevice *dev;
490
    int i, ret;
491

    
492
    for(i = 0; i < s->nb_ports; i++) {
493
        port = &s->ports[i];
494
        dev = port->port.dev;
495
        if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
496
            ret = dev->handle_packet(dev, pid, 
497
                                     devaddr, devep,
498
                                     data, len);
499
            if (ret != USB_RET_NODEV) {
500
                return ret;
501
            }
502
        }
503
    }
504
    return USB_RET_NODEV;
505
}
506

    
507
static int usb_hub_handle_packet(USBDevice *dev, int pid, 
508
                                 uint8_t devaddr, uint8_t devep,
509
                                 uint8_t *data, int len)
510
{
511
    USBHubState *s = (USBHubState *)dev;
512

    
513
#if defined(DEBUG) && 0
514
    printf("usb_hub: pid=0x%x\n", pid);
515
#endif
516
    if (dev->state == USB_STATE_DEFAULT &&
517
        dev->addr != 0 &&
518
        devaddr != dev->addr &&
519
        (pid == USB_TOKEN_SETUP || 
520
         pid == USB_TOKEN_OUT || 
521
         pid == USB_TOKEN_IN)) {
522
        /* broadcast the packet to the devices */
523
        return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len);
524
    }
525
    return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
526
}
527

    
528
USBDevice *usb_hub_init(int nb_ports)
529
{
530
    USBHubState *s;
531
    USBHubPort *port;
532
    int i;
533

    
534
    if (nb_ports > MAX_PORTS)
535
        return NULL;
536
    s = qemu_mallocz(sizeof(USBHubState));
537
    if (!s)
538
        return NULL;
539
    s->dev.speed = USB_SPEED_FULL;
540
    s->dev.handle_packet = usb_hub_handle_packet;
541

    
542
    /* generic USB device init */
543
    s->dev.handle_reset = usb_hub_handle_reset;
544
    s->dev.handle_control = usb_hub_handle_control;
545
    s->dev.handle_data = usb_hub_handle_data;
546

    
547
    s->nb_ports = nb_ports;
548
    for(i = 0; i < s->nb_ports; i++) {
549
        port = &s->ports[i];
550
        qemu_register_usb_port(&port->port, s, i, usb_hub_attach);
551
        port->wPortStatus = PORT_STAT_POWER;
552
        port->wPortChange = 0;
553
    }
554
    return (USBDevice *)s;
555
}