1/**
2 * \brief this file contains the emulation code for the root hub
3 */
4/*
5 * Copyright (c) 2007-2013 ETH Zurich.
6 * All rights reserved.
7 *
8 * This file is distributed under the terms in the attached LICENSE file.
9 * If you do not find this file, copies can be found by writing to:
10 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
11 */
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16#include <barrelfish/barrelfish.h>
17
18#include "ehci_device.h"
19
20#include <usb/usb.h>
21#include <usb/usb_descriptor.h>
22#include <usb/usb_error.h>
23#include <usb/usb_request.h>
24
25#include <usb_device.h>
26#include <usb_controller.h>
27#include <usb_hub.h>
28#include "usb_ehci.h"
29#include "usb_ehci_root_hub.h"
30
31
32/*
33 * --------------------------------------------------------------------------
34 * local variables defining the descriptors of the root hub
35 */
36static const struct usb_device_descriptor rh_dev_desc = {
37    .bLength = sizeof(struct usb_device_descriptor),
38    .bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE,
39    .bcdUSB = 0x0200,
40    .bDeviceClass = USB_HUB_CLASS_CODE,
41    .bDeviceSubClass = USB_HUB_SUBCLASS_CODE,
42    .bDeviceProtocol = USB_HUB_PROTOCOL_HSHUBSTT,
43    .bMaxPacketSize0 = 64,
44    .idVendor = 0,
45    .idProduct = 0,
46    .bcdDevice = 0x0100,
47    .iManufacturer = 1,
48    .iProduct = 2,
49    .iSerialNumber = 0,
50    .bNumConfigurations = 1,
51};
52
53static const struct usb_device_qualifier_descriptor rh_qual_desc = {
54    .bLength = sizeof(struct usb_device_qualifier_descriptor),
55    .bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
56    .bcdUSB = 0x0200,
57    .bDeviceClass = USB_HUB_CLASS_CODE,
58    .bDeviceSubClass = USB_HUB_SUBCLASS_CODE,
59    .bDeviceProtocol = USB_HUB_PROTOCOL_FSHUB,
60    .bMaxPacketSize0 = 0,
61    .bNumConfigurations = 0,
62    .bReserved = 0,
63};
64
65static const struct usb_ehci_config_descriptor rh_cfg_desc = {
66    .config = {
67        .bLength = sizeof(struct usb_config_descriptor),
68        .bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIG,
69        .wTotalLength = sizeof(rh_cfg_desc),
70        .bNumInterfaces = 1,
71        .bConfigurationValue = 1,
72        .iConfiguration = 0,
73        .bmAttributes = USB_CONFIG_SELF_POWERED,
74        .bMaxPower = 0,
75    },
76    .iface = {
77        .bLength = sizeof(struct usb_interface_descriptor),
78        .bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
79        .bNumEndpoints = 1,
80        .bInterfaceClass = USB_HUB_IFACE_CLASS_CODE,
81        .bInterfaceSubClass = USB_HUB_IFACE_SUBCLASS_CODE,
82        .bInterfaceProtocol = 0,
83    },
84    .endpoint = {
85        .bLength = sizeof(struct usb_endpoint_descriptor),
86        .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
87        .bEndpointAddress = {
88            USB_ENDPOINT_DIRECTION_IN,
89            0,
90            1
91        },
92        .bmAttributes = {
93            0,
94            0,
95            0,
96            USB_ENDPOINT_TYPE_INTR
97        },
98        .wMaxPacketSize = (8 << 8),
99        .bInterval = 255,
100    },
101};
102
103static const struct usb_hub_descriptor rh_desc = {
104    .bDescLength = 0,
105    .bDescriptorType = USB_DESCRIPTOR_TYPE_HUB,
106    .bNbrPorts = 0,
107    .wHubCharacteristics = {
108        0,
109        0,
110        0,
111        0,
112        0,
113        0
114    },
115    .bPwrOn2PwrGood = 0,
116    .bHubContrCurrent = 0,
117    .bDeviceRemovable = {
118        0
119    }
120};
121
122/*
123 * --------------------------------------------------------------------------
124 * functions emulating the root hub requests
125 */
126
127
128/**
129 * \brief this function is called when a port change detected interrupt is risen
130 *
131 * \param hc the host controller which got the interrupt
132 */
133void usb_ehci_roothub_interrupt(usb_ehci_hc_t *hc)
134{
135    USB_DEBUG_TR_ENTER;
136
137    for (uint16_t i = 0; i < hc->rh_num_ports; i++) {
138        ehci_portsc_t ps = ehci_portsc_rd(&hc->ehci_base, i);
139        /* clear out the change bits */
140        if (ehci_portsc_occ_extract(ps) || ehci_portsc_pec_extract(ps)
141                || ehci_portsc_csc_extract(ps)) {
142            USB_DEBUG_HC("roothub_interrupt: port %i has changed\n", i+1);
143        }
144    }
145    /* defer the handing to the hub driver */
146    usb_hub_root_interrupt(hc->controller);
147}
148
149#define C(req, recipent, dir) ((req) | ((recipent)<<8) | ((dir)<<16))
150
151/**
152 * \brief emulates the execution of the requests on the root hub
153 *
154 * \param device the roothub device
155 * \param req the request to execute
156 * \param ret_data the data returned when a read request was executed
157 * \param ret_length the number of bytes in the returned data
158 */
159usb_error_t usb_ehci_roothub_exec(struct usb_device *device,
160        struct usb_device_request *req, const void **ret_data,
161        uint16_t *ret_length)
162{
163    USB_DEBUG_TR_ENTER;
164    usb_ehci_hc_t *hc = (usb_ehci_hc_t *) device->controller->hc_control;
165    const char *str;
166    const void *data = (const void *) &hc->rh_desc;
167    uint16_t data_length = 0;
168
169
170    switch (C(req->bRequest, req->bType.recipient, req->bType.direction)) {
171        /* clear feature requests */
172        case C(USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_WRITE):
173        case C(USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_RECIPIENT_INTERFACE, USB_REQUEST_WRITE):
174        case C(USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_RECIPIENT_ENDPOINT, USB_REQUEST_WRITE):
175            /* no-op: don't handle write requests */
176            break;
177
178        /* get configuration request */
179        case C(USB_REQUEST_GET_CONFIG, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_READ):
180            data_length = 1;
181            hc->rh_desc.temp[0] = hc->rh_device_config;
182            break;
183
184        /* get descriptor request */
185        case C(USB_REQUEST_GET_DESCRIPTOR, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_READ):
186            switch (req->wValue >> 8) {
187                /*
188                 * the only the string type has an id.. the others
189                 * result in an IO error if there is an id set.
190                 */
191                case USB_DESCRIPTOR_TYPE_DEVICE:
192                case USB_DESCRIPTOR_TYPE_HUB:
193                    if ((req->wValue & 0xFF) != 0) {
194                        return (USB_ERR_IOERROR);
195                    }
196                    if (req->bType.type != USB_REQUEST_TYPE_CLASS) {
197                        data_length = sizeof(rh_dev_desc);
198                        data = (const void *) &rh_dev_desc;
199                        break;
200                    }
201                    /* handling class specific request  */
202                    hc->rh_desc.hub_desc = rh_desc;
203                    hc->rh_desc.hub_desc.bNbrPorts = hc
204                            ->rh_num_ports;
205                    hc->rh_desc.hub_desc.wHubCharacteristics
206                            .port_indicator = ehci_hcsparams_p_indicator_rdf(
207                            &hc->ehci_base);
208                    hc->rh_desc.hub_desc.wHubCharacteristics
209                            .power_mode = ehci_hcsparams_ppc_rdf(&hc->ehci_base);
210                    hc->rh_desc.hub_desc.bPwrOn2PwrGood = 200;
211                    hc->rh_desc.hub_desc.bDescLength = 8
212                            + ((hc->rh_num_ports + 7) / 8);
213                    data_length = hc->rh_desc.hub_desc.bDescLength;
214                    break;
215                case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
216                    if ((req->wValue & 0xFF) != 0) {
217                        return (USB_ERR_IOERROR);
218                    }
219                    data_length = sizeof(rh_qual_desc);
220                    data = (const void *) &rh_qual_desc;
221                    break;
222
223                case USB_DESCRIPTOR_TYPE_CONFIG:
224                    if ((req->wValue & 0xFF) != 0) {
225                        return (USB_ERR_IOERROR);
226                    }
227                    data_length = sizeof(rh_cfg_desc);
228                    data = (const void *) &rh_cfg_desc;
229                    break;
230
231                case USB_DESCRIPTOR_TYPE_STRING:
232                    switch (req->wValue & 0xFF) {
233                        case 0:
234                            str = "\001";
235                            break;
236                        case 1:
237                            str = hc->rh_vendor;
238                            break;
239                        case 2:
240                            str = "EHCI root hub";
241                            break;
242                        default:
243                            str = "";
244                            break;
245                    }
246                    /*
247                     * TODO: MAKE STRING DESCRIPTOR
248                     * len = ...
249                     * store in hub_desc.tmp
250                     */
251                    break;
252                default:
253                    debug_printf("GET_DESC ->IOERR\n");
254                    return (USB_ERR_IOERROR);
255                    break;
256            }
257            break;
258
259        /* get interface requests */
260        case C(USB_REQUEST_GET_INTERFACE, USB_REQUEST_RECIPIENT_INTERFACE, USB_REQUEST_READ):
261            /* we don't have an alternative interface */
262            data_length = 1;
263            hc->rh_desc.temp[0] = 0;
264            break;
265
266        /* get status request - device */
267        case C(USB_REQUEST_GET_STATUS, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_READ):
268            if (req->bType.type != USB_REQUEST_TYPE_CLASS) {
269                data_length = 2;
270                hc->rh_desc.status.wStatus = USB_STATUS_SELF_POWERED;
271                break;
272            }
273            data_length = 16;
274            memset(hc->rh_desc.temp, 0, 16);
275
276            break;
277
278        /* get status request - interface or endpoint */
279        case C(USB_REQUEST_GET_STATUS, USB_REQUEST_RECIPIENT_INTERFACE, USB_REQUEST_READ):
280        case C(USB_REQUEST_GET_STATUS, USB_REQUEST_RECIPIENT_ENDPOINT, USB_REQUEST_READ):
281            data_length = 2;
282            hc->rh_desc.status.wStatus = 0;
283            break;
284
285        /* set address request */
286        case C(USB_REQUEST_SET_ADDRESS, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_WRITE):
287            if (req->wValue >= USB_EHCI_MAX_DEVICES) {
288                return (USB_ERR_IOERROR);
289            }
290            hc->rh_device_address = req->wValue;
291            break;
292
293        /* set configuration request */
294        case C(USB_REQUEST_SET_CONFIG, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_WRITE):
295            if ((req->wValue != 0) && (req->wValue != 1)) {
296                return (USB_ERR_IOERROR);
297            }
298            hc->rh_device_config = req->wValue;
299            break;
300
301        /* set descriptor request */
302        case C(USB_REQUEST_SET_DESCRIPTOR, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_WRITE):
303            /* do not allow to change the descriptor */
304            break;
305
306        /* set feature request */
307        case C(USB_REQUEST_SET_FEATURE, USB_REQUEST_RECIPIENT_DEVICE, USB_REQUEST_WRITE):
308        case C(USB_REQUEST_SET_FEATURE, USB_REQUEST_RECIPIENT_INTERFACE, USB_REQUEST_WRITE):
309        case C(USB_REQUEST_SET_FEATURE, USB_REQUEST_RECIPIENT_ENDPOINT, USB_REQUEST_WRITE):
310            /* setting a feature results in IO error */
311            return (USB_ERR_IOERROR);
312            break;
313
314        /* set interface request */
315        case C(USB_REQUEST_SET_INTERFACE, USB_REQUEST_RECIPIENT_INTERFACE, USB_REQUEST_WRITE):
316            break;
317
318        /* set synch frame request */
319        case C(USB_REQUEST_SYNCH_FRAME, USB_REQUEST_RECIPIENT_ENDPOINT, USB_REQUEST_WRITE):
320            break;
321
322        /*
323         * handling hub class specific requests
324         */
325
326        /* clear hub feature request */
327        case C(USB_HUB_REQ_CLEAR_FEATURE, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_WRITE):
328            if ((req->wIndex < 1) || (req->wLength > hc->rh_num_ports)) {
329                /* invalid port nuber  */
330                return (USB_ERR_IOERROR);
331            }
332            /* mackerel is zero based */
333            req->wIndex--;
334
335            switch (req->wValue) {
336                case USB_HUB_FEATURE_PORT_ENABLE:
337                    ehci_portsc_ped_wrf(&hc->ehci_base, req->wIndex, 0);
338                    break;
339                case USB_HUB_FEATURE_PORT_SUSPEND:
340                    if (ehci_portsc_sus_rdf(&hc->ehci_base, req->wIndex)
341                            && (!ehci_portsc_fpr_rdf(&hc->ehci_base, req->wIndex))) {
342                        ehci_portsc_fpr_wrf(&hc->ehci_base, req->wIndex, 1);
343                    }
344                    lib_usb_wait(20);
345
346                    ehci_portsc_sus_wrf(&hc->ehci_base, req->wIndex, 0);
347                    ehci_portsc_fpr_wrf(&hc->ehci_base, req->wIndex, 0);
348                    ehci_portsc_ls_wrf(&hc->ehci_base, req->wIndex, 0x3);
349                    lib_usb_wait(4);
350                    break;
351                case USB_HUB_FEATURE_PORT_POWER:
352                    ehci_portsc_pp_wrf(&hc->ehci_base, req->wIndex, 0);
353                    break;
354                case USB_HUB_FEATURE_PORT_TEST:
355                    /* clear port test */
356                    break;
357
358                case USB_HUB_FEATURE_PORT_INDICATOR:
359                    ehci_portsc_pic_wrf(&hc->ehci_base, req->wIndex, 0);
360                    break;
361                case USB_HUB_FEATURE_C_PORT_CONNECTION:
362                    ehci_portsc_csc_wrf(&hc->ehci_base, req->wIndex, 1);
363                    break;
364
365                case USB_HUB_FEATURE_C_PORT_ENABLE:
366                    ehci_portsc_pec_wrf(&hc->ehci_base, req->wIndex, 1);
367                    break;
368                case USB_HUB_FEATURE_C_PORT_SUSPEND:
369                    ehci_portsc_sus_wrf(&hc->ehci_base, req->wIndex, 1);
370                    break;
371                case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
372                    ehci_portsc_occ_wrf(&hc->ehci_base, req->wIndex, 1);
373                    break;
374                case USB_HUB_FEATURE_C_PORT_RESET:
375                    hc->rh_reset = 0;
376                    break;
377                default:
378                    return (USB_ERR_IOERROR);
379                    break;
380            }
381            break;
382
383
384        /* get hub status request */
385        case C(USB_HUB_REQ_GET_STATUS, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_READ):
386            if ((req->wIndex < 1) || (req->wIndex > hc->rh_num_ports)) {
387                /* invalid port number  */
388                debug_printf("ehci: root_hub_exec: invalid port number %i\n",
389                        req->wIndex);
390                return (USB_ERR_IOERROR);
391            }
392            data_length = sizeof(hc->rh_desc.port_status);
393            struct usb_hub_port_status *ps = &(hc->rh_desc
394                    .port_status);
395            memset(ps, 0, sizeof(*ps));
396            /* subtract one, mackerel is zero based */
397            ehci_portsc_t ehci_ps = ehci_portsc_rd(&hc->ehci_base,
398                    req->wIndex - 1);
399
400            ps->wPortStatus.enabled = ehci_portsc_ped_extract(ehci_ps);
401            ps->wPortStatus.indicator = ehci_portsc_pic_extract(ehci_ps) > 0;
402            ps->wPortStatus.test_mode = ehci_portsc_ptc_extract(ehci_ps) > 0;
403
404            if (ehci_portsc_ls_extract(ehci_ps) == 0x1) {
405                /* low speed device */
406                USB_DEBUG("port (%u) has low speed device\n", req->wIndex);
407                ps->wPortStatus.is_ls = 1;
408                ps->wPortStatus.is_hs = 0;
409            } else {
410                ps->wPortStatus.is_hs = 1;
411                ps->wPortStatus.is_ls = 0;
412            }
413
414            ps->wPortStatus.power_state = ehci_portsc_pp_extract(ehci_ps);
415            ps->wPortStatus.reset = ehci_portsc_pr_extract(ehci_ps);
416            ps->wPortStatus.over_current = ehci_portsc_oca_extract(ehci_ps);
417            if (ehci_portsc_sus_extract(ehci_ps)
418                    && !(ehci_portsc_fpr_extract(ehci_ps))) {
419                ps->wPortStatus.suspend = 1;
420            }
421            ps->wPortStatus.connection = ehci_portsc_ccs_extract(ehci_ps);
422
423            ps->wPortChange.is_reset = hc->rh_reset;
424            ps->wPortChange.over_current = ehci_portsc_occ_extract(ehci_ps);
425            ps->wPortChange.resumed = ehci_portsc_fpr_extract(ehci_ps);
426            ps->wPortChange.disabled = ehci_portsc_pec_extract(ehci_ps);
427            ps->wPortChange.connect = ehci_portsc_csc_extract(ehci_ps);
428
429            break;
430
431        /* set hub feature request */
432        case C(USB_HUB_REQ_SET_FEATURE, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_WRITE):
433            if ((req->wIndex < 1) || (req->wIndex > hc->rh_num_ports)) {
434                /* invalid port number  */
435                return (USB_ERR_IOERROR);
436            }
437            /* mackerel is zero based */
438            req->wIndex--;
439            switch (req->wValue) {
440                case USB_HUB_FEATURE_PORT_ENABLE:
441                    ehci_portsc_ped_wrf(&hc->ehci_base, req->wIndex, 1);
442                    break;
443                case USB_HUB_FEATURE_PORT_SUSPEND:
444                    ehci_portsc_sus_wrf(&hc->ehci_base, req->wIndex, 1);
445                    break;
446                case USB_HUB_FEATURE_PORT_RESET:
447                    if (ehci_portsc_ls_rdf(&hc->ehci_base, req->wIndex) == 0x1) {
448                        /* low speed device */
449                        usb_ehci_roothub_port_disown(hc, req->wIndex);
450                        break;
451                    }
452                    /* initiate reset sequence */
453                    ehci_portsc_pr_wrf(&hc->ehci_base, req->wIndex, 1);
454                    lib_usb_wait(200);
455
456                    /* clear the reset */
457                    ehci_portsc_pr_wrf(&hc->ehci_base, req->wIndex, 0);
458                    lib_usb_wait(200);
459
460                    if (ehci_portsc_pr_rdf(&hc->ehci_base, req->wIndex)) {
461                        debug_printf("exec: timeout while resetting port\n");
462                        return (USB_ERR_TIMEOUT);
463                    }
464
465                    if (!ehci_portsc_ped_rdf(&hc->ehci_base, req->wIndex)) {
466                        /*
467                         * TODO: DISOWNING PORTS...
468                         * if (hc->flags.tt_present) {
469                            usb_ehci_roothub_port_disown(hc, req->wIndex, 0);
470                        }*/
471                    }
472                    hc->rh_reset = 1;
473
474                    break;
475                case USB_HUB_FEATURE_PORT_POWER:
476                    ehci_portsc_pp_wrf(&hc->ehci_base, req->wIndex, 1);
477                    break;
478                case USB_HUB_FEATURE_PORT_TEST:
479                    break;
480                case USB_HUB_FEATURE_PORT_INDICATOR:
481                    ehci_portsc_pic_wrf(&hc->ehci_base, req->wIndex, 1);
482                    break;
483                default:
484                    return (USB_ERR_IOERROR);
485            }
486            break;
487
488        /* transaction translator requests */
489        case C(USB_HUB_REQ_CLEAR_TT_BUFFER, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_WRITE):
490        case C(USB_HUB_REQ_RESET_TT, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_WRITE):
491        case C(USB_HUB_REQ_STOP_TT, USB_REQUEST_RECIPIENT_OTHER, USB_REQUEST_WRITE):
492            /* root hub does not have a transaction translator */
493            break;
494        default:
495            return (USB_ERR_IOERROR);
496    }
497
498    if (ret_length) {
499        *ret_length = data_length;
500    }
501
502    if (ret_data) {
503
504        *ret_data = data;
505    }
506
507    return (USB_ERR_OK);
508}
509
510
511/*
512 * \brief this function hands over the port to the companion controller
513 *
514 * \param hc the host controller of the root hub
515 * \param portno the port number to disown
516 */
517void usb_ehci_roothub_port_disown(usb_ehci_hc_t *hc, uint16_t portno)
518{
519    if (portno > ehci_hcsparams_n_ports_rdf(&hc->ehci_base)) {
520        debug_printf("ERROR: port does not exist! \n");
521        return;
522    }
523
524    assert(portno > 0);
525    /* mackerel is zero based */
526    portno--;
527
528    ehci_portsc_po_wrf(&hc->ehci_base, portno, 1);
529
530}
531
532