📄 usb-host.c
字号:
nop(); if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { data = *R_USB_EPT_DATA_ISO; restore_flags(flags); if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) && (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) && (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) && (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) { dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", i, devnum, endpoint, out_traffic ? "OUT" : "IN"); DBFEXIT; return i; } } else { data = *R_USB_EPT_DATA; restore_flags(flags); if ((IO_MASK(R_USB_EPT_DATA, valid) & data) && (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) && (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) && (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) && (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) { dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", i, devnum, endpoint, out_traffic ? "OUT" : "IN"); DBFEXIT; return i; } } } } DBFEXIT; return -1;}static int etrax_usb_allocate_epid(void){ int i; DBFENTER; for (i = 0; i < NBR_OF_EPIDS; i++) { if (!test_bit(i, (void *)&epid_usage_bitmask)) { dbg_epid("Found free epid %d", i); DBFEXIT; return i; } } dbg_epid("Found no free epids"); DBFEXIT; return -1;}static int etrax_usb_submit_urb(urb_t *urb){ etrax_hc_t *hc; int ret = -EINVAL; DBFENTER; if (!urb->dev || !urb->dev->bus) { return -ENODEV; } if (urb->next != NULL) { /* Is it possible for urb to be the head of a list of urbs (via the urb's next pointer), used for example in drivers for isochronous traffic. I haven't seen a device driver that relies on it being used for submit or unlink, so we warn about it and ignore it. */ warn("Urbs are linked, ignoring."); } if (urb->timeout) { /* FIXME. */ warn("urb->timeout specified, ignoring."); } hc = (etrax_hc_t*)urb->dev->bus->hcpriv; if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { /* This request is for the Virtual Root Hub. */ ret = etrax_rh_submit_urb(urb); } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { ret = etrax_usb_submit_bulk_urb(urb); } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { ret = etrax_usb_submit_ctrl_urb(urb); } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { int bustime; if (urb->bandwidth == 0) { bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; } else { ret = etrax_usb_submit_intr_urb(urb); if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else { /* Bandwidth already set. */ ret = etrax_usb_submit_intr_urb(urb); } } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { int bustime; if (urb->bandwidth == 0) { bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; } else { ret = etrax_usb_submit_isoc_urb(urb); if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else { /* Bandwidth already set. */ ret = etrax_usb_submit_isoc_urb(urb); } } DBFEXIT; return ret;}static int etrax_usb_unlink_urb(urb_t *urb){ etrax_hc_t *hc; etrax_urb_priv_t *urb_priv; int epid; DBFENTER; if (!urb) { return -EINVAL; } if (!urb->dev || !urb->dev->bus) { return -ENODEV; } if (!urb->hcpriv) { /* This happens if a device driver calls unlink on an urb that was never submitted (lazy driver). */ return 0; } if (urb->transfer_flags & USB_ASYNC_UNLINK) { /* FIXME. */ /* If USB_ASYNC_UNLINK is set: unlink move to a separate urb list call complete at next sof with ECONNRESET If not: wait 1 ms unlink call complete with ENOENT */ warn("USB_ASYNC_UNLINK set, ignoring."); } /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking, but that doesn't work for interrupt and isochronous traffic since they are completed repeatedly, and urb->status is set then. That may in itself be a bug though. */ hc = urb->dev->bus->hcpriv; urb_priv = (etrax_urb_priv_t *)urb->hcpriv; epid = urb_priv->epid; /* Set the urb status (synchronous unlink). */ urb->status = -ENOENT; urb_priv->urb_state = UNLINK; if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { int ret; ret = etrax_rh_unlink_urb(urb); DBFEXIT; return ret; } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb); if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { /* The EP was enabled, disable it and wait. */ TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); /* Ah, the luxury of busy-wait. */ while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid])); } /* Kicking dummy list out of the party. */ TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb); if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { /* The EP was enabled, disable it and wait. */ TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); /* Ah, the luxury of busy-wait. */ while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid])); } } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb); /* Separate function because it's a tad more complicated. */ etrax_usb_unlink_intr_urb(urb); } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb); if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { /* The EP was enabled, disable it and wait. */ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); /* Ah, the luxury of busy-wait. */ while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); } } /* Note that we need to remove the urb from the urb list *before* removing its SB descriptors. (This means that the isoc eof handler might get a null urb when we are unlinking the last urb.) */ urb_list_del(urb, epid); if (usb_pipetype(urb->pipe) == PIPE_BULK) { TxBulkEPList[epid].sub = 0; etrax_remove_from_sb_list(urb); } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { TxCtrlEPList[epid].sub = 0; etrax_remove_from_sb_list(urb); } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { /* Sanity check (should never happen). */ assert(urb_list_empty(epid)); /* Release allocated bandwidth. */ usb_release_bandwidth(urb->dev, urb, 0); } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { if (usb_pipeout(urb->pipe)) { /* FIXME: If the SB list isn't empty at this point, we need to set up the EP descriptor again. */ } else { /* For in traffic there is only one SB descriptor for each EP even though there may be several urbs (all urbs point at the same SB). */ if (urb_list_empty(epid)) { /* No more urbs, remove the SB. */ TxIsocEPList[epid].sub = 0; etrax_remove_from_sb_list(urb); } } /* Release allocated bandwidth. */ usb_release_bandwidth(urb->dev, urb, 1); } /* Must be done before calling completion handler. */ kfree(urb_priv); urb->hcpriv = 0; if (urb->complete) { urb->complete(urb); } /* Free the epid if urb list is empty. */ if (urb_list_empty(epid)) { etrax_usb_free_epid(epid); } DBFEXIT; return 0;}static int etrax_usb_get_frame_number(struct usb_device *usb_dev){ DBFENTER; DBFEXIT; return (*R_USB_FM_NUMBER & 0x7ff);}static int etrax_usb_allocate_dev(struct usb_device *usb_dev){ DBFENTER; DBFEXIT; return 0;}static int etrax_usb_deallocate_dev(struct usb_device *usb_dev){ DBFENTER; DBFEXIT; return 0;}static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs){ DBFENTER; /* This interrupt handler could be used when unlinking EP descriptors. */ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { USB_EP_Desc_t *ep; //dbg_bulk("dma8_sub0_descr (BULK) intr."); /* It should be safe clearing the interrupt here, since we don't expect to get a new one until we restart the bulk channel. */ *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); /* Wait while the DMA is running (though we don't expect it to be). */ while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd)); /* Advance the DMA to the next EP descriptor. */ ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep); /* ep->next is already a physical address; no need for a virt_to_phys. */ *R_DMA_CH8_SUB0_EP = ep->next; /* Start the DMA bulk channel again. */ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); } if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { dbg_ctrl("dma8_sub1_descr (CTRL) intr."); *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); } if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { dbg_intr("dma8_sub2_descr (INTR) intr."); *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); } if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { dbg_isoc("dma8_sub3_descr (ISOC) intr."); *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); } DBFEXIT;}static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs){ urb_t *urb; etrax_urb_priv_t *urb_priv; int epid = 0; unsigned long flags; /* Isoc diagnostics. */ static int curr_fm = 0; static int prev_fm = 0; DBFENTER; /* Clear this interrupt. */ *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); /* Note that this while loop assumes that all packets span only one rx descriptor. */ /* The reason we cli here is that we call the driver's callback functions. */ save_flags(flags); cli(); while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -