📄 hc_crisv10.c
字号:
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; if (ret != 0) printk("Submit URB error %d\n", ret); return ret;}static int etrax_usb_unlink_urb(struct urb *urb, int status){ etrax_hc_t *hc; etrax_urb_priv_t *urb_priv; int epid; unsigned int flags; DBFENTER; if (!urb) { return -EINVAL; } /* Disable interrupts here since a descriptor interrupt for the isoc epid will modify the sb list. This could possibly be done more granular, but unlink_urb should not be used frequently anyway. */ save_flags(flags); cli(); if (!urb->dev || !urb->dev->bus) { restore_flags(flags); return -ENODEV; } if (!urb->hcpriv) { /* This happens if a device driver calls unlink on an urb that was never submitted (lazy driver) or if the urb was completed while unlink was being called. */ restore_flags(flags); return 0; } if (urb->transfer_flags & URB_ASYNC_UNLINK) { /* FIXME. */ /* If URB_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("URB_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; restore_flags(flags); 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.) */ if (usb_pipetype(urb->pipe) == PIPE_BULK) { urb_list_del(urb, epid); TxBulkEPList[epid].sub = 0; etrax_remove_from_sb_list(urb); } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { urb_list_del(urb, epid); TxCtrlEPList[epid].sub = 0; etrax_remove_from_sb_list(urb); } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { urb_list_del(urb, epid); /* 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)) { USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb; if (__urb_list_entry(urb, epid)) { urb_list_del(urb, epid); iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; prev_sb = 0; while (iter_sb && (iter_sb != urb_priv->first_sb)) { prev_sb = iter_sb; iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; } if (iter_sb == 0) { /* Unlink of the URB currently being transmitted. */ prev_sb = 0; iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; } while (iter_sb && (iter_sb != urb_priv->last_sb)) { iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; } if (iter_sb) { next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; } else { /* This should only happen if the DMA has completed processing the SB list for this EP while interrupts are disabled. */ dbg_isoc("Isoc urb not found, already sent?"); next_sb = 0; } if (prev_sb) { prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0; } else { TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0; } etrax_remove_from_sb_list(urb); if (urb_list_empty(epid)) { TxIsocEPList[epid].sub = 0; dbg_isoc("Last isoc out urb epid %d", epid); } else if (next_sb || prev_sb) { dbg_isoc("Re-enable isoc out epid %d", epid); TxIsocEPList[epid].hw_len = 0; TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); } else { TxIsocEPList[epid].sub = 0; dbg_isoc("URB list non-empty and no SB list, EP disabled"); } } else { dbg_isoc("Urb 0x%p not found, completed already?", urb); } } else { urb_list_del(urb, epid); /* 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); } else { TxIsocEPList[epid].hw_len = 0; TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); } } /* Release allocated bandwidth. */ usb_release_bandwidth(urb->dev, urb, 1); } /* Free the epid if urb list is empty. */ if (urb_list_empty(epid)) { etrax_usb_free_epid(epid); } restore_flags(flags); /* Must be done before calling completion handler. */ kfree(urb_priv); urb->hcpriv = 0; if (urb->complete) { urb->complete(urb, NULL); } DBFEXIT; return 0;}static int etrax_usb_get_frame_number(struct usb_device *usb_dev){ DBFENTER; DBFEXIT; return (*R_USB_FM_NUMBER & 0x7ff);}static irqreturn_t 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)) { struct urb *urb; int epid; etrax_urb_priv_t *urb_priv; unsigned long int flags; dbg_ctrl("dma8_sub1_descr (CTRL) intr."); *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); /* The complete callback gets called so we cli. */ save_flags(flags); cli(); for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { if ((TxCtrlEPList[epid].sub == 0) || (epid == DUMMY_EPID) || (epid == INVALID_EPID)) { /* Nothing here to see. */ continue; } /* Get the first urb (if any). */ urb = urb_list_first(epid); if (urb) { /* Sanity check. */ assert(usb_pipetype(urb->pipe) == PIPE_CONTROL); urb_priv = (etrax_urb_priv_t *)urb->hcpriv; assert(urb_priv); if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) { assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); etrax_usb_complete_urb(urb, 0); } } } restore_flags(flags); } 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)) { struct urb *urb; int epid; int epid_done; etrax_urb_priv_t *urb_priv; USB_SB_Desc_t *sb_desc; usb_isoc_complete_data_t *comp_data = NULL; /* One or more isoc out transfers are done. */ dbg_isoc("dma8_sub3_descr (ISOC) intr."); /* For each isoc out EP search for the first sb_desc with the intr flag set. This descriptor must be the last packet from an URB. Then traverse the URB list for the EP until the URB with urb_priv->last_sb matching the intr-marked sb_desc is found. All URBs before this have been sent. */ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { /* Skip past epids with no SB lists, epids used for in traffic, and special (dummy, invalid) epids. */ if ((TxIsocEPList[epid].sub == 0) || (test_bit(epid, (void *)&epid_out_traffic) == 0) || (epid == DUMMY_EPID) || (epid == INVALID_EPID)) { /* Nothing here to see. */ continue; } sb_desc = phys_to_virt(TxIsocEPList[epid].sub); /* Find the last descriptor of the currently active URB for this ep. This is the first descriptor in the sub list marked for a descriptor interrupt. */ while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) { sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0; } assert(sb_desc); dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p", epid, phys_to_virt(TxIsocEPList[epid].sub), sb_desc); epid_done = 0; /* Get the first urb (if any). */ urb = urb_list_first(epid); assert(urb); while (urb && !epid_done) { /* Sanity check. */ assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); if (!usb_pipeout(urb->pipe)) { /* descr interrupts are generated only for out pipes. */ epid_done = 1; continue; } urb_priv = (etrax_urb_priv_t *)urb->hcpriv; assert(urb_priv); if (sb_desc != urb_priv->last_sb) { /* This urb has been sent. */ dbg_isoc("out URB 0x%p sent", urb); urb_priv->urb_state = TRANSFER_DONE; } else if ((sb_desc == urb_priv->last_sb) && !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes)); assert(sb_desc->next == 0); dbg_isoc("out URB 0x%p last in list, epid disabled", urb); TxIsocEPList[epid].sub = 0; TxIsocEPList[epid].hw_len = 0; urb_priv->urb_state = TRANSFER_DONE; epid_done = 1; } else { epid_done = 1; } if (!epid_done) { urb = urb_list_next(urb, epid); } } } *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, SLAB_ATOMIC); assert(comp_data != NULL); INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data); schedule_work(&comp_data->usb_bh); } DBFEXIT; return IRQ_HANDLED;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -