📄 usb_ohci.c
字号:
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;
//s_UartPrint("hc_contro|= OHCI_CTRL_CLE\n");
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;
//s_UartPrint("hc_contro|= OHCI_CTRL_BLE\n");
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;
//#define OHCI_FILL_TRACE
#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++)
// s_UartPrint("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
// s_UartPrint("\n");
//}
//if ((usb_pipetype(urb_priv->pipe) == PIPE_CONTROL) && usb_pipeout(urb_priv->pipe)) {
// for (i = 0; i < len; i++)
// s_UartPrint("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
// s_UartPrint("\n");
//}
#endif
if (!len)
data = 0;
td->hwINFO = m32_swap (info);
td->hwCBP = m32_swap (data);
if (data)
td->hwBE = m32_swap (((int)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,temp;
/* 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;
//dbg("TD_T_TOGGLE data_len=%d\n",data_len);
}
else
{
toggle = TD_T_DATA0;
usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
//dbg("TD_T_DATA0 data_len=%d\n",data_len);
}
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);
(int)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)
{
//dbg("\nstep2:");
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
//writel (OHCI_CLF, &ohci->regs->cmdstatus);
temp=rHcCommonStatus;
}
dbg("\nstep3: cmd=%04x %04x",rHcCommonStatus,temp);
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
dbg("dl_done_list: strange.., ED state %x, ed->state\n");
}
else
dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt,
lurb_priv->length);
if (ed->state != ED_NEW)
{
edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0;
edTailP = m32_swap (ed->hwTailP);
/* unlink eds if they are not busy */
if ((edHeadP == edTailP) && (ed->state == ED_OPER))
ep_unlink (ohci, ed);
}
td_list = td_list_next;
}
return stat;
}
/*-------------------------------------------------------------------------*
* 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; */
0x01, /* __u8 iProduct; */
0x00, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -