📄 usb-ohci.c
字号:
}
#ifdef DEBUG
ep_print_int_eds (ohci, "UNLINK_ISO");
#endif
break;
}
ed->state = ED_UNLINK;
return 0;
}
/*-------------------------------------------------------------------------*/
/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
* but the USB stack is a little bit stateless so we do it at every transaction
* if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
* in all other cases the state is left unchanged
* the ed info fields are setted anyway even though most of them should not change */
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*
* Done List handling functions
*-------------------------------------------------------------------------*/
/* calculate the transfer length and update the urb */
static void dl_transfer_length(td_t * td)
{
unsigned long tdINFO, tdBE, tdCBP;
unsigned short tdPSW;
urb_t * urb = td->urb;
urb_priv_t * urb_priv = urb->hcpriv;
int dlen = 0;
int cc = 0;
tdINFO = le32_to_cpup (&td->hwINFO);
tdBE = le32_to_cpup (&td->hwBE);
tdCBP = le32_to_cpup (&td->hwCBP);
if (tdINFO & TD_ISO) {
tdPSW = le16_to_cpu (td->hwPSW[0]);
cc = (tdPSW >> 12) & 0xF;
if (cc < 0xE) {
if (usb_pipeout(urb->pipe)) {
dlen = urb->iso_frame_desc[td->index].length;
} else {
dlen = tdPSW & 0x3ff;
}
urb->actual_length += dlen;
urb->iso_frame_desc[td->index].actual_length = dlen;
if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
cc = TD_CC_NOERROR;
urb->iso_frame_desc[td->index].status = cc_to_error[cc];
}
} else { /* BULK, INT, CONTROL DATA */
if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL &&
((td->index == 0) || (td->index == urb_priv->length - 1)))) {
if (tdBE != 0) {
if (td->hwCBP == 0)
urb->actual_length += tdBE - td->data_dma + 1;
else
urb->actual_length += tdCBP - td->data_dma;
}
}
}
}
/* handle an urb that is being unlinked */
static void dl_del_urb (urb_t * urb)
{
// wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
// urb_rm_priv_locked (urb);
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
urb->status = -ECONNRESET;
if (urb->complete)
urb->complete (urb);
} else {
urb->status = -ENOENT;
/* unblock sohci_unlink_urb */
// if (wait_head)
// wake_up (wait_head);
}
}
/*-------------------------------------------------------------------------*/
/* replies to the request have to be on a FIFO basis so
* we reverse the reversed done-list */
static td_t * dl_reverse_done_list (ohci_t * ohci)
{
unsigned long td_list_hc;
td_t * td_rev = NULL;
td_t * td_list = NULL;
urb_priv_t * urb_priv = NULL;
// unsigned long flags;
// spin_lock_irqsave (&usb_ed_lock, flags);
td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
ohci->hcca->done_head = 0;
while (td_list_hc) {
td_list = dma_to_td (ohci, td_list_hc);
if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
printf(" USB-error/status: 0x%x/0x%X\n",
TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
td_list->ed->hwHeadP =
(urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
(td_list->ed->hwHeadP & cpu_to_le32 (0x2));
urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
} else
td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
}
}
td_list->next_dl_td = td_rev;
td_rev = td_list;
td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;
}
// spin_unlock_irqrestore (&usb_ed_lock, flags);
return td_list;
}
/*-------------------------------------------------------------------------*/
/* there are some pending requests to remove
* - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
* - some URBs/TDs if urb_priv->state == URB_DEL */
static void dl_del_list (ohci_t * ohci, unsigned int frame)
{
// unsigned long flags;
ed_t * ed;
unsigned long edINFO;
unsigned long tdINFO;
td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
unsigned long * td_p;
int ctrl = 0, bulk = 0;
// spin_lock_irqsave (&usb_ed_lock, flags);
for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
edINFO = le32_to_cpup (&ed->hwINFO);
td_p = &ed->hwHeadP;
for (td = tdHeadP; td != tdTailP; td = td_next) {
urb_t * urb = td->urb;
urb_priv_t * urb_priv = td->urb->hcpriv;
td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
tdINFO = le32_to_cpup (&td->hwINFO);
if (TD_CC_GET (tdINFO) < 0xE)
dl_transfer_length (td);
*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
/* URB is done; clean up */
if (++(urb_priv->td_cnt) == urb_priv->length)
dl_del_urb (urb);
} else {
td_p = &td->hwNextTD;
}
}
if (ed->state & ED_DEL) { /* set by sohci_free_dev */
struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
td_free (ohci, tdTailP); /* free dummy td */
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
ed->state = ED_NEW;
// hash_free_ed(ohci, ed);
/* if all eds are removed wake up sohci_free_dev */
if (!--dev->ed_cnt) {
// wait_queue_head_t *wait_head = dev->wait;
;
// dev->wait = 0;
// if (wait_head)
// wake_up (wait_head);
}
} else {
ed->state &= ~ED_URB_DEL;
tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
if (tdHeadP == tdTailP) {
if (ed->state == ED_OPER)
ep_unlink(ohci, ed);
td_free (ohci, tdTailP);
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
ed->state = ED_NEW;
// hash_free_ed(ohci, ed);
--(usb_to_ohci (ohci->dev[edINFO & 0x7F]))->ed_cnt;
} else
ed->hwINFO &= ~(cpu_to_le32 (OHCI_ED_SKIP));
}
switch (ed->type) {
case PIPE_CONTROL:
ctrl = 1;
break;
case PIPE_BULK:
bulk = 1;
break;
}
}
/* maybe reenable control and bulk lists */
if (!ohci->disabled) {
if (ctrl) /* reset control list */
writel (0, &ohci->regs->ed_controlcurrent);
if (bulk) /* reset bulk list */
writel (0, &ohci->regs->ed_bulkcurrent);
if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
}
ohci->ed_rm_list[frame] = NULL;
// spin_unlock_irqrestore (&usb_ed_lock, flags);
}
/*-------------------------------------------------------------------------*/
/* td done list */
static void dl_done_list (ohci_t * ohci, td_t * td_list)
{
td_t * td_list_next = NULL;
ed_t * ed;
int cc = 0;
urb_t * urb;
urb_priv_t * urb_priv;
unsigned long tdINFO, edHeadP, edTailP;
// unsigned long flags;
while (td_list) {
td_list_next = td_list->next_dl_td;
urb = td_list->urb;
urb_priv = urb->hcpriv;
tdINFO = le32_to_cpup (&td_list->hwINFO);
ed = td_list->ed;
dl_transfer_length(td_list);
/* error code of transfer */
cc = TD_CC_GET (tdINFO);
printf("TD CC:0x%X\n",cc);
if (cc == TD_CC_STALL)
usb_endpoint_halt(urb->dev,
usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
if (!(urb->transfer_flags & USB_DISABLE_SPD)
&& (cc == TD_DATAUNDERRUN))
cc = TD_CC_NOERROR;
if (++(urb_priv->td_cnt) == urb_priv->length) {
if ((ed->state & (ED_OPER | ED_UNLINK))
&& (urb_priv->state != URB_DEL)) {
urb->status = cc_to_error[cc];
sohci_return_urb (ohci, urb);
} else {
// spin_lock_irqsave (&usb_ed_lock, flags);
dl_del_urb (urb);
// spin_unlock_irqrestore (&usb_ed_lock, flags);
}
}
// spin_lock_irqsave (&usb_ed_lock, flags);
if (ed->state != ED_NEW) {
edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
edTailP = le32_to_cpup (&ed->hwTailP);
/* unlink eds if they are not busy */
if ((edHeadP == edTailP) && (ed->state == ED_OPER))
ep_unlink (ohci, ed);
}
// spin_unlock_irqrestore (&usb_ed_lock, flags);
td_list = td_list_next;
}
}
/*-------------------------------------------------------------------------*
* Virtual Root Hub
*-------------------------------------------------------------------------*/
/* Device descriptor */
static __u8 root_hub_dev_des[] =
{
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, /* __u16 bcdUSB; v1.1 */
0x01,
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
0x00, /* __u16 idVendor; */
0x00,
0x00, /* __u16 idProduct; */
0x00,
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
/* 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;
int res;
unsigned char 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",
readl (&ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
return 0;
}
*(unsigned char *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
? 1: 0;
ret = *(unsigned char *) data;
for ( i = 0; i < num_ports; i++) {
*(unsigned char *) (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 += *(unsigned char *) (data + (i + 1) / 8);
// printf("Root hub Port[%d] status:%X\n",i+1,roothub_portstatus (ohci, i));
}
len = i/8 + 1;
res =(rh_len < sizeof(data)) ? rh_len : sizeof(data);
if (ret > 0) {
memcpy(rh_data, data,((len<res) ? len : res));
return len;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
void rh_int_timer_do ()
{
int len;
struct omap_dev *dev=&omap_hc_dev;
urb_t * urb =dev->ohci->rh.urb;
// urb_t * urb =dev->ohci->rh.urb;
// ohci_t * ohci = urb->dev->bus->hcpriv;
ohci_t * ohci =dev->ohci;
if (ohci->disabled)
return;
/* ignore timers firing during PM suspend, etc */
if ((ohci->hc_control & OHCI_CTRL_HCFS)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -