Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
 * Universal Host Controller Interface driver for USB.
 *
 * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
 *
 * (C) Copyright 1999 Linus Torvalds
 * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
 * (C) Copyright 1999 Randy Dunlap
 * (C) Copyright 1999 Georg Acher, acher@in.tum.de
 * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
 * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
 */


static __u8 root_hub_hub_des[] =
{
        0x09,                   /*  __u8  bLength; */
        0x29,                   /*  __u8  bDescriptorType; Hub-descriptor */
        0x02,                   /*  __u8  bNbrPorts; */
        0x00,                   /* __u16  wHubCharacteristics; */
        0x00,
        0x01,                   /*  __u8  bPwrOn2pwrGood; 2ms */
        0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
        0x00,                   /*  __u8  DeviceRemovable; *** 7 Ports max *** */
        0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
};

static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        unsigned int io_addr = uhci->io_addr;
        int i, len = 1;

        *buf = 0;
        for (i = 0; i < uhci->rh_numports; i++) {
                *buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
                len = (i + 1) / 8 + 1;
        }

        return !!*buf;
}

#define OK(x)                   len = (x); break

#define CLR_RH_PORTSTAT(x) \
        status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
        status = (status & 0xfff5) & ~(x); \
        outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))


#define SET_RH_PORTSTAT(x) \
        status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
        status = (status & 0xfff5) | (x); \
        outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))



/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        u16 wIndex, char *buf, u16 wLength)
{
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int i, status, retval = 0, len = 0;
        unsigned int io_addr = uhci->io_addr;
        __u16 cstatus;
        char c_p_r[8];

        for (i = 0; i < 8; i++)
                c_p_r[i] = 0;

        switch (typeReq) {
                /* Request Destination:
                   without flags: Device,
                   RH_INTERFACE: interface,
                   RH_ENDPOINT: endpoint,
                   RH_CLASS means HUB here,
                   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
                */


        case GetHubStatus:
                *(__u32 *)buf = cpu_to_le32(0);
                OK(4);          /* hub power */
        case GetPortStatus:
                status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
                cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
                        ((status & USBPORTSC_PEC) >> (3 - 1)) |
                        (c_p_r[wIndex - 1] << (0 + 4));
                        status = (status & USBPORTSC_CCS) |
                        ((status & USBPORTSC_PE) >> (2 - 1)) |
                        ((status & USBPORTSC_SUSP) >> (12 - 2)) |
                        ((status & USBPORTSC_PR) >> (9 - 4)) |
                        (1 << 8) |      /* power on */
                        ((status & USBPORTSC_LSDA) << (-8 + 9));

                *(__u16 *)buf = cpu_to_le16(status);
                *(__u16 *)(buf + 2) = cpu_to_le16(cstatus);
                OK(4);
        case SetHubFeature:
                switch (wValue) {
                case C_HUB_OVER_CURRENT:
                case C_HUB_LOCAL_POWER:
                        break;
                default:
                        goto err;
                }
                break;
        case ClearHubFeature:
                switch (wValue) {
                case C_HUB_OVER_CURRENT:
                        OK(0);  /* hub power over current */
                default:
                        goto err;
                }
                break;
        case SetPortFeature:
                if (!wIndex || wIndex > uhci->rh_numports)
                        goto err;

                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        SET_RH_PORTSTAT(USBPORTSC_SUSP);
                        OK(0);
                case USB_PORT_FEAT_RESET:
                        SET_RH_PORTSTAT(USBPORTSC_PR);
                        mdelay(50);     /* USB v1.1 7.1.7.3 */
                        c_p_r[wIndex - 1] = 1;
                        CLR_RH_PORTSTAT(USBPORTSC_PR);
                        udelay(10);
                        SET_RH_PORTSTAT(USBPORTSC_PE);
                        mdelay(10);
                        SET_RH_PORTSTAT(0xa);
                        OK(0);
                case USB_PORT_FEAT_POWER:
                        OK(0); /* port power ** */
                case USB_PORT_FEAT_ENABLE:
                        SET_RH_PORTSTAT(USBPORTSC_PE);
                        OK(0);
                default:
                        goto err;
                }
                break;
        case ClearPortFeature:
                if (!wIndex || wIndex > uhci->rh_numports)
                        goto err;

                switch (wValue) {
                case USB_PORT_FEAT_ENABLE:
                        CLR_RH_PORTSTAT(USBPORTSC_PE);
                        OK(0);
                case USB_PORT_FEAT_C_ENABLE:
                        SET_RH_PORTSTAT(USBPORTSC_PEC);
                        OK(0);
                case USB_PORT_FEAT_SUSPEND:
                        CLR_RH_PORTSTAT(USBPORTSC_SUSP);
                        OK(0);
                case USB_PORT_FEAT_C_SUSPEND:
                        /*** WR_RH_PORTSTAT(RH_PS_PSSC); */
                        OK(0);
                case USB_PORT_FEAT_POWER:
                        OK(0);  /* port power */
                case USB_PORT_FEAT_C_CONNECTION:
                        SET_RH_PORTSTAT(USBPORTSC_CSC);
                        OK(0);
                case USB_PORT_FEAT_C_OVER_CURRENT:
                        OK(0);  /* port power over current */
                case USB_PORT_FEAT_C_RESET:
                        c_p_r[wIndex - 1] = 0;
                        OK(0);
                default:
                        goto err;
                }
                break;
        case GetHubDescriptor:
                len = min_t(unsigned int, wLength,
                          min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
                memcpy(buf, root_hub_hub_des, len);
                if (len > 2)
                        buf[2] = uhci->rh_numports;
                OK(len);
        default:
err:
                retval = -EPIPE;
        }

        return retval;
}