📄 uhci-hcd.c
字号:
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
urb->actual_length += uhci_actual_length(td_status(td));
if (status)
goto td_error;
/* Check to see if we received a short packet */
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
}
if (uhci_packetid(td_token(td)) == USB_PID_IN)
return usb_control_retrigger_status(uhci, urb);
else
return 0;
}
}
status_phase:
td = list_entry(tmp, struct uhci_td, list);
/* Control status phase */
status = td_status(td);
#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 (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 (uhci_status_bits(status))
goto td_error;
return 0;
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
err:
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dbg("uhci_result_control() failed with status %x", status);
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
lprintk(errbuf);
}
}
return ret;
}
/*
* Common submit for bulk and interrupt
*/
static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
{
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;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
dma_addr_t data = urb->transfer_dma;
if (len < 0)
return -EINVAL;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
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 | uhci_explen(pktsze - 1) |
(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);
/*
* URB_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 & URB_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_explen(UHCI_NULL_DATA_SIZE) |
(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 |= cpu_to_le32(TD_CTRL_IOC);
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
return -ENOMEM;
urbp->qh = qh;
qh->urbp = urbp;
/* Always breadth first */
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
if (eurb)
uhci_append_queued_urb(uhci, eurb, urb);
else
uhci_insert_qh(uhci, skelqh, urb);
return -EINPROGRESS;
}
/*
* Common result for bulk and interrupt
*/
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status = 0;
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(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
urb->actual_length += uhci_actual_length(td_status(td));
if (status)
goto td_error;
if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
} else
return 0;
}
}
return 0;
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
if (ret == -EPIPE)
/* endpoint has stalled - mark it halted */
usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)));
err:
/*
* Enable this chunk of code if you want to see some more debugging.
* But be careful, it has the tendancy to starve out khubd and prevent
* disconnects from happening successfully if you have a slow debug
* log interface (like a serial console.
*/
#if 0
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
/* Some debugging code */
dbg("uhci_result_common() failed with status %x", status);
if (errbuf) {
/* Print the chain for debugging purposes */
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
lprintk(errbuf);
}
}
#endif
return ret;
}
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
int ret;
/* Can't have low speed bulk transfers */
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
if (ret == -EINPROGRESS)
uhci_inc_fsbr(uhci, urb);
return ret;
}
static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{
/* USB 1.1 interrupt transfers only involve one packet per interval;
* that's the uhci_submit_common() "breadth first" policy. Drivers
* can submit urbs of any length, but longer ones might need many
* intervals to complete.
*/
return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
}
/*
* Bulk and interrupt use common result
*/
#define uhci_result_bulk uhci_result_common
#define uhci_result_interrupt uhci_result_common
/*
* Isochronous transfers
*/
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
struct list_head *tmp, *head;
int ret = 0;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
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 *
last_urb->interval) & (UHCI_NUMFRAMES-1);
ret = 0;
} else
ret = -1; /* no previous urb found */
return ret;
}
static int isochronous_find_start(struct uhci_hcd *uhci, 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(uhci, urb, &start, &end);
if (urb->transfer_flags & URB_ISO_ASAP) {
if (limits) {
int curframe;
curframe = uhci_get_current_frame_number(uhci) % 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 uhci_hcd *uhci, struct urb *urb)
{
struct uhci_td *td;
int i, ret, frame;
int status, destination;
status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
ret = isochronous_find_start(uhci, urb);
if (ret)
return ret;
frame = urb->start_frame;
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
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 | uhci_explen(urb->iso_frame_desc[i].length - 1),
urb->transfer_dma + urb->iso_frame_desc[i].offset);
if (i + 1 >= urb->number_of_packets)
td->status |= cpu_to_le32(TD_CTRL_IOC);
uhci_insert_td_frame_list(uhci, td, frame);
}
return -EINPROGRESS;
}
static int uhci_result_isochronous(struct uhci_hcd *uhci, 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) & TD_CTRL_ACTIVE)
return -EINPROGRESS;
actlength = uhci_actual_length(td_status(td));
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength;
status = uhci_map_status(uhci_status_bits(td_status(td)), 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_hcd *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_priv *up = list_entry(tmp, struct urb_priv, urb_list);
struct urb *u = up->urb;
tmp = tmp->next;
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
(u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
return u;
else if (u->pipe == urb->pipe)
return u;
}
}
return NULL;
}
static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags)
{
int ret = -EINVAL;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb *eurb;
int bustime;
spin_lock_irqsave(&uhci->urb_list_lock, flags);
eurb = uhci_find_urb_ep(uhci, urb);
if (!uhci_alloc_urb_priv(uhci, urb)) {
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return -ENOMEM;
}
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
ret = uhci_submit_control(uhci, urb, eurb);
break;
case PIPE_INTERRUPT:
if (!eurb) {
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
ret = bustime;
else {
ret = uhci_submit_interrupt(uhci, urb, eurb);
if (ret == -EINPROGRESS)
usb_claim_bandwidth(urb->dev, urb, bustime, 0);
}
} else { /* inherit from parent */
urb->bandwidth = eurb->bandwidth;
ret = uhci_submit_interrupt(uhci, urb, eurb);
}
break;
case PIPE_BULK:
ret = uhci_submit_bulk(uhci, urb, eurb);
break;
case PIPE_ISOCHRONOUS:
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) {
ret = bustime;
break;
}
ret = uhci_submit_isochronous(uhci, urb);
if (ret == -EINPROGRESS)
usb_claim_bandwidth(urb->dev, urb, bustime, 1);
break;
}
if (ret != -EINPROGRESS) {
/* Submit failed, so delete it from the urb_list */
struct urb_priv *urbp = urb->hcpriv;
list_del_init(&urbp->urb_list);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_destroy_urb_priv (uhci, urb);
return ret;
}
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return 0;
}
/*
* Return the result of a transfer
*
* MUST be called with urb_list_lock acquired
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -