📄 usb_ohci.c
字号:
case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
size = (transfer_len == 0)? 2:
(transfer_len - 1) / 4096 + 3;
break;
}
if (size >= (N_URB_TD - 1)) {
err("need %d TDs, only have %d", size, N_URB_TD);
return -1;
}
purb_priv = &urb_priv;
purb_priv->pipe = pipe;
/* fill the private part of the URB */
purb_priv->length = size;
purb_priv->ed = ed;
purb_priv->actual_length = 0;
/* allocate the TDs */
/* note that td[0] was allocated in ep_add_ed */
for (i = 0; i < size; i++) {
purb_priv->td[i] = td_alloc (dev);
if (!purb_priv->td[i]) {
purb_priv->length = i;
urb_free_priv (purb_priv);
err("sohci_submit_job: ENOMEM");
return -1;
}
}
if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
urb_free_priv (purb_priv);
err("sohci_submit_job: EINVAL");
return -1;
}
/* link the ed into a chain if is not already */
if (ed->state != ED_OPER)
ep_link (ohci, ed);
/* fill the TDs and link it to the ed */
td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
/* tell us the current USB frame number */
static int sohci_get_current_frame_number (struct usb_device *usb_dev)
{
ohci_t *ohci = &gohci;
return m16_swap (ohci->hcca->frame_no);
}
#endif
/*-------------------------------------------------------------------------*
* ED handling functions
*-------------------------------------------------------------------------*/
/* link an ed into one of the HC chains */
static int ep_link (ohci_t *ohci, ed_t *edi)
{
volatile ed_t *ed = edi;
ed->state = ED_OPER;
switch (ed->type) {
case PIPE_CONTROL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
writel (ed, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->hwNextED = m32_swap (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 (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;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* 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 *ed)
{
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 (*((__u32 *)&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 (*((__u32 *)&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 (*((__u32 *)&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 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
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)
{
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 (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);
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 = (__u32)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 (data);
if (data)
td->hwBE = m32_swap (data + len - 1);
else
td->hwBE = 0;
td->hwNextTD = m32_swap (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;
void *data;
int cnt = 0;
__u32 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;
}
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)
{
__u32 tdINFO, tdBE, tdCBP;
urb_priv_t *lurb_priv = &urb_priv;
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)
{
__u32 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 = &urb_priv;
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);
}
}
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;
__u32 tdINFO, edHeadP, edTailP;
while (td_list) {
td_list_next = td_list->next_dl_td;
lurb_priv = &urb_priv;
tdINFO = m32_swap (td_list->hwINFO);
ed = td_list->ed;
dl_transfer_length(td_list);
/* error code of transfer */
cc = TD_CC_GET (tdINFO);
if (cc != 0) {
dbg("ConditionCode %#x", cc);
stat = cc_to_error[cc];
}
/* see if this done list makes for all TD's of current URB,
* and mark the URB finished if so */
if (++(lurb_priv->td_cnt) == lurb_priv->length) {
if ((ed->state & (ED_OPER | ED_UNLINK)))
urb_finished = 1;
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -