📄 uhci.c
字号:
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
lprintk(errbuf);
}
}
return ret;
}
static int usb_control_retrigger_status(struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci *uhci = urb->dev->bus->hcpriv;
urbp->short_control_packet = 1;
/* Create a new QH to avoid pointer overwriting problems */
uhci_remove_qh(uhci, urbp->qh);
/* Delete all of the TD's except for the status TD at the end */
head = &urbp->td_list;
tmp = head->next;
while (tmp != head && tmp->next != 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);
}
urbp->qh = uhci_alloc_qh(uhci, urb->dev);
if (!urbp->qh) {
err("unable to allocate new QH for control retrigger");
return -ENOMEM;
}
urbp->qh->urbp = urbp;
/* One TD, who cares about Breadth first? */
uhci_insert_tds_in_qh(urbp->qh, urb, 0);
/* Low speed or small transfers gets a different queue and treatment */
if (urb->dev->speed == USB_SPEED_LOW)
uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
else
uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
return -EINPROGRESS;
}
/*
* Interrupt transfers
*/
static int uhci_submit_interrupt(struct urb *urb)
{
struct uhci_td *td;
unsigned long destination, status;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
return -EINVAL;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
status = TD_CTRL_ACTIVE | TD_CTRL_IOC;
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
destination |= ((urb->transfer_buffer_length - 1) << 21);
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);
uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);
return -EINPROGRESS;
}
static int uhci_result_interrupt(struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status;
int ret = 0;
urb->actual_length = 0;
head = &urbp->td_list;
tmp = head->next;
while (tmp != 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;
if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
ret = -EREMOTEIO;
goto err;
} else
return 0;
}
}
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_interrupt/bulk() failed with status %x",
status);
if (errbuf) {
/* Print the chain for debugging purposes */
if (urbp->qh)
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
else
uhci_show_td(td, errbuf, ERRBUF_LEN, 0);
lprintk(errbuf);
}
}
return ret;
}
static void uhci_reset_interrupt(struct urb *urb)
{
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
unsigned long flags;
spin_lock_irqsave(&urb->lock, flags);
/* Root hub is special */
if (urb->dev == uhci->rh.dev)
goto out;
td = list_entry(urbp->td_list.next, struct uhci_td, list);
td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
td->info &= ~TD_TOKEN_TOGGLE;
td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
out:
urb->status = -EINPROGRESS;
spin_unlock_irqrestore(&urb->lock, flags);
}
/*
* Bulk transfers
*/
static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
{
struct uhci_td *td;
struct uhci_qh *qh;
unsigned long destination, status;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
dma_addr_t data = urbp->transfer_buffer_dma_handle;
if (len < 0)
return -EINVAL;
/* Can't have low speed bulk transfers */
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
/* 3 errors */
status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
if (!(urb->transfer_flags & USB_DISABLE_SPD))
status |= TD_CTRL_SPD;
/*
* Build the DATA TD's
*/
do { /* Allow zero length packets */
int pktsze = len;
if (pktsze > maxsze)
pktsze = maxsze;
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination |
(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
data += pktsze;
len -= maxsze;
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
} while (len > 0);
/*
* USB_ZERO_PACKET means adding a 0-length packet, if
* direction is OUT and the transfer_length was an
* exact multiple of maxsze, hence
* (len = transfer_length - N * maxsze) == 0
* however, if transfer_length == 0, the zero packet
* was already prepared above.
*/
if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
!len && urb->transfer_buffer_length) {
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination |
(UHCI_NULL_DATA_SIZE << 21) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
}
/* Set the flag on the last packet */
td->status |= TD_CTRL_IOC;
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
return -ENOMEM;
urbp->qh = qh;
qh->urbp = urbp;
/* Always assume breadth first */
uhci_insert_tds_in_qh(qh, urb, 1);
if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
uhci_append_queued_urb(uhci, eurb, urb);
else
uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb);
uhci_inc_fsbr(uhci, urb);
return -EINPROGRESS;
}
/* We can use the result interrupt since they're identical */
#define uhci_result_bulk uhci_result_interrupt
/*
* Isochronous transfers
*/
static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct list_head *tmp, *head;
int ret = 0;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb *u = list_entry(tmp, struct urb, urb_list);
tmp = tmp->next;
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
if (!last_urb)
*start = u->start_frame;
last_urb = u;
}
}
if (last_urb) {
*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
ret = 0;
} else
ret = -1; /* no previous urb found */
return ret;
}
static int isochronous_find_start(struct urb *urb)
{
int limits;
unsigned int start = 0, end = 0;
if (urb->number_of_packets > 900) /* 900? Why? */
return -EFBIG;
limits = isochronous_find_limits(urb, &start, &end);
if (urb->transfer_flags & USB_ISO_ASAP) {
if (limits) {
int curframe;
curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES;
urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
} else
urb->start_frame = end;
} else {
urb->start_frame %= UHCI_NUMFRAMES;
/* FIXME: Sanity check */
}
return 0;
}
/*
* Isochronous transfers
*/
static int uhci_submit_isochronous(struct urb *urb)
{
struct uhci_td *td;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
int i, ret, framenum;
int status, destination;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
ret = isochronous_find_start(urb);
if (ret)
return ret;
framenum = urb->start_frame;
for (i = 0; i < urb->number_of_packets; i++, framenum++) {
if (!urb->iso_frame_desc[i].length)
continue;
td = uhci_alloc_td(uhci, urb->dev);
if (!td)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset);
if (i + 1 >= urb->number_of_packets)
td->status |= TD_CTRL_IOC;
uhci_insert_td_frame_list(uhci, td, framenum);
}
return -EINPROGRESS;
}
static int uhci_result_isochronous(struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
urb->actual_length = 0;
i = 0;
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
int actlength;
tmp = tmp->next;
if (td->status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
actlength = uhci_actual_length(td->status);
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength;
status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe));
urb->iso_frame_desc[i].status = status;
if (status) {
urb->error_count++;
ret = status;
}
i++;
}
return ret;
}
/*
* MUST be called with uhci->urb_list_lock acquired
*/
static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb *u = list_entry(tmp, struct urb, urb_list);
tmp = tmp->next;
if (u->dev == urb->dev && u->pipe == urb->pipe &&
u->status == -EINPROGRESS)
return u;
}
return NULL;
}
static int uhci_submit_urb(struct urb *urb, int mem_flags)
{
int ret = -EINVAL;
struct uhci *uhci;
unsigned long flags;
struct urb *eurb;
int bustime;
if (!urb)
return -EINVAL;
if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
return -ENODEV;
}
/* increment the reference count of the urb, as we now also control it */
urb = usb_get_urb(urb);
uhci = (struct uhci *)urb->dev->bus->hcpriv;
INIT_LIST_HEAD(&urb->urb_list);
usb_get_dev(urb->dev);
spin_lock_irqsave(&uhci->urb_list_lock, flags);
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
urb->status == -ECONNABORTED) {
dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status);
/* Since we can have problems on the out path */
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
usb_put_dev(urb->dev);
usb_put_urb(urb);
return ret;
}
if (!uhci_alloc_urb_priv(uhci, urb)) {
ret = -ENOMEM;
goto out;
}
eurb = uhci_find_urb_ep(uhci, urb);
if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
ret = -ENXIO;
goto out;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -