📄 usb-ohci.c
字号:
/* pci_map_single (ohci->ohci_dev, //modified by Changming HUANG
urb->setup_packet, 8,
PCI_DMA_TODEVICE),*/
mpuva_to_lbva(urb->setup_packet), //added by Changming HUANG
8, urb, cnt++);
// printf("setup_packet Virt address:%X\n",urb->setup_packet);
// printf("setup_packet LB address :%X\n",mpuva_to_lbva(urb->setup_packet));
if (data_len > 0) {
info = usb_pipeout (urb->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, urb, cnt++);
}
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, urb, cnt++);
// printf("start control list\n");
if (!ohci->sleeping){
// hc_register_info(ohci);
// printf("[Start Control]Command control:\t%X\n",readl(&ohci->regs->control));
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
// wait_ms(50);
// hc_register_info(ohci);
// printf("Start Control list\n");
}
break;
case PIPE_ISOCHRONOUS:
for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff),
data + urb->iso_frame_desc[cnt].offset,
urb->iso_frame_desc[cnt].length, urb, cnt);
}
break;
}
// if (urb_priv->length != cnt)
// printf("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
}
/*-------------------------------------------------------------------------*
* Interface functions (URB)
*-------------------------------------------------------------------------*/
/* return a request to the completion handler */
static int sohci_return_urb (struct ohci *hc, urb_t * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
urb_t * urbt;
// unsigned long flags;
int i;
if (!urb_priv)
return -1; /* urb already unlinked */
/* just to be sure */
if (!urb->complete) {
// urb_rm_priv (urb);
return -1;
}
#ifdef DEBUG
urb_print (urb, "RET", usb_pipeout (urb->pipe));
#endif
switch (usb_pipetype (urb->pipe)) {
case PIPE_INTERRUPT:
/* pci_unmap_single (hc->ohci_dev, //modified by Changming HUANG
urb_priv->td [0]->data_dma,
urb->transfer_buffer_length,
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);*/
urb->complete (urb);
/* implicitly requeued */
urb->actual_length = 0;
urb->status = USB_ST_URB_PENDING;
if (urb_priv->state != URB_DEL)
td_submit_urb (urb);
break;
case PIPE_ISOCHRONOUS:
for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
if (urbt) { /* send the reply and requeue URB */
/* pci_unmap_single (hc->ohci_dev, //modified by Changming HUANG
urb_priv->td [0]->data_dma,
urb->transfer_buffer_length,
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);*/
urb->complete (urb);
// spin_lock_irqsave (&usb_ed_lock, flags);
urb->actual_length = 0;
urb->status = USB_ST_URB_PENDING;
urb->start_frame = urb_priv->ed->last_iso + 1;
if (urb_priv->state != URB_DEL) {
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc[i].actual_length = 0;
urb->iso_frame_desc[i].status = -EXDEV;
}
td_submit_urb (urb);
}
// spin_unlock_irqrestore (&usb_ed_lock, flags);
} else { /* unlink URB, call complete */
// urb_rm_priv (urb);
urb->complete (urb);
;
}
break;
case PIPE_BULK:
case PIPE_CONTROL: /* unlink URB, call complete */
// urb_rm_priv (urb);
urb->complete (urb);
break;
}
return 0;
}
/*-------------------------------------------------------------------------*/
ed_t * ep_add_ed (
struct usb_device * usb_dev,
unsigned int pipe,
int interval,
int load
)
{
ohci_t * ohci = usb_dev->bus->hcpriv;
td_t * td;
ed_t * ed_ret;
volatile ed_t * ed;
// unsigned long flags;
// spin_lock_irqsave (&usb_ed_lock, flags);
ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
/* pending delete request */
// spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
// printf("PIPE is:%X\n",pipe);
if (ed->state == ED_NEW) {
// printf("ED NEW status,so skip the ed to next ed\n");
// ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
ed->hwINFO =OHCI_ED_SKIP; /* skip ed */
/* dummy td; end of td list for ed */
td = td_alloc (ohci);
printf("First TD MPUVA:0x%X LBVA:0x%X\n",td,td->td_dma);
/* hash the ed for later reverse mapping */
// if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
if (!td ) {
/* out of memory */
// if (td)
// td_free(ohci, td);
// spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
// ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwTailP = td->td_dma;
ed->hwHeadP = ed->hwTailP;
ed->state = ED_UNLINK;
ed->type = usb_pipetype (pipe);
usb_to_ohci (usb_dev)->ed_cnt++;
}
// printf("TD address:%X\n",ed->hwTailP);
ohci->dev[usb_pipedevice (pipe)] = usb_dev;
// ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
ed->hwINFO = (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, usb_pipeout (pipe)) << 16);
if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
ed->int_period = interval;
ed->int_load = load;
}
// spin_unlock_irqrestore (&usb_ed_lock, flags);
return ed_ret;
}
/*-------------------------------------------------------------------------*/
/* request the removal of an endpoint
* put the ep on the rm_list and request a stop of the bulk or ctrl list
* real removal is done at the next start frame (SF) hardware interrupt */
void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
{
unsigned int frame;
ohci_t * ohci = usb_dev->bus->hcpriv;
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
return;
ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
if (!ohci->disabled) {
switch (ed->type) {
case PIPE_CONTROL: /* stop control list */
ohci->hc_control &= ~OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
break;
case PIPE_BULK: /* stop bulk list */
ohci->hc_control &= ~OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
break;
}
}
frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
ed->ed_rm_list = ohci->ed_rm_list[frame];
ohci->ed_rm_list[frame] = ed;
if (!ohci->disabled && !ohci->sleeping) {
/* enable SOF interrupt */
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
printf("SOF Enable -ep_rm_ed\n");
}
}
/* get a transfer request */
static int sohci_submit_urb (urb_t * urb)
{
ohci_t * ohci;
ed_t * ed;
urb_priv_t * urb_priv;
unsigned int pipe = urb->pipe;
int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int i, size = 0;
// unsigned long flags;
int bustime = 0;
// int mem_flags = ALLOC_FLAGS;
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
if (urb->hcpriv) /* urb already in use */
return -EINVAL;
// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
// return -EPIPE;
usb_inc_dev_use (urb->dev);
ohci = (ohci_t *) urb->dev->bus->hcpriv;
#ifdef DEBUG
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
/* handle a request to the virtual root hub */
if (usb_pipedevice (pipe) == ohci->rh.devnum)
return rh_submit_urb (urb);
// printf("Root hub DevNum:%d\n",ohci->rh.devnum);//added by Changming HUANG
// printf("PIPE device is:%d\n",usb_pipedevice (pipe));
/* when controller's hung, permit only roothub cleanup attempts
* such as powering down ports */
if (ohci->disabled) {
usb_dec_dev_use (urb->dev);
return -ESHUTDOWN;
}
/* every endpoint has a ed, locate and fill it */
if (!(ed = (ed_t *)ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
usb_dec_dev_use (urb->dev);
return -ENOMEM;
}
/* for the private part of the URB we need the number of TDs (size) */
switch (usb_pipetype (pipe)) {
case PIPE_BULK: /* one TD for every 4096 Byte */
size = (urb->transfer_buffer_length - 1) / 4096 + 1;
/* If the transfer size is multiple of the pipe mtu,
* we may need an extra TD to create a empty frame
* Jean II */
if ((urb->transfer_flags & USB_ZERO_PACKET) &&
usb_pipeout (pipe) &&
(urb->transfer_buffer_length != 0) &&
((urb->transfer_buffer_length % maxps) == 0))
size++;
break;
case PIPE_ISOCHRONOUS: /* number of packets from URB */
size = urb->number_of_packets;
if (size <= 0) {
usb_dec_dev_use (urb->dev);
return -EINVAL;
}
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc[i].actual_length = 0;
urb->iso_frame_desc[i].status = -EXDEV;
}
break;
case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
size = (urb->transfer_buffer_length == 0)? 2:
(urb->transfer_buffer_length - 1) / 4096 + 3;
break;
case PIPE_INTERRUPT: /* one TD */
size = 1;
break;
}
/* allocate the private part of the URB */
urb_priv = (urb_priv_t *)malloc (sizeof (urb_priv_t) + size * sizeof (td_t *));
if (!urb_priv) {
usb_dec_dev_use (urb->dev);
return -ENOMEM;
}
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
/* fill the private part of the URB */
urb_priv->length = size;
urb_priv->ed = ed;
/* allocate the TDs (updating hash chains) */
// spin_lock_irqsave (&usb_ed_lock, flags);
for (i = 0; i < size; i++) {
urb_priv->td[i] = td_alloc (ohci);
printf("TD[%d] MPUVA:0x%X LBVA:0x%X\n",i,urb_priv->td[i],urb_priv->td[i]->td_dma);
if (!urb_priv->td[i]) {
urb_priv->length = i;
urb_free_priv (ohci, urb_priv);
// spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev);
return -ENOMEM;
}
}
if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
urb_free_priv (ohci, urb_priv);
// spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev);
return -EINVAL;
}
/* allocate and claim bandwidth if needed; ISO
* needs start frame index if it was't provided.
*/
switch (usb_pipetype (pipe)) {
case PIPE_ISOCHRONOUS:
if (urb->transfer_flags & USB_ISO_ASAP) {
urb->start_frame = ((ed->state == ED_OPER)
? (ed->last_iso + 1)
// : (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
: ((ohci->hcca->frame_no) + 10)) & 0xffff;
}
/* FALLTHROUGH */
case PIPE_INTERRUPT:
if (urb->bandwidth == 0) {
bustime = usb_check_bandwidth (urb->dev, urb);
}
if (bustime < 0) {
urb_free_priv (ohci, urb_priv);
// spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev);
return bustime;
}
usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
#ifdef DO_TIMEOUTS
urb->timeout = 0;
#endif
}
urb->actual_length = 0;
urb->hcpriv = urb_priv;
urb->status = USB_ST_URB_PENDING;
/* 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_urb (urb);
#ifdef DO_TIMEOUTS
/* maybe add to ordered list of timeouts */
if (urb->timeout) {
struct list_head *entry;
// FIXME: usb-uhci uses relative timeouts (like this),
// while uhci uses absolute ones (probably better).
// Pick one solution and change the affected drivers.
urb->timeout += jiffies;
list_for_each (entry, &ohci->timeout_list) {
struct urb *next_urb;
next_urb = list_entry (entry, struct urb, urb_list);
if (time_after_eq (urb->timeout, next_urb->timeout))
break;
}
list_add (&urb->urb_list, entry);
/* drive timeouts by SF (messy, but works) */
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
printf("SOF Enable -urb->timeout\n");
}
#endif
// spin_unlock_irqrestore (&usb_ed_lock, flags);
printf("ED->hwHeadP:0x%X\thwTailP:0x%X\n\n",urb_priv->ed->hwHeadP,urb_priv->ed->hwTailP);//added by hcm
return 0;
}
/*-------------------------------------------------------------------------*/
/* deactivate all TDs and remove the private part of the URB */
/* interrupt callers must use async unlink mode */
static int sohci_unlink_urb (urb_t * urb)
{
// unsigned long flags;
ohci_t * ohci;
if (!urb) /* just to be sure */
return -EINVAL;
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
ohci = (ohci_t *) urb->dev->bus->hcpriv;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -