aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/uwb/i1480/i1480u-wlp/tx.c
blob: 39032cc3503e8aee7e183862c595ce6c3f5c1801 (plain) (tree)
























































                                                                          






                                                       
                                                      

































                                                                                         
  
















































                                                                               






                                                               
  





























































































                                                                                




                                                                      








                                                                                   











                                                                       


















                                                                  
  




































                                                                              
  













































































                                                                              
  




















                                                                        
 




                                                                                 



























                                                                                               








                                                                             






                                       



                      
  





















                                                                            







                                                                         



                                                                           




                                                                




                                   



                            
  










































                                                                                  
/*
 * WUSB Wire Adapter: WLP interface
 * Deal with TX (massaging data to transmit, handling it)
 *
 * Copyright (C) 2005-2006 Intel Corporation
 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 *
 * Transmission engine. Get an skb, create from that a WLP transmit
 * context, add a WLP TX header (which we keep prefilled in the
 * device's instance), fill out the target-specific fields and
 * fire it.
 *
 * ROADMAP:
 *
 *   Entry points:
 *
 *     i1480u_tx_release(): called by i1480u_disconnect() to release
 *                          pending tx contexts.
 *
 *     i1480u_tx_cb(): callback for TX contexts (USB URBs)
 *       i1480u_tx_destroy():
 *
 *     i1480u_tx_timeout(): called for timeout handling from the
 *                          network stack.
 *
 *     i1480u_hard_start_xmit(): called for transmitting an skb from
 *                               the network stack. Will interact with WLP
 *                               substack to verify and prepare frame.
 *       i1480u_xmit_frame(): actual transmission on hardware
 *
 *         i1480u_tx_create()	    Creates TX context
 *            i1480u_tx_create_1()    For packets in 1 fragment
 *            i1480u_tx_create_n()    For packets in >1 fragments
 *
 * TODO:
 *
 * - FIXME: rewrite using usb_sg_*(), add asynch support to
 *          usb_sg_*(). It might not make too much sense as most of
 *          the times the MTU will be smaller than one page...
 */

#include "i1480u-wlp.h"

enum {
	/* This is only for Next and Last TX packets */
	i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE
		- sizeof(struct untd_hdr_rst),
};

/* Free resources allocated to a i1480u tx context. */
static
void i1480u_tx_free(struct i1480u_tx *wtx)
{
	kfree(wtx->buf);
	if (wtx->skb)
		dev_kfree_skb_irq(wtx->skb);
	usb_free_urb(wtx->urb);
	kfree(wtx);
}

static
void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx)
{
	unsigned long flags;
	spin_lock_irqsave(&i1480u->tx_list_lock, flags);	/* not active any more */
	list_del(&wtx->list_node);
	i1480u_tx_free(wtx);
	spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
}

static
void i1480u_tx_unlink_urbs(struct i1480u *i1480u)
{
	unsigned long flags;
	struct i1480u_tx *wtx, *next;

	spin_lock_irqsave(&i1480u->tx_list_lock, flags);
	list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
		usb_unlink_urb(wtx->urb);
	}
	spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
}


/*
 * Callback for a completed tx USB URB.
 *
 * TODO:
 *
 * - FIXME: recover errors more gracefully
 * - FIXME: handle NAKs (I dont think they come here) for flow ctl
 */
static
void i1480u_tx_cb(struct urb *urb)
{
	struct i1480u_tx *wtx = urb->context;
	struct i1480u *i1480u = wtx->i1480u;
	struct net_device *net_dev = i1480u->net_dev;
	struct device *dev = &i1480u->usb_iface->dev;
	unsigned long flags;

	switch (urb->status) {
	case 0:
		spin_lock_irqsave(&i1480u->lock, flags);
		i1480u->stats.tx_packets++;
		i1480u->stats.tx_bytes += urb->actual_length;
		spin_unlock_irqrestore(&i1480u->lock, flags);
		break;
	case -ECONNRESET:	/* Not an error, but a controlled situation; */
	case -ENOENT:		/* (we killed the URB)...so, no broadcast */
		dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status);
		netif_stop_queue(net_dev);
		break;
	case -ESHUTDOWN:	/* going away! */
		dev_dbg(dev, "notif endp: down %d\n", urb->status);
		netif_stop_queue(net_dev);
		break;
	default:
		dev_err(dev, "TX: unknown URB status %d\n", urb->status);
		if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS,
					EDC_ERROR_TIMEFRAME)) {
			dev_err(dev, "TX: max acceptable errors exceeded."
					"Reset device.\n");
			netif_stop_queue(net_dev);
			i1480u_tx_unlink_urbs(i1480u);
			wlp_reset_all(&i1480u->wlp);
		}
		break;
	}
	i1480u_tx_destroy(i1480u, wtx);
	if (atomic_dec_return(&i1480u->tx_inflight.count)
	    <= i1480u->tx_inflight.threshold
	    && netif_queue_stopped(net_dev)
	    && i1480u->tx_inflight.threshold != 0) {
		netif_start_queue(net_dev);
		atomic_inc(&i1480u->tx_inflight.restart_count);
	}
	return;
}


/*
 * Given a buffer that doesn't fit in a single fragment, create an
 * scatter/gather structure for delivery to the USB pipe.
 *
 * Implements functionality of i1480u_tx_create().
 *
 * @wtx:	tx descriptor
 * @skb:	skb to send
 * @gfp_mask:	gfp allocation mask
 * @returns:    Pointer to @wtx if ok, NULL on error.
 *
 * Sorry, TOO LONG a function, but breaking it up is kind of hard
 *
 * This will break the buffer in chunks smaller than
 * i1480u_MAX_FRG_SIZE (including the header) and add proper headers
 * to each:
 *
 *   1st header           \
 *   i1480 tx header      |  fragment 1
 *   fragment data        /
 *   nxt header           \  fragment 2
 *   fragment data        /
 *   ..
 *   ..
 *   last header          \  fragment 3
 *   last fragment data   /
 *
 * This does not fill the i1480 TX header, it is left up to the
 * caller to do that; you can get it from @wtx->wlp_tx_hdr.
 *
 * This function consumes the skb unless there is an error.
 */
static
int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb,
		       gfp_t gfp_mask)
{
	int result;
	void *pl;
	size_t pl_size;

	void *pl_itr, *buf_itr;
	size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0;
	struct untd_hdr_1st *untd_hdr_1st;
	struct wlp_tx_hdr *wlp_tx_hdr;
	struct untd_hdr_rst *untd_hdr_rst;

	wtx->skb = NULL;
	pl = skb->data;
	pl_itr = pl;
	pl_size = skb->len;
	pl_size_left = pl_size;	/* payload size */
	/* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus
	 * the headers */
	pl_size_1st = i1480u_MAX_FRG_SIZE
		- sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr);
	BUG_ON(pl_size_1st > pl_size);
	pl_size_left -= pl_size_1st;
	/* The rest have an smaller header (no i1480 TX header). We
	 * need to break up the payload in blocks smaller than
	 * i1480u_MAX_PL_SIZE (payload excluding header). */
	frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE;
	/* Allocate space for the new buffer. In this new buffer we'll
	 * place the headers followed by the data fragment, headers,
	 * data fragments, etc..
	 */
	result = -ENOMEM;
	wtx->buf_size = sizeof(*untd_hdr_1st)
		+ sizeof(*wlp_tx_hdr)
		+ frgs * sizeof(*untd_hdr_rst)
		+ pl_size;
	wtx->buf = kmalloc(wtx->buf_size, gfp_mask);
	if (wtx->buf == NULL)
		goto error_buf_alloc;

	buf_itr = wtx->buf;		/* We got the space, let's fill it up */
	/* Fill 1st fragment */
	untd_hdr_1st = buf_itr;
	buf_itr += sizeof(*untd_hdr_1st);
	untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST);
	untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0);
	untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr));
	untd_hdr_1st->fragment_len =
		cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr));
	memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding));
	/* Set up i1480 header info */
	wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr;
	buf_itr += sizeof(*wlp_tx_hdr);
	/* Copy the first fragment */
	memcpy(buf_itr, pl_itr, pl_size_1st);
	pl_itr += pl_size_1st;
	buf_itr += pl_size_1st;

	/* Now do each remaining fragment */
	result = -EINVAL;
	while (pl_size_left > 0) {
		if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf
		    > wtx->buf_size) {
			printk(KERN_ERR "BUG: no space for header\n");
			goto error_bug;
		}
		untd_hdr_rst = buf_itr;
		buf_itr += sizeof(*untd_hdr_rst);
		if (pl_size_left > i1480u_MAX_PL_SIZE) {
			frg_pl_size = i1480u_MAX_PL_SIZE;
			untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT);
		} else {
			frg_pl_size = pl_size_left;
			untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST);
		}
		untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0);
		untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size);
		untd_hdr_rst->padding = 0;
		if (buf_itr + frg_pl_size - wtx->buf
		    > wtx->buf_size) {
			printk(KERN_ERR "BUG: no space for payload\n");
			goto error_bug;
		}
		memcpy(buf_itr, pl_itr, frg_pl_size);
		buf_itr += frg_pl_size;
		pl_itr += frg_pl_size;
		pl_size_left -= frg_pl_size;
	}
	dev_kfree_skb_irq(skb);
	return 0;

error_bug:
	printk(KERN_ERR
	       "BUG: skb %u bytes\n"
	       "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n"
	       "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n",
	       skb->len,
	       frg_pl_size, i1480u_MAX_FRG_SIZE,
	       buf_itr - wtx->buf, wtx->buf_size, pl_size_left);

	kfree(wtx->buf);
error_buf_alloc:
	return result;
}


/*
 * Given a buffer that fits in a single fragment, fill out a @wtx
 * struct for transmitting it down the USB pipe.
 *
 * Uses the fact that we have space reserved in front of the skbuff
 * for hardware headers :]
 *
 * This does not fill the i1480 TX header, it is left up to the
 * caller to do that; you can get it from @wtx->wlp_tx_hdr.
 *
 * @pl:		pointer to payload data
 * @pl_size:    size of the payuload
 *
 * This function does not consume the @skb.
 */
static
int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb,
		       gfp_t gfp_mask)
{
	struct untd_hdr_cmp *untd_hdr_cmp;
	struct wlp_tx_hdr *wlp_tx_hdr;

	wtx->buf = NULL;
	wtx->skb = skb;
	BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr));
	wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr));
	wtx->wlp_tx_hdr = wlp_tx_hdr;
	BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp));
	untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp));

	untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP);
	untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0);
	untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp));
	untd_hdr_cmp->padding = 0;
	return 0;
}


/*
 * Given a skb to transmit, massage it to become palatable for the TX pipe
 *
 * This will break the buffer in chunks smaller than
 * i1480u_MAX_FRG_SIZE and add proper headers to each.
 *
 *   1st header           \
 *   i1480 tx header      |  fragment 1
 *   fragment data        /
 *   nxt header           \  fragment 2
 *   fragment data        /
 *   ..
 *   ..
 *   last header          \  fragment 3
 *   last fragment data   /
 *
 * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE.
 *
 * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the
 * following is composed:
 *
 *   complete header      \
 *   i1480 tx header      | single fragment
 *   packet data          /
 *
 * We were going to use s/g support, but because the interface is
 * synch and at the end there is plenty of overhead to do it, it
 * didn't seem that worth for data that is going to be smaller than
 * one page.
 */
static
struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u,
				   struct sk_buff *skb, gfp_t gfp_mask)
{
	int result;
	struct usb_endpoint_descriptor *epd;
	int usb_pipe;
	unsigned long flags;

	struct i1480u_tx *wtx;
	const size_t pl_max_size =
		i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp)
		- sizeof(struct wlp_tx_hdr);

	wtx = kmalloc(sizeof(*wtx), gfp_mask);
	if (wtx == NULL)
		goto error_wtx_alloc;
	wtx->urb = usb_alloc_urb(0, gfp_mask);
	if (wtx->urb == NULL)
		goto error_urb_alloc;
	epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc;
	usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress);
	/* Fits in a single complete packet or need to split? */
	if (skb->len > pl_max_size) {
		result = i1480u_tx_create_n(wtx, skb, gfp_mask);
		if (result < 0)
			goto error_create;
		usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
				  wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx);
	} else {
		result = i1480u_tx_create_1(wtx, skb, gfp_mask);
		if (result < 0)
			goto error_create;
		usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
				  skb->data, skb->len, i1480u_tx_cb, wtx);
	}
	spin_lock_irqsave(&i1480u->tx_list_lock, flags);
	list_add(&wtx->list_node, &i1480u->tx_list);
	spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
	return wtx;

error_create:
	kfree(wtx->urb);
error_urb_alloc:
	kfree(wtx);
error_wtx_alloc:
	return NULL;
}

/*
 * Actual fragmentation and transmission of frame
 *
 * @wlp:  WLP substack data structure
 * @skb:  To be transmitted
 * @dst:  Device address of destination
 * @returns: 0 on success, <0 on failure
 *
 * This function can also be called directly (not just from
 * hard_start_xmit), so we also check here if the interface is up before
 * taking sending anything.
 */
int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb,
		      struct uwb_dev_addr *dst)
{
	int result = -ENXIO;
	struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
	struct device *dev = &i1480u->usb_iface->dev;
	struct net_device *net_dev = i1480u->net_dev;
	struct i1480u_tx *wtx;
	struct wlp_tx_hdr *wlp_tx_hdr;
	static unsigned char dev_bcast[2] = { 0xff, 0xff };

	BUG_ON(i1480u->wlp.rc == NULL);
	if ((net_dev->flags & IFF_UP) == 0)
		goto out;
	result = -EBUSY;
	if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) {
		netif_stop_queue(net_dev);
		goto error_max_inflight;
	}
	result = -ENOMEM;
	wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC);
	if (unlikely(wtx == NULL)) {
		if (printk_ratelimit())
			dev_err(dev, "TX: no memory for WLP TX URB,"
				"dropping packet (in flight %d)\n",
				atomic_read(&i1480u->tx_inflight.count));
		netif_stop_queue(net_dev);
		goto error_wtx_alloc;
	}
	wtx->i1480u = i1480u;
	/* Fill out the i1480 header; @i1480u->def_tx_hdr read without
	 * locking. We do so because they are kind of orthogonal to
	 * each other (and thus not changed in an atomic batch).
	 * The ETH header is right after the WLP TX header. */
	wlp_tx_hdr = wtx->wlp_tx_hdr;
	*wlp_tx_hdr = i1480u->options.def_tx_hdr;
	wlp_tx_hdr->dstaddr = *dst;
	if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast))
	    && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) {
		/*Broadcast message directed to DRP host. Send as best effort
		 * on PCA. */
		wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority);
	}

	result = usb_submit_urb(wtx->urb, GFP_ATOMIC);		/* Go baby */
	if (result < 0) {
		dev_err(dev, "TX: cannot submit URB: %d\n", result);
		/* We leave the freeing of skb to calling function */
		wtx->skb = NULL;
		goto error_tx_urb_submit;
	}
	atomic_inc(&i1480u->tx_inflight.count);
	net_dev->trans_start = jiffies;
	return result;

error_tx_urb_submit:
	i1480u_tx_destroy(i1480u, wtx);
error_wtx_alloc:
error_max_inflight:
out:
	return result;
}


/*
 * Transmit an skb  Called when an skbuf has to be transmitted
 *
 * The skb is first passed to WLP substack to ensure this is a valid
 * frame. If valid the device address of destination will be filled and
 * the WLP header prepended to the skb. If this step fails we fake sending
 * the frame, if we return an error the network stack will just keep trying.
 *
 * Broadcast frames inside a WSS needs to be treated special as multicast is
 * not supported. A broadcast frame is sent as unicast to each member of the
 * WSS - this is done by the WLP substack when it finds a broadcast frame.
 * So, we test if the WLP substack took over the skb and only transmit it
 * if it has not (been taken over).
 *
 * @net_dev->xmit_lock is held
 */
int i1480u_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
{
	int result;
	struct i1480u *i1480u = netdev_priv(net_dev);
	struct device *dev = &i1480u->usb_iface->dev;
	struct uwb_dev_addr dst;

	if ((net_dev->flags & IFF_UP) == 0)
		goto error;
	result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst);
	if (result < 0) {
		dev_err(dev, "WLP verification of TX frame failed (%d). "
			"Dropping packet.\n", result);
		goto error;
	} else if (result == 1) {
		/* trans_start time will be set when WLP actually transmits
		 * the frame */
		goto out;
	}
	result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst);
	if (result < 0) {
		dev_err(dev, "Frame TX failed (%d).\n", result);
		goto error;
	}
	return NETDEV_TX_OK;
error:
	dev_kfree_skb_any(skb);
	i1480u->stats.tx_dropped++;
out:
	return NETDEV_TX_OK;
}


/*
 * Called when a pkt transmission doesn't complete in a reasonable period
 * Device reset may sleep - do it outside of interrupt context (delayed)
 */
void i1480u_tx_timeout(struct net_device *net_dev)
{
	struct i1480u *i1480u = netdev_priv(net_dev);

	wlp_reset_all(&i1480u->wlp);
}


void i1480u_tx_release(struct i1480u *i1480u)
{
	unsigned long flags;
	struct i1480u_tx *wtx, *next;
	int count = 0, empty;

	spin_lock_irqsave(&i1480u->tx_list_lock, flags);
	list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
		count++;
		usb_unlink_urb(wtx->urb);
	}
	spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
	count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */
	/*
	 * We don't like this sollution too much (dirty as it is), but
	 * it is cheaper than putting a refcount on each i1480u_tx and
	 * i1480uting for all of them to go away...
	 *
	 * Called when no more packets can be added to tx_list
	 * so can i1480ut for it to be empty.
	 */
	while (1) {
		spin_lock_irqsave(&i1480u->tx_list_lock, flags);
		empty = list_empty(&i1480u->tx_list);
		spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
		if (empty)
			break;
		count--;
		BUG_ON(count == 0);
		msleep(20);
	}
}