📄 uhci.c
字号:
list_entry(tmp, struct urb_priv, queue_list);
if (!turbp->queued)
break;
tmp = tmp->next;
}
} else
tmp = &eurbp->queue_list;
furbp = list_entry(tmp, struct urb_priv, queue_list);
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
/* All qh's in the queue need to link to the next queue */
urbp->qh->link = eurbp->qh->link;
mb(); /* Make sure we flush everything */
/* Only support bulk right now, so no depth */
lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH;
list_add_tail(&urbp->queue_list, &furbp->queue_list);
urbp->queued = 1;
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
{
struct urb_priv *urbp, *nurbp;
struct list_head *head, *tmp;
struct urb_priv *purbp;
struct uhci_td *pltd;
unsigned int toggle;
unsigned long flags;
urbp = urb->hcpriv;
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (list_empty(&urbp->queue_list))
goto out;
nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
/* Fix up the toggle for the next URB's */
if (!urbp->queued)
/* We set the toggle when we unlink */
toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
else {
/* If we're in the middle of the queue, grab the toggle */
/* from the TD previous to us */
purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
queue_list);
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
toggle = uhci_toggle(pltd->info) ^ 1;
}
head = &urbp->queue_list;
tmp = head->next;
while (head != tmp) {
struct urb_priv *turbp;
turbp = list_entry(tmp, struct urb_priv, queue_list);
tmp = tmp->next;
if (!turbp->queued)
break;
toggle = uhci_fixup_toggle(turbp->urb, toggle);
}
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), toggle);
if (!urbp->queued) {
nurbp->queued = 0;
_uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb);
} else {
/* We're somewhere in the middle (or end). A bit trickier */
/* than the head scenario */
purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
queue_list);
pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
if (nurbp->queued)
pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH;
else
/* The next URB happens to be the beginning, so */
/* we're the last, end the chain */
pltd->link = UHCI_PTR_TERM;
}
list_del_init(&urbp->queue_list);
out:
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb)
{
struct urb_priv *urbp;
urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
if (!urbp) {
err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
return NULL;
}
memset((void *)urbp, 0, sizeof(*urbp));
urbp->inserttime = jiffies;
urbp->fsbrtime = jiffies;
urbp->urb = urb;
urbp->dev = urb->dev;
INIT_LIST_HEAD(&urbp->td_list);
INIT_LIST_HEAD(&urbp->queue_list);
INIT_LIST_HEAD(&urbp->complete_list);
urb->hcpriv = urbp;
if (urb->dev != uhci->rh.dev) {
if (urb->transfer_buffer_length) {
urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,
urb->transfer_buffer, urb->transfer_buffer_length,
usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :
PCI_DMA_TODEVICE);
if (!urbp->transfer_buffer_dma_handle)
return NULL;
}
if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {
urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,
urb->setup_packet, sizeof(struct usb_ctrlrequest),
PCI_DMA_TODEVICE);
if (!urbp->setup_packet_dma_handle)
return NULL;
}
}
return urbp;
}
/*
* MUST be called with urb->lock acquired
*/
static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
td->urb = urb;
list_add_tail(&td->list, &urbp->td_list);
}
/*
* MUST be called with urb->lock acquired
*/
static void uhci_remove_td_from_urb(struct uhci_td *td)
{
if (list_empty(&td->list))
return;
list_del_init(&td->list);
td->urb = NULL;
}
/*
* MUST be called with urb->lock acquired
*/
static void uhci_destroy_urb_priv(struct urb *urb)
{
struct list_head *head, *tmp;
struct urb_priv *urbp;
struct uhci *uhci;
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
return;
if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb);
return;
}
if (!list_empty(&urb->urb_list))
warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);
if (!list_empty(&urbp->complete_list))
warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
uhci = urbp->dev->bus->hcpriv;
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
tmp = tmp->next;
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
uhci_free_td(uhci, td);
}
if (urbp->setup_packet_dma_handle) {
pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
urbp->setup_packet_dma_handle = 0;
}
if (urbp->transfer_buffer_dma_handle) {
pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
urbp->transfer_buffer_dma_handle = 0;
}
urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
{
unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {
urbp->fsbr = 1;
if (!uhci->fsbr++ && !uhci->fsbrtimeout)
uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
}
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
{
unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {
urbp->fsbr = 0;
if (!--uhci->fsbr)
uhci->fsbrtimeout = jiffies + FSBR_DELAY;
}
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
/*
* Map status to standard result codes
*
* <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
* <dir_out> is True for output TDs and False for input TDs.
*/
static int uhci_map_status(int status, int dir_out)
{
if (!status)
return 0;
if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */
return -EPROTO;
if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */
if (dir_out)
return -ETIMEDOUT;
else
return -EILSEQ;
}
if (status & TD_CTRL_NAK) /* NAK */
return -ETIMEDOUT;
if (status & TD_CTRL_BABBLE) /* Babble */
return -EOVERFLOW;
if (status & TD_CTRL_DBUFERR) /* Buffer error */
return -ENOSR;
if (status & TD_CTRL_STALLED) /* Stalled */
return -EPIPE;
if (status & TD_CTRL_ACTIVE) /* Active */
return 0;
return -EINVAL;
}
/*
* Control transfers
*/
static int uhci_submit_control(struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct uhci_td *td;
struct uhci_qh *qh;
unsigned long destination, status;
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
dma_addr_t data = urbp->transfer_buffer_dma_handle;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
/* 3 errors */
status = TD_CTRL_ACTIVE | (3 << 27);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
/*
* Build the TD for the control request
*/
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | (7 << 21),
urbp->setup_packet_dma_handle);
/*
* If direction is "send", change the frame from SETUP (0x2D)
* to OUT (0xE1). Else change it from SETUP to IN (0x69).
*/
destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
if (!(urb->transfer_flags & USB_DISABLE_SPD))
status |= TD_CTRL_SPD;
/*
* Build the DATA TD's
*/
while (len > 0) {
int pktsze = len;
if (pktsze > maxsze)
pktsze = maxsze;
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
/* Alternate Data0/1 (start with Data1) */
destination ^= TD_TOKEN_TOGGLE;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
data);
data += pktsze;
len -= pktsze;
}
/*
* Build the final TD for control status
*/
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
/*
* It's IN if the pipe is an output pipe or we're not expecting
* data back.
*/
destination &= ~TD_TOKEN_PID_MASK;
if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
status &= ~TD_CTRL_SPD;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status | TD_CTRL_IOC,
destination | (UHCI_NULL_DATA_SIZE << 21), 0);
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
return -ENOMEM;
urbp->qh = qh;
qh->urbp = urbp;
/* Low speed or small transfers gets a different queue and treatment */
if (urb->dev->speed == USB_SPEED_LOW) {
uhci_insert_tds_in_qh(qh, urb, 0);
uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
} else {
uhci_insert_tds_in_qh(qh, urb, 1);
uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
uhci_inc_fsbr(uhci, urb);
}
return -EINPROGRESS;
}
static int usb_control_retrigger_status(struct urb *urb);
static int uhci_result_control(struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status;
int ret = 0;
if (list_empty(&urbp->td_list))
return -EINVAL;
head = &urbp->td_list;
if (urbp->short_control_packet) {
tmp = head->prev;
goto status_phase;
}
tmp = head->next;
td = list_entry(tmp, struct uhci_td, list);
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
if (status)
goto td_error;
urb->actual_length = 0;
/* The rest of the TD's (but the last) are data */
tmp = tmp->next;
while (tmp != head && tmp->next != head) {
td = list_entry(tmp, struct uhci_td, list);
tmp = tmp->next;
status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
urb->actual_length += uhci_actual_length(td->status);
if (status)
goto td_error;
/* Check to see if we received a short packet */
if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
ret = -EREMOTEIO;
goto err;
}
if (uhci_packetid(td->info) == USB_PID_IN)
return usb_control_retrigger_status(urb);
else
return 0;
}
}
status_phase:
td = list_entry(tmp, struct uhci_td, list);
/* Control status phase */
status = uhci_status_bits(td->status);
#ifdef I_HAVE_BUGGY_APC_BACKUPS
/* APC BackUPS Pro kludge */
/* It tries to send all of the descriptor instead of the amount */
/* we requested */
if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
return 0;
#endif
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
if (status)
goto td_error;
return 0;
td_error:
ret = uhci_map_status(status, uhci_packetout(td->info));
if (ret == -EPIPE)
/* endpoint has stalled - mark it halted */
usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
uhci_packetout(td->info));
err:
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dbg("uhci_result_control() failed with status %x", status);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -