📄 usb-ohci.c
字号:
/* Configuration descriptor */
static __u8 root_hub_config_des[] =
{
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, /* __u16 wTotalLength; */
0x00,
0x01, /* __u8 bNumInterfaces; */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
0x00, /* __u8 MaxPower; */
/* interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* endpoint */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
0x00,
0xff /* __u8 ep_bInterval; 255 ms */
};
/* Hub class-specific descriptor is constructed dynamically */
/*-------------------------------------------------------------------------*/
/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */
static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
{
int num_ports;
int i;
int ret;
int len;
__u8 data[8];
num_ports = roothub_a (ohci) & RH_A_NDP;
if (num_ports > MAX_ROOT_PORTS) {
err ("bogus NDP=%d for OHCI usb-%s", num_ports,
ohci->ohci_dev->slot_name);
err ("rereads as NDP=%d",
Ohci_ReadRegister (&ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
return 0;
}
*(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
? 1: 0;
ret = *(__u8 *) data;
for ( i = 0; i < num_ports; i++) {
*(__u8 *) (data + (i + 1) / 8) |=
((roothub_portstatus (ohci, i) &
(RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC))
? 1: 0) << ((i + 1) % 8);
ret += *(__u8 *) (data + (i + 1) / 8);
}
len = i/8 + 1;
if (ret > 0) {
memcpy(rh_data, data,
min_t(unsigned int, len,
min_t(unsigned int, rh_len, sizeof(data))));
return len;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
static void rh_int_timer_do (unsigned long ptr)
{
int len;
urb_t * urb = (urb_t *) ptr;
ohci_t * ohci = urb->dev->bus->hcpriv;
if (ohci->disabled)
return;
/* ignore timers firing during PM suspend, etc */
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
goto out;
if(ohci->rh.send) {
len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
if (len > 0) {
urb->actual_length = len;
#ifdef DEBUG
urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
#endif
if (urb->complete)
urb->complete (urb);
}
}
out:
rh_init_int_timer (urb);
}
/*-------------------------------------------------------------------------*/
/* Root Hub INTs are polled by this timer */
static int rh_init_int_timer (urb_t * urb)
{
ohci_t * ohci = urb->dev->bus->hcpriv;
ohci->rh.interval = urb->interval;
init_timer (&ohci->rh.rh_int_timer);
ohci->rh.rh_int_timer.function = rh_int_timer_do;
ohci->rh.rh_int_timer.data = (unsigned long) urb;
ohci->rh.rh_int_timer.expires =
jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
add_timer (&ohci->rh.rh_int_timer);
return 0;
}
/*-------------------------------------------------------------------------*/
#define OK(x) len = (x); break
#define WR_RH_STAT(x) Ohci_WriteReg((x), &ohci->regs->roothub.status)
#define WR_RH_PORTSTAT(x) Ohci_WriteReg((x), &ohci->regs->roothub.portstatus[wIndex-1])
#define RD_RH_STAT roothub_status(ohci)
#define RD_RH_PORTSTAT roothub_portstatus(ohci,wIndex-1)
/* request to virtual root hub */
static int rh_submit_urb (urb_t * urb)
{
struct usb_device * usb_dev = urb->dev;
ohci_t * ohci = usb_dev->bus->hcpriv;
unsigned int pipe = urb->pipe;
devrequest * cmd = (devrequest *) urb->setup_packet;
void * data = urb->transfer_buffer;
int leni = urb->transfer_buffer_length;
int len = 0;
int status = TD_CC_NOERROR;
__u32 datab[4];
__u8 * data_buf = (__u8 *) datab;
__u16 bmRType_bReq;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
if (usb_pipeint(pipe)) {
ohci->rh.urb = urb;
ohci->rh.send = 1;
ohci->rh.interval = urb->interval;
rh_init_int_timer(urb);
urb->status = cc_to_error [TD_CC_NOERROR];
return 0;
}
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
wValue = le16_to_cpu (cmd->value);
wIndex = le16_to_cpu (cmd->index);
wLength = le16_to_cpu (cmd->length);
switch (bmRType_bReq) {
/* 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 RH_GET_STATUS:
*(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
case RH_GET_STATUS | RH_INTERFACE:
*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
case RH_GET_STATUS | RH_ENDPOINT:
*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
case RH_GET_STATUS | RH_CLASS:
*(__u32 *) data_buf = cpu_to_le32 (
RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
OK (4);
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
case RH_CLEAR_FEATURE | RH_ENDPOINT:
switch (wValue) {
case (RH_ENDPOINT_STALL): OK (0);
}
break;
case RH_CLEAR_FEATURE | RH_CLASS:
switch (wValue) {
case RH_C_HUB_LOCAL_POWER:
OK(0);
case (RH_C_HUB_OVER_CURRENT):
WR_RH_STAT(RH_HS_OCIC); OK (0);
}
break;
case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case (RH_PORT_ENABLE):
WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
case (RH_PORT_SUSPEND):
WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
case (RH_PORT_POWER):
WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
case (RH_C_PORT_CONNECTION):
WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
case (RH_C_PORT_ENABLE):
WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
case (RH_C_PORT_SUSPEND):
WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
case (RH_C_PORT_OVER_CURRENT):
WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
case (RH_C_PORT_RESET):
WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
}
break;
case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case (RH_PORT_SUSPEND):
WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
if (RD_RH_PORTSTAT & RH_PS_CCS)
WR_RH_PORTSTAT (RH_PS_PRS);
OK (0);
case (RH_PORT_POWER):
WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
if (RD_RH_PORTSTAT & RH_PS_CCS)
WR_RH_PORTSTAT (RH_PS_PES );
OK (0);
}
break;
case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
case RH_GET_DESCRIPTOR:
switch ((wValue & 0xff00) >> 8) {
case (0x01): /* device descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof (root_hub_dev_des),
wLength));
data_buf = root_hub_dev_des; OK(len);
case (0x02): /* configuration descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof (root_hub_config_des),
wLength));
data_buf = root_hub_config_des; OK(len);
case (0x03): /* string descriptors */
len = usb_root_hub_string (wValue & 0xff,
(int)(long) ohci->regs, "OHCI",
data, wLength);
if (len > 0) {
data_buf = data;
OK(min_t(int, leni, len));
}
// else fallthrough
default:
status = TD_CC_STALL;
}
break;
case RH_GET_DESCRIPTOR | RH_CLASS:
{
__u32 temp = roothub_a (ohci);
data_buf [0] = 9; // min length;
data_buf [1] = 0x29;
data_buf [2] = temp & RH_A_NDP;
data_buf [3] = 0;
if (temp & RH_A_PSM) /* per-port power switching? */
data_buf [3] |= 0x1;
if (temp & RH_A_NOCP) /* no overcurrent reporting? */
data_buf [3] |= 0x10;
else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */
data_buf [3] |= 0x8;
datab [1] = 0;
data_buf [5] = (temp & RH_A_POTPGT) >> 24;
temp = roothub_b (ohci);
data_buf [7] = temp & RH_B_DR;
if (data_buf [2] < 7) {
data_buf [8] = 0xff;
} else {
data_buf [0] += 2;
data_buf [8] = (temp & RH_B_DR) >> 8;
data_buf [10] = data_buf [9] = 0xff;
}
len = min_t(unsigned int, leni,
min_t(unsigned int, data_buf [0], wLength));
OK (len);
}
case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
default:
dbg ("unsupported root hub command");
status = TD_CC_STALL;
}
#ifdef DEBUG
// ohci_dump_roothub (ohci, 0);
#endif
len = min_t(int, len, leni);
if (data != data_buf)
memcpy (data, data_buf, len);
urb->actual_length = len;
urb->status = cc_to_error [status];
#ifdef DEBUG
urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
#endif
urb->hcpriv = NULL;
usb_dec_dev_use (usb_dev);
urb->dev = NULL;
if (urb->complete)
urb->complete (urb);
return 0;
}
/*-------------------------------------------------------------------------*/
static int rh_unlink_urb (urb_t * urb)
{
ohci_t * ohci = urb->dev->bus->hcpriv;
if (ohci->rh.urb == urb) {
ohci->rh.send = 0;
del_timer (&ohci->rh.rh_int_timer);
ohci->rh.urb = NULL;
urb->hcpriv = NULL;
usb_dec_dev_use(urb->dev);
urb->dev = NULL;
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
urb->status = -ECONNRESET;
if (urb->complete)
urb->complete (urb);
} else
urb->status = -ENOENT;
}
return 0;
}
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
/* reset the HC and BUS */
static int hc_reset (ohci_t * ohci)
{
int timeout = 30;
int smm_timeout = 50; /* 0,5 sec */
if (Ohci_ReadRegister (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
Ohci_WriteReg (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
dbg("USB HC TakeOver from SMM");
while (Ohci_ReadRegister (&ohci->regs->control) & OHCI_CTRL_IR) {
wait_ms (10);
if (--smm_timeout == 0) {
err("USB HC TakeOver failed!");
return -1;
}
}
}
/* Disable HC interrupts */
Ohci_WriteReg (OHCI_INTR_MIE, &ohci->regs->intrdisable);
dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
ohci->ohci_dev->slot_name,
Ohci_ReadRegister (&ohci->regs->control));
/* Reset USB (needed by some controllers) */
Ohci_WriteReg (0, &ohci->regs->control);
/* HC Reset requires max 10 ms delay */
Ohci_WriteReg (OHCI_HCR, &ohci->regs->cmdstatus);
while ((Ohci_ReadRegister (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--timeout == 0) {
err("USB HC reset timed out!");
return -1;
}
udelay (1);
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* Start an OHCI controller, set the BUS operational
* enable interrupts
* connect the virtual root hub */
static int hc_start (ohci_t * ohci)
{
__u32 mask;
unsigned int fminterval;
struct usb_device * usb_dev;
struct ohci_device * dev;
ohci->disabled = 1;
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
Ohci_WriteReg (0, &ohci->regs->ed_controlhead);
Ohci_WriteReg (0, &ohci->regs->ed_bulkhead);
Ohci_WriteReg (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */
fminterval = 0x2edf;
Ohci_WriteReg ((fminterval * 9) / 10, &ohci->regs->periodicstart);
fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
Ohci_WriteReg (fminterval, &ohci->regs->fminterval);
Ohci_WriteReg (0x628, &ohci->regs->lsthresh);
/* start controller operations */
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci->disabled = 0;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
Ohci_WriteReg (mask, &ohci->regs->intrenable);
Ohci_WriteReg (mask, &ohci->regs->intrstatus);
#ifdef OHCI_USE_NPS
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -