usb/isp1760: Clean up urb enqueueing

This collects urb enqueue code that was spread out all over the place
into a couple of more readable functions.

Signed-off-by: Arvid Brodin <arvid.brodin@enea.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Arvid Brodin 2011-04-26 21:47:37 +02:00 committed by Greg Kroah-Hartman
parent 847ed3e8f1
commit 34537731d7

View File

@ -272,7 +272,7 @@ static void init_memory(struct isp1760_hcd *priv)
payload_addr += priv->memory_pool[curr + i].size; payload_addr += priv->memory_pool[curr + i].size;
} }
BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE);
} }
static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
@ -280,7 +280,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
struct isp1760_hcd *priv = hcd_to_priv(hcd); struct isp1760_hcd *priv = hcd_to_priv(hcd);
int i; int i;
BUG_ON(qtd->payload_addr); WARN_ON(qtd->payload_addr);
if (!qtd->length) if (!qtd->length)
return; return;
@ -318,7 +318,7 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
for (i = 0; i < BLOCKS; i++) { for (i = 0; i < BLOCKS; i++) {
if (priv->memory_pool[i].start == qtd->payload_addr) { if (priv->memory_pool[i].start == qtd->payload_addr) {
BUG_ON(priv->memory_pool[i].free); WARN_ON(priv->memory_pool[i].free);
priv->memory_pool[i].free = 1; priv->memory_pool[i].free = 1;
qtd->payload_addr = 0; qtd->payload_addr = 0;
return; return;
@ -379,7 +379,7 @@ static int ehci_reset(struct usb_hcd *hcd)
static void qh_destroy(struct isp1760_qh *qh) static void qh_destroy(struct isp1760_qh *qh)
{ {
BUG_ON(!list_empty(&qh->qtd_list)); WARN_ON(!list_empty(&qh->qtd_list));
kmem_cache_free(qh_cachep, qh); kmem_cache_free(qh_cachep, qh);
} }
@ -738,23 +738,6 @@ static void transform_into_int(struct isp1760_qh *qh,
transform_add_int(qh, qtd, ptd); transform_add_int(qh, qtd, ptd);
} }
static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len,
u32 token)
{
int count;
qtd->data_buffer = databuffer;
qtd->packet_type = GET_QTD_TOKEN_TYPE(token);
if (len > MAX_PAYLOAD_SIZE)
count = MAX_PAYLOAD_SIZE;
else
count = len;
qtd->length = count;
return count;
}
static int check_error(struct usb_hcd *hcd, struct ptd *ptd) static int check_error(struct usb_hcd *hcd, struct ptd *ptd)
{ {
int error = 0; int error = 0;
@ -948,9 +931,25 @@ __acquires(priv->lock)
spin_lock(&priv->lock); spin_lock(&priv->lock);
} }
static void isp1760_qtd_free(struct isp1760_qtd *qtd) static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb,
u8 packet_type)
{ {
BUG_ON(qtd->payload_addr); struct isp1760_qtd *qtd;
qtd = kmem_cache_zalloc(qtd_cachep, flags);
if (!qtd)
return NULL;
INIT_LIST_HEAD(&qtd->qtd_list);
qtd->urb = urb;
qtd->packet_type = packet_type;
return qtd;
}
static void qtd_free(struct isp1760_qtd *qtd)
{
WARN_ON(qtd->payload_addr);
kmem_cache_free(qtd_cachep, qtd); kmem_cache_free(qtd_cachep, qtd);
} }
@ -965,7 +964,7 @@ static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd,
tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd, tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd,
qtd_list); qtd_list);
list_del(&qtd->qtd_list); list_del(&qtd->qtd_list);
isp1760_qtd_free(qtd); qtd_free(qtd);
return tmp_qtd; return tmp_qtd;
} }
@ -1294,210 +1293,95 @@ static void do_intl_int(struct usb_hcd *hcd)
} }
} }
static struct isp1760_qh *qh_make(struct usb_hcd *hcd, struct urb *urb, static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
gfp_t flags)
{ {
struct isp1760_qh *qh; qtd->data_buffer = databuffer;
int is_input, type;
qh = isp1760_qh_alloc(flags); if (len > MAX_PAYLOAD_SIZE)
if (!qh) len = MAX_PAYLOAD_SIZE;
return qh; qtd->length = len;
/* return qtd->length;
* init endpoint/device data for this QH
*/
is_input = usb_pipein(urb->pipe);
type = usb_pipetype(urb->pipe);
if (!usb_pipecontrol(urb->pipe))
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input,
1);
return qh;
} }
/* static void qtd_list_free(struct list_head *qtd_list)
* For control/bulk/interrupt, return QH with these TDs appended.
* Allocates and initializes the QH if necessary.
* Returns null if it can't allocate a QH it needs to.
* If the QH has TDs (urbs) already, that's great.
*/
static struct isp1760_qh *qh_append_tds(struct usb_hcd *hcd,
struct urb *urb, struct list_head *qtd_list, int epnum,
void **ptr)
{ {
struct isp1760_qh *qh; struct isp1760_qtd *qtd, *qtd_next;
qh = (struct isp1760_qh *)*ptr; list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) {
if (!qh) {
/* can't sleep here, we have priv->lock... */
qh = qh_make(hcd, urb, GFP_ATOMIC);
if (!qh)
return qh;
*ptr = qh;
}
list_splice(qtd_list, qh->qtd_list.prev);
return qh;
}
static void qtd_list_free(struct urb *urb, struct list_head *qtd_list)
{
struct list_head *entry, *temp;
list_for_each_safe(entry, temp, qtd_list) {
struct isp1760_qtd *qtd;
qtd = list_entry(entry, struct isp1760_qtd, qtd_list);
list_del(&qtd->qtd_list); list_del(&qtd->qtd_list);
isp1760_qtd_free(qtd); qtd_free(qtd);
} }
} }
static int isp1760_prepare_enqueue(struct usb_hcd *hcd, struct urb *urb,
struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct isp1760_qtd *qtd;
int epnum;
unsigned long flags;
struct isp1760_qh *qh = NULL;
int rc;
int qh_busy;
qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list);
epnum = urb->ep->desc.bEndpointAddress;
spin_lock_irqsave(&priv->lock, flags);
if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
goto done;
}
rc = usb_hcd_link_urb_to_ep(hcd, urb);
if (rc)
goto done;
qh = urb->ep->hcpriv;
if (qh)
qh_busy = !list_empty(&qh->qtd_list);
else
qh_busy = 0;
qh = qh_append_tds(hcd, urb, qtd_list, epnum, &urb->ep->hcpriv);
if (!qh) {
usb_hcd_unlink_urb_from_ep(hcd, urb);
rc = -ENOMEM;
goto done;
}
if (!qh_busy)
p(hcd, qh, qtd);
done:
spin_unlock_irqrestore(&priv->lock, flags);
if (!qh)
qtd_list_free(urb, qtd_list);
return rc;
}
static struct isp1760_qtd *isp1760_qtd_alloc(gfp_t flags)
{
struct isp1760_qtd *qtd;
qtd = kmem_cache_zalloc(qtd_cachep, flags);
if (qtd)
INIT_LIST_HEAD(&qtd->qtd_list);
return qtd;
}
/* /*
* create a list of filled qtds for this URB; won't link into qh. * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize.
* Also calculate the PID type (SETUP/IN/OUT) for each packet.
*/ */
#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) #define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
static struct list_head *qh_urb_transaction(struct usb_hcd *hcd, static void packetize_urb(struct usb_hcd *hcd,
struct urb *urb, struct list_head *head, gfp_t flags) struct urb *urb, struct list_head *head, gfp_t flags)
{ {
struct isp1760_qtd *qtd; struct isp1760_qtd *qtd;
void *buf; void *buf;
int len, maxpacket; int len, maxpacketsize;
int is_input; u8 packet_type;
u32 token;
/* /*
* URBs map to sequences of QTDs: one logical transaction * URBs map to sequences of QTDs: one logical transaction
*/ */
qtd = isp1760_qtd_alloc(flags);
if (!qtd)
return NULL;
list_add_tail(&qtd->qtd_list, head); if (!urb->transfer_buffer && urb->transfer_buffer_length) {
qtd->urb = urb; /* XXX This looks like usb storage / SCSI bug */
urb->status = -EINPROGRESS; dev_err(hcd->self.controller,
"buf is null, dma is %08lx len is %d\n",
(long unsigned)urb->transfer_dma,
urb->transfer_buffer_length);
WARN_ON(1);
}
token = 0; if (usb_pipein(urb->pipe))
/* for split transactions, SplitXState initialized to zero */ packet_type = IN_PID;
else
packet_type = OUT_PID;
len = urb->transfer_buffer_length;
is_input = usb_pipein(urb->pipe);
if (usb_pipecontrol(urb->pipe)) { if (usb_pipecontrol(urb->pipe)) {
/* SETUP pid */ qtd = qtd_alloc(flags, urb, SETUP_PID);
qtd_fill(qtd, urb->setup_packet,
sizeof(struct usb_ctrlrequest),
token | SETUP_PID);
/* ... and always at least one more pid */
qtd = isp1760_qtd_alloc(flags);
if (!qtd) if (!qtd)
goto cleanup; goto cleanup;
qtd->urb = urb; qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
list_add_tail(&qtd->qtd_list, head); list_add_tail(&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */ /* for zero length DATA stages, STATUS is always IN */
if (len == 0) if (urb->transfer_buffer_length == 0)
token |= IN_PID; packet_type = IN_PID;
} }
/* maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe,
* data transfer stage: buffer setup usb_pipeout(urb->pipe)));
*/
buf = urb->transfer_buffer;
if (is_input)
token |= IN_PID;
else
token |= OUT_PID;
maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
/* /*
* buffer gets wrapped in one or more qtds; * buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len) * last one may be "short" (including zero len)
* and may serve as a control status ack * and may serve as a control status ack
*/ */
buf = urb->transfer_buffer;
len = urb->transfer_buffer_length;
for (;;) { for (;;) {
int this_qtd_len; int this_qtd_len;
if (!buf && len) { qtd = qtd_alloc(flags, urb, packet_type);
/* XXX This looks like usb storage / SCSI bug */ if (!qtd)
dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n", goto cleanup;
(long unsigned)urb->transfer_dma, len); this_qtd_len = qtd_fill(qtd, buf, len);
WARN_ON(1); list_add_tail(&qtd->qtd_list, head);
}
this_qtd_len = qtd_fill(qtd, buf, len, token);
len -= this_qtd_len; len -= this_qtd_len;
buf += this_qtd_len; buf += this_qtd_len;
if (len <= 0) if (len <= 0)
break; break;
qtd = isp1760_qtd_alloc(flags);
if (!qtd)
goto cleanup;
qtd->urb = urb;
list_add_tail(&qtd->qtd_list, head);
} }
/* /*
@ -1509,31 +1393,78 @@ static struct list_head *qh_urb_transaction(struct usb_hcd *hcd,
if (usb_pipecontrol(urb->pipe)) { if (usb_pipecontrol(urb->pipe)) {
one_more = 1; one_more = 1;
/* "in" <--> "out" */ if (packet_type == IN_PID)
token ^= IN_PID; packet_type = OUT_PID;
else
packet_type = IN_PID;
} else if (usb_pipebulk(urb->pipe) } else if (usb_pipebulk(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET) && (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) { && !(urb->transfer_buffer_length %
maxpacketsize)) {
one_more = 1; one_more = 1;
} }
if (one_more) { if (one_more) {
qtd = isp1760_qtd_alloc(flags); qtd = qtd_alloc(flags, urb, packet_type);
if (!qtd) if (!qtd)
goto cleanup; goto cleanup;
qtd->urb = urb;
list_add_tail(&qtd->qtd_list, head);
/* never any data in such packets */ /* never any data in such packets */
qtd_fill(qtd, NULL, 0, token); qtd_fill(qtd, NULL, 0);
list_add_tail(&qtd->qtd_list, head);
} }
} }
qtd->status = 0; return;
return head;
cleanup: cleanup:
qtd_list_free(urb, head); qtd_list_free(head);
return NULL; }
static int enqueue_qtdlist(struct usb_hcd *hcd, struct urb *urb,
struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct isp1760_qtd *qtd;
struct isp1760_qh *qh = NULL;
unsigned long flags;
int qh_empty;
int rc;
spin_lock_irqsave(&priv->lock, flags);
if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
goto done;
}
rc = usb_hcd_link_urb_to_ep(hcd, urb);
if (rc)
goto done;
qh = urb->ep->hcpriv;
if (!qh) {
qh = isp1760_qh_alloc(GFP_ATOMIC);
if (!qh) {
usb_hcd_unlink_urb_from_ep(hcd, urb);
rc = -ENOMEM;
goto done;
}
if (!usb_pipecontrol(urb->pipe))
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
!usb_pipein(urb->pipe), 1);
urb->ep->hcpriv = qh;
}
qh_empty = list_empty(&qh->qtd_list);
list_splice_tail(qtd_list, &qh->qtd_list);
if (qh_empty) {
qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list);
p(hcd, qh, qtd);
}
done:
spin_unlock_irqrestore(&priv->lock, flags);
if (!qh)
qtd_list_free(qtd_list);
return rc;
} }
static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
@ -1547,14 +1478,10 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
switch (usb_pipetype(urb->pipe)) { switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL: case PIPE_CONTROL:
case PIPE_BULK: case PIPE_BULK:
if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
return -ENOMEM;
pe = enqueue_an_ATL_packet; pe = enqueue_an_ATL_packet;
break; break;
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
return -ENOMEM;
pe = enqueue_an_INT_packet; pe = enqueue_an_INT_packet;
break; break;
@ -1564,7 +1491,11 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
return -EPIPE; return -EPIPE;
} }
return isp1760_prepare_enqueue(hcd, urb, &qtd_list, mem_flags, pe); packetize_urb(hcd, urb, &qtd_list, mem_flags);
if (list_empty(&qtd_list))
return -ENOMEM;
return enqueue_qtdlist(hcd, urb, &qtd_list, mem_flags, pe);
} }
static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
@ -1605,7 +1536,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
if (!ints[i].qh) if (!ints[i].qh)
continue; continue;
BUG_ON(!ints[i].qtd); WARN_ON(!ints[i].qtd);
if (ints[i].qtd->urb == urb) { if (ints[i].qtd->urb == urb) {
u32 skip_map; u32 skip_map;