📄 usb_ohci.c
字号:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
writel (ed, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);
}
ed->ed_prev = ohci->ed_controltail;
if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_controltail = edi;
break;
case PIPE_BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
writel (ed, &ohci->regs->ed_bulkhead);
} else {
ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);
}
ed->ed_prev = ohci->ed_bulktail;
if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_bulktail = edi;
break;
case PIPE_INTERRUPT:
load = ed->int_load;
interval = ep_2_n_interval (ed->int_period);
ed->int_interval = interval;
int_branch = ep_int_ballance (ohci, interval, load);
ed->int_branch = int_branch;
for (i = 0; i < ep_rev (6, interval); i += inter) {
inter = 1;
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
(*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval);
ed_p = &(((ed_t *)ed_p)->hwNextED))
{ inter = ep_rev (6, ((ed_t *)ed_p)->int_interval); }
ed->hwNextED = *ed_p;
*ed_p = m32_swap((unsigned long)ed);
}
break;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* scan the periodic table to find and unlink this ED */
static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed,
unsigned index, unsigned period)
{
for (; index < NUM_INTS; index += period) {
unsigned int *ed_p = &ohci->hcca->int_table [index];
/* ED might have been unlinked through another path */
while (*ed_p != 0) {
if (((struct ed *)m32_swap ((unsigned long)ed_p)) == ed) {
*ed_p = ed->hwNextED;
break;
}
ed_p = & (((struct ed *)m32_swap ((unsigned long)ed_p))->hwNextED);
}
}
}
/* unlink an ed from one of the HC chains.
* just the link to the ed is unlinked.
* the link from the ed still points to another operational ed or 0
* so the HC can eventually finish the processing of the unlinked ed */
static int ep_unlink (ohci_t *ohci, ed_t *edi)
{
volatile ed_t *ed = edi;
int i;
ed->hwINFO |= m32_swap (OHCI_ED_SKIP);
switch (ed->type) {
case PIPE_CONTROL:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
}
writel (m32_swap (*((unsigned int *)&ed->hwNextED)), &ohci->regs->ed_controlhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
} else {
((ed_t *)m32_swap (*((unsigned int *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
case PIPE_BULK:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
writel (m32_swap (*((unsigned int *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
} else {
((ed_t *)m32_swap (*((unsigned int *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
case PIPE_INTERRUPT:
periodic_unlink (ohci, ed, 0, 1);
for (i = ed->int_branch; i < 32; i += ed->int_interval)
ohci->ohci_int_load[i] -= ed->int_load;
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
*/
static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe,
int interval, int load)
{
td_t *td;
ed_t *ed_ret;
volatile ed_t *ed;
ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |
(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
err("ep_add_ed: pending delete");
/* pending delete request */
return NULL;
}
if (ed->state == ED_NEW) {
ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
/* dummy td; end of td list for ed */
td = td_alloc (usb_dev);
ed->hwTailP = m32_swap ((unsigned long)td);
ed->hwHeadP = ed->hwTailP;
ed->state = ED_UNLINK;
ed->type = usb_pipetype (pipe);
ohci_dev.ed_cnt++;
}
ed->hwINFO = m32_swap (usb_pipedevice (pipe)
| usb_pipeendpoint (pipe) << 7
| (usb_pipeisoc (pipe)? 0x8000: 0)
| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
| usb_pipeslow (pipe) << 13
| usb_maxpacket (usb_dev, pipe) << 16);
if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
ed->int_period = interval;
ed->int_load = load;
}
return ed_ret;
}
/*-------------------------------------------------------------------------*
* TD handling functions
*-------------------------------------------------------------------------*/
/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
static void td_fill (ohci_t *ohci, unsigned int info,
void *data, int len,
struct usb_device *dev, int index, urb_priv_t *urb_priv)
{
volatile td_t *td, *td_pt;
#ifdef OHCI_FILL_TRACE
int i;
#endif
if (index > urb_priv->length) {
err("index > length");
return;
}
/* use this td as the next dummy */
td_pt = urb_priv->td [index];
td_pt->hwNextTD = 0;
/* fill the old dummy TD */
td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);
td->ed = urb_priv->ed;
td->next_dl_td = NULL;
td->index = index;
td->data = (unsigned int)data;
#ifdef OHCI_FILL_TRACE
if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {
for (i = 0; i < len; i++)
printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
printf("\n");
}
#endif
if (!len)
data = 0;
td->hwINFO = m32_swap (info);
td->hwCBP = m32_swap ((unsigned long)data);
if (data)
{ td->hwBE = m32_swap ((unsigned long)data + len - 1); }
else
{ td->hwBE = 0; }
td->hwNextTD = m32_swap ((unsigned long)td_pt);
/* append to queue */
td->ed->hwTailP = td->hwNextTD;
}
/*-------------------------------------------------------------------------*/
/* prepare all TDs of a transfer */
static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval)
{
ohci_t *ohci = &gohci;
int data_len = transfer_len;
unsigned char *data;
int cnt = 0;
unsigned int info = 0;
unsigned int toggle = 0;
/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
toggle = TD_T_TOGGLE;
} else {
toggle = TD_T_DATA0;
usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
}
urb->td_cnt = 0;
if (data_len)
data = buffer;
else
data = 0;
switch (usb_pipetype (pipe)) {
case PIPE_BULK:
info = usb_pipeout (pipe)?
TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
while(data_len > 4096) {
td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);
data += 4096; data_len -= 4096; cnt++;
}
info = usb_pipeout (pipe)?
TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);
cnt++;
if (!ohci->sleeping)
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
break;
case PIPE_CONTROL:
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
td_fill (ohci, info, setup, 8, dev, cnt++, urb);
if (data_len > 0) {
info = usb_pipeout (pipe)?
TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
/* NOTE: mishandles transfers >8K, some >4K */
td_fill (ohci, info, data, data_len, dev, cnt++, urb);
}
info = usb_pipeout (pipe)?
TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, dev, cnt++, urb);
if (!ohci->sleeping)
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
case PIPE_INTERRUPT:
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_OUT | toggle:
TD_CC | TD_R | TD_DP_IN | toggle;
td_fill (ohci, info, data, data_len, dev, cnt++, urb);
break;
}
if (urb->length != cnt)
dbg("TD LENGTH %d != CNT %d", urb->length, cnt);
}
/*-------------------------------------------------------------------------*
* Done List handling functions
*-------------------------------------------------------------------------*/
/* calculate the transfer length and update the urb */
static void dl_transfer_length(td_t * td)
{
unsigned int tdBE, tdCBP;
urb_priv_t *lurb_priv = td->ed->purb;
//tdINFO = m32_swap (td->hwINFO);
tdBE = m32_swap (td->hwBE);
tdCBP = m32_swap (td->hwCBP);
if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&
((td->index == 0) || (td->index == lurb_priv->length - 1)))) {
if (tdBE != 0) {
if (td->hwCBP == 0)
lurb_priv->actual_length += tdBE - td->data + 1;
else
lurb_priv->actual_length += tdCBP - td->data;
}
}
}
/*-------------------------------------------------------------------------*/
/* 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 int td_list_hc;
td_t *td_rev = NULL;
td_t *td_list = NULL;
urb_priv_t *lurb_priv = NULL;
td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0;
ohci->hcca->done_head = 0;
while (td_list_hc) {
td_list = (td_t *)td_list_hc;
if (TD_CC_GET (m32_swap (td_list->hwINFO))) {
lurb_priv = td_list->ed->purb;
dbg(" USB-error/status: %x : %p",
TD_CC_GET (m32_swap (td_list->hwINFO)), td_list);
if (td_list->ed->hwHeadP & m32_swap (0x1)) {
if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) {
td_list->ed->hwHeadP =
(lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) |
(td_list->ed->hwHeadP & m32_swap (0x2));
lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1;
} else
td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
}
#ifdef CONFIG_MPC5200
td_list->hwNextTD = 0;
#endif
}
td_list->next_dl_td = td_rev;
td_rev = td_list;
td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0;
}
return td_list;
}
/*-------------------------------------------------------------------------*/
/* td done list */
static int dl_done_list (ohci_t *ohci, td_t *td_list)
{
td_t *td_list_next = NULL;
ed_t *ed;
int cc = 0;
int stat = 0;
/* urb_t *urb; */
urb_priv_t *lurb_priv;
unsigned int tdINFO, edHeadP, edTailP;
while (td_list) {
td_list_next = td_list->next_dl_td;
tdINFO = m32_swap (td_list->hwINFO);
ed = td_list->ed;
lurb_priv = ed->purb;
dl_transfer_length(td_list);
/* error code of transfer */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -