1/*
2 * Copyright (c) 2007-2013 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13#include <barrelfish/barrelfish.h>
14
15#include "ohci_device.h"
16
17#include <usb/usb.h>
18#include <usb/usb_descriptor.h>
19#include <usb/usb_error.h>
20#include <usb/usb_request.h>
21#include <usb_device.h>
22#include <usb_hub.h>
23#include <usb_controller.h>
24#include "usb_ohci.h"
25#include "usb_ohci_root_hub.h"
26
27
28static const struct usb_device_descriptor usb_ohci_root_hub_device_desc = {
29        .bLength = sizeof(struct usb_device_descriptor),
30        .bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE,
31        .bcdUSB = 0x0100,
32        .bDeviceClass = USB_HUB_CLASS_CODE,
33        .bDeviceSubClass = USB_HUB_SUBCLASS_CODE,
34        .bDeviceProtocol = USB_HUB_PROTOCOL_FSHUB,
35        .bMaxPacketSize0 = 64,
36        .idVendor = 0,
37        .idProduct = 0,
38        .bcdDevice = 0x0100,
39        .iManufacturer = 1,
40        .iProduct = 2,
41        .iSerialNumber = 0,
42        .bNumConfigurations = 1,
43};
44
45static const struct usb_ohci_config_desc usb_ohci_root_hub_config_desc = {
46.config_desc = {
47        .bLength = sizeof(struct usb_config_descriptor),
48        .bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIG,
49        .wTotalLength = sizeof(usb_ohci_root_hub_config_desc),
50        .bNumInterfaces = 1,
51        .bConfigurationValue = 1,
52        .iConfiguration = 0,
53        .bmAttributes = USB_CONFIG_SELF_POWERED,
54        .bMaxPower = 0,
55}, .iface_desc = {
56.bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType =
57        USB_DESCRIPTOR_TYPE_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass =
58        USB_HUB_IFACE_CLASS_CODE, .bInterfaceSubClass =
59        USB_HUB_IFACE_SUBCLASS_CODE, .bInterfaceProtocol = 0,
60}, .ep_desc = {
61        .bLength = sizeof(struct usb_endpoint_descriptor),
62        .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
63        .bEndpointAddress = {USB_ENDPOINT_DIRECTION_IN,0,1},
64        .bmAttributes = {0,0,0,USB_ENDPOINT_TYPE_INTR},
65        .wMaxPacketSize = 32,
66        .bInterval = 255,
67},
68};
69
70static const struct usb_hub_descriptor usb_ohci_root_hub_desc = {
71        .bDescLength = 0,
72        .bDescriptorType = USB_DESCRIPTOR_TYPE_HUB,
73        .bNbrPorts = 0,
74        .wHubCharacteristics = {0, 0, 0, 0, 0, 0},
75        .bPwrOn2PwrGood = 0,
76        .bHubContrCurrent = 0,
77        .bDeviceRemovable =     {0}
78};
79
80/**
81 * \brief   this function clears the old interrupt data and reads the
82 *          status from the ports
83 *
84 * \param   hc  the host controller
85 */
86void usb_ohci_root_hub_interrupt(usb_ohci_hc_t *hc)
87{
88    /* clear old interrupt data */
89    memset(hc->root_hub_intr_data, 0, sizeof(hc->root_hub_intr_data));
90
91    /* get the root hub status */
92    //ohci_rh_status_t hstatus = ohci_rh_status_rd(hc->ohci_base);
93
94
95    /* get the number of ports */
96    uint16_t num_ports = hc->root_hub_num_ports + 1;
97    if (num_ports > (8 * sizeof(hc->root_hub_intr_data))) {
98        num_ports = (8 * sizeof(hc->root_hub_intr_data));
99    }
100
101    //char buf[4048];
102        //     ohci_rh_portstat_pr(buf, 4047, hc->ohci_base);
103          //  printf(buf);
104    ohci_rh_portstat_t ps;
105    /* set the bits in the interrupt data field */
106    for (uint16_t i = 0; i < num_ports; i++) {
107
108        ps = ohci_rh_portstat_rd(hc->ohci_base, i);
109        if (ps >> 16) {
110            /* port i has changed */
111            USB_DEBUG("port %u, has changed..\n", i);
112            hc->root_hub_intr_data[i / 8] |= (1 << (i % 8));
113        }
114    }
115
116    /* TODO: Handle the hub interrupts */
117   // assert(!"NYI: Root hub interrupt handling");
118    //uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata));
119}
120
121/*
122 * \brief   this function enables the status change interrupt
123 *          of the root hub and updates the enabled_intrs field
124 *          of the host controller.
125 *
126 * \param   hc  the host controller
127 */
128static void usb_ohci_root_hub_sc_intr_enable(usb_ohci_hc_t *hc)
129{
130    /* update the enabled interrupt field in our hc */
131    ohci_interrupt_t ie = ohci_intenable_rd(hc->ohci_base);
132    ohci_interrupt_rhsc_insert(ie, 1);
133    hc->enabled_intrs = ie;
134
135    /* enable the interrupt */
136    ohci_intenable_rhsc_wrf(hc->ohci_base, 1);
137
138    /* acknowledge any RHSC interrupts */
139    ohci_intstatus_rhsc_wrf(hc->ohci_base, 1);
140
141    usb_ohci_root_hub_interrupt(hc);
142}
143
144/**
145 *  \brief  this function emulates the USB root hub device by software
146 *
147 *  \param  device      the USB device we want to issue the request for
148 *  \param  req         the USB request to execute
149 *  \param  ret_data    pointer to the returned data
150 *  \param  ret_length  the returned length
151 */
152usb_error_t usb_ohci_roothub_exec(struct usb_device *device,
153        struct usb_device_request *req, const void **ret_data,
154        uint16_t *ret_length)
155{
156    usb_ohci_hc_t *hc = (usb_ohci_hc_t *) device->controller->hc_control;
157
158    const void *data = (const void *) hc->root_hub_desc.temp;
159    uint16_t length = 0;
160
161    uint16_t req_value = req->wValue;
162    uint16_t req_index = req->wIndex;
163
164    const char *str_ptr;
165
166    /*
167     * execute the request for the USB root hub
168     */
169    switch (req->bRequest) {
170
171        case USB_REQUEST_CLEAR_FEATURE:
172            /*
173             * ClearFeature() Request
174             *
175             * The only clear feature request is the clear port feature
176             * for an existing port 1..num_ports
177             */
178            if (req->bType.recipient == USB_REQUEST_RECIPIENT_OTHER) {
179                if ((req_index < 1) || (req_index > hc->root_hub_num_ports)) {
180                    *ret_length = length;
181                    *ret_data = data;
182                    return USB_ERR_IOERROR;
183                }
184                // get the current value of the register
185                ohci_rh_portstat_t ps = ohci_rh_portstat_rd(hc->ohci_base,
186                        req_index);
187
188                switch (req_value) {
189                    case USB_HUB_FEATURE_PORT_ENABLE:
190                        ohci_rh_portstat_ccs_insert(ps, 1);
191                        break;
192                    case USB_HUB_FEATURE_PORT_SUSPEND:
193                        ohci_rh_portstat_poci_insert(ps, 1);
194                        break;
195                    case USB_HUB_FEATURE_PORT_POWER:
196                        ohci_rh_portstat_lsda_insert(ps, 1);
197                        break;
198                    case USB_HUB_FEATURE_C_PORT_CONNECTION:
199                        ohci_rh_portstat_csc_insert(ps, 1);
200                        break;
201                    case USB_HUB_FEATURE_C_PORT_ENABLE:
202                        ohci_rh_portstat_pesc_insert(ps, 1);
203                        break;
204                    case USB_HUB_FEATURE_C_PORT_SUSPEND:
205                        ohci_rh_portstat_pssc_insert(ps, 1);
206                        break;
207                    case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
208                        ohci_rh_portstat_ocic_insert(ps, 1);
209                        break;
210                    case USB_HUB_FEATURE_C_PORT_RESET:
211                        ohci_rh_portstat_prsc_insert(ps, 1);
212                        break;
213                    default:
214                        *ret_length = length;
215                        *ret_data = data;
216                        return USB_ERR_IOERROR;
217                }
218                // write the value to the register
219                ohci_rh_portctrl_rawwr(hc->ohci_base, req_index, ps);
220
221                switch (req_value) {
222                    case USB_HUB_FEATURE_C_PORT_CONNECTION:
223                    case USB_HUB_FEATURE_C_PORT_ENABLE:
224                    case USB_HUB_FEATURE_C_PORT_SUSPEND:
225                    case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
226                    case USB_HUB_FEATURE_C_PORT_RESET:
227                        ps = ohci_rh_portstat_rd(hc->ohci_base, req_index);
228                        if ((ps >> 16) == 0) {
229                            usb_ohci_root_hub_sc_intr_enable(hc);
230                        }
231                        break;
232                    default:
233                        break;
234                }
235            }
236            break;
237
238        case USB_REQUEST_GET_CONFIG:
239            /*
240             * GetConfiguration() Request
241             */
242            if ((req->bType.recipient == USB_REQUEST_RECIPIENT_DEVICE)
243                    && req->bType.direction == USB_REQUEST_READ) {
244                length = 1;
245                hc->root_hub_desc.temp[0] = hc->root_hub_config;
246            }
247            break;
248
249        case USB_REQUEST_GET_DESCRIPTOR:
250            /*
251             * GetDescriptor() Request
252             *
253             * There are 3 different kind of standard descriptors we can
254             * return: Device, Interface and String
255             *
256             * and also a class specific request
257             */
258            if ((req->bType.recipient == USB_REQUEST_RECIPIENT_DEVICE)
259                    && req->bType.direction == USB_REQUEST_READ) {
260
261                /* hub class specific request */
262                if (req->bType.type == USB_REQUEST_TYPE_CLASS) {
263                    if ((req_value & 0xFF) != 0) {
264                        *ret_length = length;
265                        *ret_data = data;
266                        return USB_ERR_IOERROR;
267                    }
268                    ohci_rh_descra_t cda;
269                    cda = ohci_rh_descra_rd(hc->ohci_base);
270
271                    // get the standard hub descriptor to fill in data
272                    hc->root_hub_desc.hub_descriptor = usb_ohci_root_hub_desc;
273
274                    struct usb_hub_descriptor *hub = &(hc->root_hub_desc
275                            .hub_descriptor);
276                    hub->bNbrPorts = hc->root_hub_num_ports;
277
278                    hub->wHubCharacteristics.power_mode =
279                            ohci_rh_descra_nps_extract(cda);
280                    hub->bPwrOn2PwrGood = ohci_rh_descra_potpgt_extract(cda);
281
282                    /* update device removable stats */
283                    ohci_rh_descra_t cdb;
284                    cdb = ohci_rh_descrb_rd(hc->ohci_base);
285                    for (uint16_t i = 0; i < hc->root_hub_num_ports; i++) {
286                        if (cdb & 1) {
287                            hub->bDeviceRemovable[i / 8] |= (1 << (i % 8));
288                        }
289                        i >>= 1;
290                    }
291                    hub->bDescLength = 8 + ((hc->root_hub_num_ports + 7 / 8));
292                    length = hub->bDescLength;
293                    break;
294                }
295                /* standard usb request */
296                switch (req_value >> 8) {
297                    case USB_DESCRIPTOR_TYPE_DEVICE:
298                        if ((req_value & 0xFF) != 0) {
299                            *ret_length = length;
300                            *ret_data = data;
301                            return USB_ERR_IOERROR;
302                        }
303                        length = sizeof(usb_ohci_root_hub_device_desc);
304                        data = (const void *) &usb_ohci_root_hub_device_desc;
305                        break;
306                    case USB_DESCRIPTOR_TYPE_CONFIG:
307                        if ((req_value & 0xFF) != 0) {
308                            *ret_length = length;
309                            *ret_data = data;
310                            return USB_ERR_IOERROR;
311                        }
312                        length = sizeof(usb_ohci_root_hub_config_desc);
313                        data = (const void *) &usb_ohci_root_hub_config_desc;
314                        break;
315                    case USB_DESCRIPTOR_TYPE_STRING:
316                        switch (req_value & 0xFF) {
317                            case 0:
318                                str_ptr = "\001";
319                                break;
320                            case 1:
321                                break;
322                                str_ptr = hc->vendor;
323                            case 2:
324                                break;
325                                str_ptr = "OHCI root HUB";
326                            default:
327                                str_ptr = "";
328                                break;
329                        }
330                        /* TODO: Make string descriptor */
331                        assert(!"NYI: Make string descriptor");
332                        break;
333                    default:
334                        *ret_length = length;
335                        *ret_data = data;
336                        return USB_ERR_IOERROR;
337                        break;
338                }
339            }
340            break;
341
342        case USB_REQUEST_GET_INTERFACE:
343            /*
344             * GetInterface() Request
345             *
346             * Root hub has just one interface and no alternative one
347             */
348            if ((req->bType.recipient == USB_REQUEST_RECIPIENT_DEVICE)
349                    && req->bType.direction == USB_REQUEST_READ) {
350                length = 1;
351                hc->root_hub_desc.temp[0] = 0;
352            }
353            break;
354
355        case USB_REQUEST_GET_STATUS:
356            /*
357             * GetStatus() Request
358             */
359            if (req->bType.direction == USB_REQUEST_WRITE) {
360                *ret_data = data;
361                *ret_length = length;
362                return USB_ERR_IOERROR;
363            }
364
365            if (req->bType.type == USB_REQUEST_TYPE_CLASS) {
366                if (req->bType.recipient == USB_REQUEST_RECIPIENT_DEVICE) {
367                    length = 16;
368                    memset(hc->root_hub_desc.temp, 0, 16);
369                    break;
370                } else if (req->bType.recipient == USB_REQUEST_RECIPIENT_OTHER) {
371                    /* get port status */
372                    if ((req_index < 1) || req_index > hc->root_hub_num_ports) {
373                        // invalid port number;
374                        *ret_length = length;
375                        *ret_data = data;
376                        return USB_ERR_IOERROR;
377                    }
378                    ohci_rh_portstat_t ps = ohci_rh_portstat_rawrd(
379                            hc->ohci_base, req_index);
380                    memcpy(&hc->root_hub_desc.port_status.wPortChange, &ps, 2);
381                    ps <<= 16;
382                    memcpy(&hc->root_hub_desc.port_status.wPortStatus, &ps, 2);
383                    //hc->root_hub_desc.port_status.wPortChange = (ps >> 16);
384                    //hc->root_hub_desc.port_status.wPortStatus = (ps & 0xFFFF);
385                    length = sizeof(hc->root_hub_desc.port_status);
386                    break;
387                }
388            }
389
390            if ((req->bType.recipient == USB_REQUEST_RECIPIENT_DEVICE)) {
391                length = 2;
392                hc->root_hub_desc.status.wStatus = USB_STATUS_SELF_POWERED;
393            } else {
394                length = 2;
395                hc->root_hub_desc.status.wStatus = 0;
396            }
397
398            break;
399
400        case USB_REQUEST_SET_ADDRESS:
401            /*
402             * SetAddress() Request
403             *
404             * we can set the address of the root hub if it is
405             * withint the maximum devices range
406             */
407            if (req_value > USB_OHCI_MAX_DEVICES) {
408                *ret_length = length;
409                *ret_data = data;
410                return USB_ERR_IOERROR;
411            }
412            hc->root_hub_address = req_value;
413            break;
414
415        case USB_REQUEST_SET_CONFIG:
416            /*
417             * SetConfiguration() Request
418             *
419             * We have only two two options (0, 1) that we can set
420             */
421            if (req_value > 1) {
422                *ret_length = length;
423                *ret_data = data;
424                return USB_ERR_IOERROR;
425            }
426            hc->root_hub_config = req_value;
427            break;
428
429        case USB_REQUEST_SET_DESCRIPTOR:
430            /*
431             * SetDescriptor() Request
432             *
433             * We do not allow to change the USB root hub descriptor
434             * so this is a no-op for standard requests
435             *
436             * but is is an error for hub class requests
437             */
438            if (req->bType.type == USB_REQUEST_TYPE_CLASS) {
439                *ret_length = length;
440                *ret_data = data;
441                return USB_ERR_IOERROR;
442            }
443            break;
444
445        case USB_REQUEST_SET_FEATURE:
446            /*
447             * SetFeature() Request
448             *
449             * Setting a feature on the root hub is an error,
450             * so we reply with USB_ERR_IOERROR
451             */
452            if (req->bType.type != USB_REQUEST_TYPE_CLASS) {
453                *ret_length = length;
454                *ret_data = data;
455                return USB_ERR_IOERROR;
456                break;
457            }
458            /* handling of hub cpass specific port request */
459
460            if (req->bType.recipient == USB_REQUEST_RECIPIENT_OTHER) {
461                if ((req_index < 1) || (req_index > hc->root_hub_num_ports)) {
462                    *ret_length = length;
463                    *ret_data = data;
464                    return USB_ERR_IOERROR;
465                    break;
466                }
467                ohci_rh_portstat_t ps = ohci_rh_portstat_rawrd(hc->ohci_base,
468                        req_index);
469                switch (req_value) {
470                    case USB_HUB_FEATURE_PORT_ENABLE:
471                        ohci_rh_portstat_pes_insert(ps, 1);
472                        break;
473                    case USB_HUB_FEATURE_PORT_SUSPEND:
474                        ohci_rh_portstat_pss_insert(ps, 1);
475                        break;
476                    case USB_HUB_FEATURE_PORT_RESET:
477                        ohci_rh_portstat_prs_insert(ps, 1);
478                        break;
479                    case USB_HUB_FEATURE_PORT_POWER:
480                        ohci_rh_portstat_pps_insert(ps, 1);
481                        break;
482                    default:
483                        *ret_length = length;
484                        *ret_data = data;
485                        return USB_ERR_IOERROR;
486                        break;
487                }
488                ohci_rh_portctrl_rawwr(hc->ohci_base, req_index, ps);
489                /*
490                 * handle the reset of the port
491                 */
492                if (req_value == USB_HUB_FEATURE_PORT_RESET) {
493                    for (uint32_t i = 0;; i++) {
494                        if (ohci_rh_portstat_prs_rdf(hc->ohci_base,
495                                req_index)) {
496                            /*
497                             * bit has not been cleared, that means the
498                             * reset is not completed yet
499                             */
500                            /* TODO: WAIT SOME TIME */
501                            if (i > 12) {
502                                *ret_data = data;
503                                *ret_length = length;
504                                return USB_ERR_TIMEOUT;
505                            }
506                        } else {
507                            // reset complete
508                            break;
509                        }
510                    }
511                }
512            }
513            break;
514
515        case USB_REQUEST_SET_INTERFACE:
516            /*
517             * SetInterface() Request
518             *
519             * we have just one interface, so this is a no-op
520             */
521            break;
522
523        case USB_REQUEST_SYNCH_FRAME:
524            /*
525             * SetSynchFrame() Request
526             *
527             * No-op
528             */
529            break;
530        default:
531            *ret_length = length;
532            *ret_data = data;
533            return USB_ERR_IOERROR;
534            break;
535    }
536    *ret_length = length;
537    *ret_data = data;
538    return USB_ERR_OK;
539}
540
541