331 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * WUSB Wire Adapter: WLP interface
 | 
						|
 * Driver for the Linux Network stack.
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * FIXME: docs
 | 
						|
 *
 | 
						|
 * Implementation of the netdevice linkage (except tx and rx related stuff).
 | 
						|
 *
 | 
						|
 * ROADMAP:
 | 
						|
 *
 | 
						|
 *   ENTRY POINTS (Net device):
 | 
						|
 *
 | 
						|
 *     i1480u_open(): Called when we ifconfig up the interface;
 | 
						|
 *                    associates to a UWB host controller, reserves
 | 
						|
 *                    bandwidth (MAS), sets up RX USB URB and starts
 | 
						|
 *                    the queue.
 | 
						|
 *
 | 
						|
 *     i1480u_stop(): Called when we ifconfig down a interface;
 | 
						|
 *                    reverses _open().
 | 
						|
 *
 | 
						|
 *     i1480u_set_config():
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/if_arp.h>
 | 
						|
#include <linux/etherdevice.h>
 | 
						|
 | 
						|
#include "i1480u-wlp.h"
 | 
						|
 | 
						|
struct i1480u_cmd_set_ip_mas {
 | 
						|
	struct uwb_rccb     rccb;
 | 
						|
	struct uwb_dev_addr addr;
 | 
						|
	u8                  stream;
 | 
						|
	u8                  owner;
 | 
						|
	u8                  type;	/* enum uwb_drp_type */
 | 
						|
	u8                  baMAS[32];
 | 
						|
} __attribute__((packed));
 | 
						|
 | 
						|
 | 
						|
static
 | 
						|
int i1480u_set_ip_mas(
 | 
						|
	struct uwb_rc *rc,
 | 
						|
	const struct uwb_dev_addr *dstaddr,
 | 
						|
	u8 stream, u8 owner, u8 type, unsigned long *mas)
 | 
						|
{
 | 
						|
 | 
						|
	int result;
 | 
						|
	struct i1480u_cmd_set_ip_mas *cmd;
 | 
						|
	struct uwb_rc_evt_confirm reply;
 | 
						|
 | 
						|
	result = -ENOMEM;
 | 
						|
	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 | 
						|
	if (cmd == NULL)
 | 
						|
		goto error_kzalloc;
 | 
						|
	cmd->rccb.bCommandType = 0xfd;
 | 
						|
	cmd->rccb.wCommand = cpu_to_le16(0x000e);
 | 
						|
	cmd->addr = *dstaddr;
 | 
						|
	cmd->stream = stream;
 | 
						|
	cmd->owner = owner;
 | 
						|
	cmd->type = type;
 | 
						|
	if (mas == NULL)
 | 
						|
		memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS));
 | 
						|
	else
 | 
						|
		memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS));
 | 
						|
	reply.rceb.bEventType = 0xfd;
 | 
						|
	reply.rceb.wEvent = cpu_to_le16(0x000e);
 | 
						|
	result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd),
 | 
						|
			    &reply.rceb, sizeof(reply));
 | 
						|
	if (result < 0)
 | 
						|
		goto error_cmd;
 | 
						|
	if (reply.bResultCode != UWB_RC_RES_FAIL) {
 | 
						|
		dev_err(&rc->uwb_dev.dev,
 | 
						|
			"SET-IP-MAS: command execution failed: %d\n",
 | 
						|
			reply.bResultCode);
 | 
						|
		result = -EIO;
 | 
						|
	}
 | 
						|
error_cmd:
 | 
						|
	kfree(cmd);
 | 
						|
error_kzalloc:
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Inform a WLP interface of a MAS reservation
 | 
						|
 *
 | 
						|
 * @rc is assumed refcnted.
 | 
						|
 */
 | 
						|
/* FIXME: detect if remote device is WLP capable? */
 | 
						|
static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc,
 | 
						|
			      u8 stream, u8 owner, u8 type, unsigned long *mas)
 | 
						|
{
 | 
						|
	int result = 0;
 | 
						|
	struct device *dev = &rc->uwb_dev.dev;
 | 
						|
 | 
						|
	result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner,
 | 
						|
				   type, mas);
 | 
						|
	if (result < 0) {
 | 
						|
		char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE];
 | 
						|
		uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf),
 | 
						|
				   &rc->uwb_dev.dev_addr);
 | 
						|
		uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf),
 | 
						|
				   &uwb_dev->dev_addr);
 | 
						|
		dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n",
 | 
						|
			rcaddrbuf, devaddrbuf, result);
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Called by bandwidth allocator when change occurs in reservation.
 | 
						|
 *
 | 
						|
 * @rsv:     The reservation that is being established, modified, or
 | 
						|
 *           terminated.
 | 
						|
 *
 | 
						|
 * When a reservation is established, modified, or terminated the upper layer
 | 
						|
 * (WLP here) needs set/update the currently available Media Access Slots
 | 
						|
 * that can be use for IP traffic.
 | 
						|
 *
 | 
						|
 * Our action taken during failure depends on how the reservation is being
 | 
						|
 * changed:
 | 
						|
 * - if reservation is being established we do nothing if we cannot set the
 | 
						|
 *   new MAS to be used
 | 
						|
 * - if reservation is being terminated we revert back to PCA whether the
 | 
						|
 *   SET IP MAS command succeeds or not.
 | 
						|
 */
 | 
						|
void i1480u_bw_alloc_cb(struct uwb_rsv *rsv)
 | 
						|
{
 | 
						|
	int result = 0;
 | 
						|
	struct i1480u *i1480u = rsv->pal_priv;
 | 
						|
	struct device *dev = &i1480u->usb_iface->dev;
 | 
						|
	struct uwb_dev *target_dev = rsv->target.dev;
 | 
						|
	struct uwb_rc *rc = i1480u->wlp.rc;
 | 
						|
	u8 stream = rsv->stream;
 | 
						|
	int type = rsv->type;
 | 
						|
	int is_owner = rsv->owner == &rc->uwb_dev;
 | 
						|
	unsigned long *bmp = rsv->mas.bm;
 | 
						|
 | 
						|
	dev_err(dev, "WLP callback called - sending set ip mas\n");
 | 
						|
	/*user cannot change options while setting configuration*/
 | 
						|
	mutex_lock(&i1480u->options.mutex);
 | 
						|
	switch (rsv->state) {
 | 
						|
	case UWB_RSV_STATE_T_ACCEPTED:
 | 
						|
	case UWB_RSV_STATE_O_ESTABLISHED:
 | 
						|
		result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
 | 
						|
					type, bmp);
 | 
						|
		if (result < 0) {
 | 
						|
			dev_err(dev, "MAS reservation failed: %d\n", result);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		if (is_owner) {
 | 
						|
			wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
 | 
						|
							WLP_DRP | stream);
 | 
						|
			wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case UWB_RSV_STATE_NONE:
 | 
						|
		/* revert back to PCA */
 | 
						|
		result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
 | 
						|
					    type, bmp);
 | 
						|
		if (result < 0)
 | 
						|
			dev_err(dev, "MAS reservation failed: %d\n", result);
 | 
						|
		/* Revert to PCA even though SET IP MAS failed. */
 | 
						|
		wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr,
 | 
						|
						i1480u->options.pca_base_priority);
 | 
						|
		wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dev_err(dev, "unexpected WLP reservation state: %s (%d).\n",
 | 
						|
			uwb_rsv_state_str(rsv->state), rsv->state);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
out:
 | 
						|
	mutex_unlock(&i1480u->options.mutex);
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * Called on 'ifconfig up'
 | 
						|
 */
 | 
						|
int i1480u_open(struct net_device *net_dev)
 | 
						|
{
 | 
						|
	int result;
 | 
						|
	struct i1480u *i1480u = netdev_priv(net_dev);
 | 
						|
	struct wlp *wlp = &i1480u->wlp;
 | 
						|
	struct uwb_rc *rc;
 | 
						|
	struct device *dev = &i1480u->usb_iface->dev;
 | 
						|
 | 
						|
	rc = wlp->rc;
 | 
						|
	result = i1480u_rx_setup(i1480u);		/* Alloc RX stuff */
 | 
						|
	if (result < 0)
 | 
						|
		goto error_rx_setup;
 | 
						|
 | 
						|
	result = uwb_radio_start(&wlp->pal);
 | 
						|
	if (result < 0)
 | 
						|
		goto error_radio_start;
 | 
						|
 | 
						|
	netif_wake_queue(net_dev);
 | 
						|
#ifdef i1480u_FLOW_CONTROL
 | 
						|
	result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);
 | 
						|
	if (result < 0) {
 | 
						|
		dev_err(dev, "Can't submit notification URB: %d\n", result);
 | 
						|
		goto error_notif_urb_submit;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	/* Interface is up with an address, now we can create WSS */
 | 
						|
	result = wlp_wss_setup(net_dev, &wlp->wss);
 | 
						|
	if (result < 0) {
 | 
						|
		dev_err(dev, "Can't create WSS: %d. \n", result);
 | 
						|
		goto error_wss_setup;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
error_wss_setup:
 | 
						|
#ifdef i1480u_FLOW_CONTROL
 | 
						|
	usb_kill_urb(i1480u->notif_urb);
 | 
						|
error_notif_urb_submit:
 | 
						|
#endif
 | 
						|
	uwb_radio_stop(&wlp->pal);
 | 
						|
error_radio_start:
 | 
						|
	netif_stop_queue(net_dev);
 | 
						|
	i1480u_rx_release(i1480u);
 | 
						|
error_rx_setup:
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Called on 'ifconfig down'
 | 
						|
 */
 | 
						|
int i1480u_stop(struct net_device *net_dev)
 | 
						|
{
 | 
						|
	struct i1480u *i1480u = netdev_priv(net_dev);
 | 
						|
	struct wlp *wlp = &i1480u->wlp;
 | 
						|
 | 
						|
	BUG_ON(wlp->rc == NULL);
 | 
						|
	wlp_wss_remove(&wlp->wss);
 | 
						|
	netif_carrier_off(net_dev);
 | 
						|
#ifdef i1480u_FLOW_CONTROL
 | 
						|
	usb_kill_urb(i1480u->notif_urb);
 | 
						|
#endif
 | 
						|
	netif_stop_queue(net_dev);
 | 
						|
	uwb_radio_stop(&wlp->pal);
 | 
						|
	i1480u_rx_release(i1480u);
 | 
						|
	i1480u_tx_release(i1480u);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * Change the interface config--we probably don't have to do anything.
 | 
						|
 */
 | 
						|
int i1480u_set_config(struct net_device *net_dev, struct ifmap *map)
 | 
						|
{
 | 
						|
	int result;
 | 
						|
	struct i1480u *i1480u = netdev_priv(net_dev);
 | 
						|
	BUG_ON(i1480u->wlp.rc == NULL);
 | 
						|
	result = 0;
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Change the MTU of the interface
 | 
						|
 */
 | 
						|
int i1480u_change_mtu(struct net_device *net_dev, int mtu)
 | 
						|
{
 | 
						|
	static union {
 | 
						|
		struct wlp_tx_hdr tx;
 | 
						|
		struct wlp_rx_hdr rx;
 | 
						|
	} i1480u_all_hdrs;
 | 
						|
 | 
						|
	if (mtu < ETH_HLEN)	/* We encap eth frames */
 | 
						|
		return -ERANGE;
 | 
						|
	if (mtu > 4000 - sizeof(i1480u_all_hdrs))
 | 
						|
		return -ERANGE;
 | 
						|
	net_dev->mtu = mtu;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Stop the network queue
 | 
						|
 *
 | 
						|
 * Enable WLP substack to stop network queue. We also set the flow control
 | 
						|
 * threshold at this time to prevent the flow control from restarting the
 | 
						|
 * queue.
 | 
						|
 *
 | 
						|
 * we are loosing the current threshold value here ... FIXME?
 | 
						|
 */
 | 
						|
void i1480u_stop_queue(struct wlp *wlp)
 | 
						|
{
 | 
						|
	struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
 | 
						|
	struct net_device *net_dev = i1480u->net_dev;
 | 
						|
	i1480u->tx_inflight.threshold = 0;
 | 
						|
	netif_stop_queue(net_dev);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Start the network queue
 | 
						|
 *
 | 
						|
 * Enable WLP substack to start network queue. Also re-enable the flow
 | 
						|
 * control to manage the queue again.
 | 
						|
 *
 | 
						|
 * We re-enable the flow control by storing the default threshold in the
 | 
						|
 * flow control threshold. This means that if the user modified the
 | 
						|
 * threshold before the queue was stopped and restarted that information
 | 
						|
 * will be lost. FIXME?
 | 
						|
 */
 | 
						|
void i1480u_start_queue(struct wlp *wlp)
 | 
						|
{
 | 
						|
	struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
 | 
						|
	struct net_device *net_dev = i1480u->net_dev;
 | 
						|
	i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
 | 
						|
	netif_start_queue(net_dev);
 | 
						|
}
 |