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;
}
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)
@ -280,7 +280,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
struct isp1760_hcd *priv = hcd_to_priv(hcd);
int i;
BUG_ON(qtd->payload_addr);
WARN_ON(qtd->payload_addr);
if (!qtd->length)
return;
@ -318,7 +318,7 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
for (i = 0; i < BLOCKS; i++) {
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;
qtd->payload_addr = 0;
return;
@ -379,7 +379,7 @@ static int ehci_reset(struct usb_hcd *hcd)
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);
}
@ -738,23 +738,6 @@ static void transform_into_int(struct isp1760_qh *qh,
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)
{
int error = 0;
@ -948,9 +931,25 @@ __acquires(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);
}
@ -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,
qtd_list);
list_del(&qtd->qtd_list);
isp1760_qtd_free(qtd);
qtd_free(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,
gfp_t flags)
static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
{
struct isp1760_qh *qh;
int is_input, type;
qtd->data_buffer = databuffer;
qh = isp1760_qh_alloc(flags);
if (!qh)
return qh;
if (len > MAX_PAYLOAD_SIZE)
len = MAX_PAYLOAD_SIZE;
qtd->length = len;
/*
* 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;
return qtd->length;
}
/*
* 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)
static void qtd_list_free(struct list_head *qtd_list)
{
struct isp1760_qh *qh;
struct isp1760_qtd *qtd, *qtd_next;
qh = (struct isp1760_qh *)*ptr;
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_for_each_entry_safe(qtd, qtd_next, qtd_list, 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)
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 isp1760_qtd *qtd;
void *buf;
int len, maxpacket;
int is_input;
u32 token;
int len, maxpacketsize;
u8 packet_type;
/*
* 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);
qtd->urb = urb;
urb->status = -EINPROGRESS;
if (!urb->transfer_buffer && urb->transfer_buffer_length) {
/* XXX This looks like usb storage / SCSI bug */
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;
/* for split transactions, SplitXState initialized to zero */
if (usb_pipein(urb->pipe))
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)) {
/* 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);
qtd = qtd_alloc(flags, urb, SETUP_PID);
if (!qtd)
goto cleanup;
qtd->urb = urb;
qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
list_add_tail(&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */
if (len == 0)
token |= IN_PID;
if (urb->transfer_buffer_length == 0)
packet_type = IN_PID;
}
/*
* data transfer stage: buffer setup
*/
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));
maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe,
usb_pipeout(urb->pipe)));
/*
* buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len)
* and may serve as a control status ack
*/
buf = urb->transfer_buffer;
len = urb->transfer_buffer_length;
for (;;) {
int this_qtd_len;
if (!buf && len) {
/* XXX This looks like usb storage / SCSI bug */
dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n",
(long unsigned)urb->transfer_dma, len);
WARN_ON(1);
}
qtd = qtd_alloc(flags, urb, packet_type);
if (!qtd)
goto cleanup;
this_qtd_len = qtd_fill(qtd, buf, len);
list_add_tail(&qtd->qtd_list, head);
this_qtd_len = qtd_fill(qtd, buf, len, token);
len -= this_qtd_len;
buf += this_qtd_len;
if (len <= 0)
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)) {
one_more = 1;
/* "in" <--> "out" */
token ^= IN_PID;
if (packet_type == IN_PID)
packet_type = OUT_PID;
else
packet_type = IN_PID;
} else if (usb_pipebulk(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
&& !(urb->transfer_buffer_length %
maxpacketsize)) {
one_more = 1;
}
if (one_more) {
qtd = isp1760_qtd_alloc(flags);
qtd = qtd_alloc(flags, urb, packet_type);
if (!qtd)
goto cleanup;
qtd->urb = urb;
list_add_tail(&qtd->qtd_list, head);
/* 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 head;
return;
cleanup:
qtd_list_free(urb, head);
return NULL;
qtd_list_free(head);
}
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,
@ -1547,14 +1478,10 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
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;
case PIPE_INTERRUPT:
if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
return -ENOMEM;
pe = enqueue_an_INT_packet;
break;
@ -1564,7 +1491,11 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
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)
@ -1605,7 +1536,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
for (i = 0; i < 32; i++) {
if (!ints[i].qh)
continue;
BUG_ON(!ints[i].qtd);
WARN_ON(!ints[i].qtd);
if (ints[i].qtd->urb == urb) {
u32 skip_map;