📄 usb-ohci.c
字号:
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 (leni, min (sizeof (root_hub_dev_des), wLength));
data_buf = root_hub_dev_des; OK(len);
case (0x02): /* configuration descriptor */
len = min (leni, min (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 (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 (leni, min (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(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->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
/* required for AMD-756 and some Mac platforms */
Ohci_WriteReg ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
&ohci->regs->roothub.a);
Ohci_WriteReg (RH_HS_LPSC, &ohci->regs->roothub.status);
#endif /* OHCI_USE_NPS */
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
/* connect the virtual root hub */
ohci->rh.devnum = 0;
usb_dev = usb_alloc_dev (NULL, ohci->bus);
if (!usb_dev) {
ohci->disabled = 1;
return -ENOMEM;
}
dev = usb_to_ohci (usb_dev);
ohci->bus->root_hub = usb_dev;
usb_connect (usb_dev);
if (usb_new_device (usb_dev) != 0) {
usb_free_dev (usb_dev);
ohci->disabled = 1;
return -ENODEV;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* called only from interrupt handler */
static void check_timeouts (struct ohci *ohci)
{
spin_lock (&usb_ed_lock);
while (!list_empty (&ohci->timeout_list)) {
struct urb *urb;
urb = list_entry (ohci->timeout_list.next, struct urb, urb_list);
if (time_after (jiffies, urb->timeout))
break;
list_del_init (&urb->urb_list);
if (urb->status != -EINPROGRESS)
continue;
urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
spin_unlock (&usb_ed_lock);
// outside the interrupt handler (in a timer...)
// this reference would race interrupts
sohci_unlink_urb (urb);
spin_lock (&usb_ed_lock);
}
spin_unlock (&usb_ed_lock);
}
/*-------------------------------------------------------------------------*/
/* an interrupt happens */
void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
{
ohci_t * ohci = __ohci;
struct ohci_regs * regs = ohci->regs;
int ints;
if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
ints = OHCI_INTR_WDH;
} else if ((ints = (Ohci_ReadRegister (®s->intrstatus) & Ohci_ReadRegister (®s->intrenable))) == 0) {
return;
}
// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
if (ints & OHCI_INTR_UE) {
ohci->disabled++;
err ("OHCI Unrecoverable Error, controller usb-%s disabled",
ohci->slot_name);
// e.g. due to PCI Master/Target Abort
#ifdef DEBUG
ohci_dump (ohci, 1);
#else
// FIXME: be optimistic, hope that bug won't repeat often.
// Make some non-interrupt context restart the controller.
// Count and limit the retries though; either hardware or
// software errors can go forever...
#endif
hc_reset (ohci);
}
if (ints & OHCI_INTR_WDH) {
Ohci_WriteReg (OHCI_INTR_WDH, ®s->intrdisable);
dl_done_list (ohci, dl_reverse_done_list (ohci));
Ohci_WriteReg (OHCI_INTR_WDH, ®s->intrenable);
}
if (ints & OHCI_INTR_SO) {
dbg("USB Schedule overrun");
O
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -