Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a21b963a authored by Inaky Perez-Gonzalez's avatar Inaky Perez-Gonzalez Committed by David Vrabel
Browse files

uwb: add the i1480 WLP driver



Add the driver for the WLP capability of the Intel i1480 device.

Signed-off-by: default avatarDavid Vrabel <david.vrabel@csr.com>
parent 1ba47da5
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -76,4 +76,14 @@ config UWB_I1480U
         To compile this driver select Y (built in) or M (module). It
         To compile this driver select Y (built in) or M (module). It
         is safe to select any even if you do not have the hardware.
         is safe to select any even if you do not have the hardware.


config UWB_I1480U_WLP
        tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface"
        depends on UWB_I1480U &&  UWB_WLP && NET
        help
         This driver enables WLP support for the i1480 when connected via
         USB. WLP is the WiMedia Link Protocol, or IP over UWB.

         To compile this driver select Y (built in) or M (module). It
         is safe to select any even if you don't have the hardware.

endif # UWB
endif # UWB
+1 −0
Original line number Original line Diff line number Diff line
obj-$(CONFIG_UWB_I1480U)	+= dfu/ i1480-est.o
obj-$(CONFIG_UWB_I1480U)	+= dfu/ i1480-est.o
obj-$(CONFIG_UWB_I1480U_WLP)	+= i1480u-wlp/
+200 −0
Original line number Original line Diff line number Diff line
/*
 * Intel 1480 Wireless UWB Link
 * WLP specific definitions
 *
 *
 * 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
 */

#ifndef __i1480_wlp_h__
#define __i1480_wlp_h__

#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/uwb.h>
#include <linux/if_ether.h>
#include <asm/byteorder.h>

/* New simplified header format? */
#undef WLP_HDR_FMT_2 		/* FIXME: rename */

/**
 * Values of the Delivery ID & Type field when PCA or DRP
 *
 * The Delivery ID & Type field in the WLP TX header indicates whether
 * the frame is PCA or DRP. This is done based on the high level bit of
 * this field.
 * We use this constant to test if the traffic is PCA or DRP as follows:
 * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)
 * 	this is DRP traffic
 * else
 * 	this is PCA traffic
 */
enum deliver_id_type_bit {
	WLP_DRP = 8,
};

/**
 * WLP TX header
 *
 * Indicates UWB/WLP-specific transmission parameters for a network
 * packet.
 */
struct wlp_tx_hdr {
	/* dword 0 */
	struct uwb_dev_addr dstaddr;
	u8                  key_index;
	u8                  mac_params;
	/* dword 1 */
	u8                  phy_params;
#ifndef WLP_HDR_FMT_2
	u8                  reserved;
	__le16              oui01;              /* FIXME: not so sure if __le16 or u8[2] */
	/* dword 2 */
	u8                  oui2;               /*        if all LE, it could be merged */
	__le16              prid;
#endif
} __attribute__((packed));

static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr)
{
	return hdr->mac_params & 0x0f;
}

static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr)
{
	return (hdr->mac_params >> 4) & 0x07;
}

static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr)
{
	return (hdr->mac_params >> 7) & 0x01;
}

static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id)
{
	hdr->mac_params = (hdr->mac_params & ~0x0f) | id;
}

static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr,
					     enum uwb_ack_pol policy)
{
	hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4);
}

static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts)
{
	hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7);
}

static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr)
{
	return hdr->phy_params & 0x0f;
}

static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr)
{
	return (hdr->phy_params >> 4) & 0x0f;
}

static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate)
{
	hdr->phy_params = (hdr->phy_params & ~0x0f) | rate;
}

static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr)
{
	hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4);
}


/**
 * WLP RX header
 *
 * Provides UWB/WLP-specific transmission data for a received
 * network packet.
 */
struct wlp_rx_hdr {
	/* dword 0 */
	struct uwb_dev_addr dstaddr;
	struct uwb_dev_addr srcaddr;
	/* dword 1 */
	u8 		    LQI;
	s8		    RSSI;
	u8		    reserved3;
#ifndef WLP_HDR_FMT_2
	u8 		    oui0;
	/* dword 2 */
	__le16		    oui12;
	__le16		    prid;
#endif
} __attribute__((packed));


/** User configurable options for WLP */
struct wlp_options {
	struct mutex mutex; /* access to user configurable options*/
	struct wlp_tx_hdr def_tx_hdr;	/* default tx hdr */
	u8 pca_base_priority;
	u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/
};


static inline
void wlp_options_init(struct wlp_options *options)
{
	mutex_init(&options->mutex);
	wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM);
	wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1);
	/* FIXME: default to phy caps */
	wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480);
#ifndef WLP_HDR_FMT_2
	options->def_tx_hdr.prid = cpu_to_le16(0x0000);
#endif
}


/* sysfs helpers */

extern ssize_t uwb_pca_base_priority_store(struct wlp_options *,
					   const char *, size_t);
extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *);
extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t);
extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *);
extern ssize_t uwb_ack_policy_store(struct wlp_options *,
				    const char *, size_t);
extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *);
extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t);
extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *);
extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t);
extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *);


/** Simple bandwidth allocation (temporary and too simple) */
struct wlp_bw_allocs {
	const char *name;
	struct {
		u8 mask, stream;
	} tx, rx;
};


#endif /* #ifndef __i1480_wlp_h__ */
+8 −0
Original line number Original line Diff line number Diff line
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o

i1480u-wlp-objs :=	\
	lc.o		\
	netdev.o	\
	rx.o		\
	sysfs.o		\
	tx.o
+284 −0
Original line number Original line Diff line number Diff line
/*
 * Intel 1480 Wireless UWB Link USB
 * Header formats, constants, general internal interfaces
 *
 *
 * 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.
 *
 *
 * This is not an standard interface.
 *
 * FIXME: docs
 *
 * i1480u-wlp is pretty simple: two endpoints, one for tx, one for
 * rx. rx is polled. Network packets (ethernet, whatever) are wrapped
 * in i1480 TX or RX headers (for sending over the air), and these
 * packets are wrapped in UNTD headers (for sending to the WLP UWB
 * controller).
 *
 * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets
 * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the
 * i1480 packet is broken in chunks/packets:
 *
 * UNTD-1st.hdr + i1480.hdr + payload
 * UNTD-next.hdr + payload
 * ...
 * UNTD-last.hdr + payload
 *
 * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE.
 *
 * All HW structures and bitmaps are little endian, so we need to play
 * ugly tricks when defining bitfields. Hoping for the day GCC
 * implements __attribute__((endian(1234))).
 *
 * FIXME: ROADMAP to the whole implementation
 */

#ifndef __i1480u_wlp_h__
#define __i1480u_wlp_h__

#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/uwb.h>		/* struct uwb_rc, struct uwb_notifs_handler */
#include <linux/wlp.h>
#include "../i1480-wlp.h"

#undef i1480u_FLOW_CONTROL	/* Enable flow control code */

/**
 * Basic flow control
 */
enum {
	i1480u_TX_INFLIGHT_MAX = 1000,
	i1480u_TX_INFLIGHT_THRESHOLD = 100,
};

/** Maximum size of a transaction that we can tx/rx */
enum {
	/* Maximum packet size computed as follows: max UNTD header (8) +
	 * i1480 RX header (8) + max Ethernet header and payload (4096) +
	 * Padding added by skb_reserve (2) to make post Ethernet payload
	 * start on 16 byte boundary*/
	i1480u_MAX_RX_PKT_SIZE = 4114,
	i1480u_MAX_FRG_SIZE = 512,
	i1480u_RX_BUFS = 9,
};


/**
 * UNTD packet type
 *
 * We need to fragment any payload whose UNTD packet is going to be
 * bigger than i1480u_MAX_FRG_SIZE.
 */
enum i1480u_pkt_type {
	i1480u_PKT_FRAG_1ST = 0x1,
	i1480u_PKT_FRAG_NXT = 0x0,
	i1480u_PKT_FRAG_LST = 0x2,
	i1480u_PKT_FRAG_CMP = 0x3
};
enum {
	i1480u_PKT_NONE = 0x4,
};

/** USB Network Transfer Descriptor - common */
struct untd_hdr {
	u8     type;
	__le16 len;
} __attribute__((packed));

static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr)
{
	return hdr->type & 0x03;
}

static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr)
{
	return (hdr->type >> 2) & 0x01;
}

static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type)
{
	hdr->type = (hdr->type & ~0x03) | type;
}

static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx)
{
	hdr->type = (hdr->type & ~0x04) | (rx_tx << 2);
}


/**
 * USB Network Transfer Descriptor - Complete Packet
 *
 * This is for a packet that is smaller (header + payload) than
 * i1480u_MAX_FRG_SIZE.
 *
 * @hdr.total_len is the size of the payload; the payload doesn't
 * count this header nor the padding, but includes the size of i1480
 * header.
 */
struct untd_hdr_cmp {
	struct untd_hdr	hdr;
	u8		padding;
} __attribute__((packed));


/**
 * USB Network Transfer Descriptor - First fragment
 *
 * @hdr.len is the size of the *whole packet* (excluding UNTD
 * headers); @fragment_len is the size of the payload (excluding UNTD
 * headers, but including i1480 headers).
 */
struct untd_hdr_1st {
	struct untd_hdr	hdr;
	__le16		fragment_len;
	u8		padding[3];
} __attribute__((packed));


/**
 * USB Network Transfer Descriptor - Next / Last [Rest]
 *
 * @hdr.len is the size of the payload, not including headrs.
 */
struct untd_hdr_rst {
	struct untd_hdr	hdr;
	u8		padding;
} __attribute__((packed));


/**
 * Transmission context
 *
 * Wraps all the stuff needed to track a pending/active tx
 * operation.
 */
struct i1480u_tx {
	struct list_head list_node;
	struct i1480u *i1480u;
	struct urb *urb;

	struct sk_buff *skb;
	struct wlp_tx_hdr *wlp_tx_hdr;

	void *buf;	/* if NULL, no new buf was used */
	size_t buf_size;
};

/**
 * Basic flow control
 *
 * We maintain a basic flow control counter. "count" how many TX URBs are
 * outstanding. Only allow "max"
 * TX URBs to be outstanding. If this value is reached the queue will be
 * stopped. The queue will be restarted when there are
 * "threshold" URBs outstanding.
 * Maintain a counter of how many time the TX queue needed to be restarted
 * due to the "max" being exceeded and the "threshold" reached again. The
 * timestamp "restart_ts" is to keep track from when the counter was last
 * queried (see sysfs handling of file wlp_tx_inflight).
 */
struct i1480u_tx_inflight {
	atomic_t count;
	unsigned long max;
	unsigned long threshold;
	unsigned long restart_ts;
	atomic_t restart_count;
};

/**
 * Instance of a i1480u WLP interface
 *
 * Keeps references to the USB device that wraps it, as well as it's
 * interface and associated UWB host controller. As well, it also
 * keeps a link to the netdevice for integration into the networking
 * stack.
 * We maintian separate error history for the tx and rx endpoints because
 * the implementation does not rely on locking - having one shared
 * structure between endpoints may cause problems. Adding locking to the
 * implementation will have higher cost than adding a separate structure.
 */
struct i1480u {
	struct usb_device *usb_dev;
	struct usb_interface *usb_iface;
	struct net_device *net_dev;

	spinlock_t lock;
	struct net_device_stats stats;

	/* RX context handling */
	struct sk_buff *rx_skb;
	struct uwb_dev_addr rx_srcaddr;
	size_t rx_untd_pkt_size;
	struct i1480u_rx_buf {
		struct i1480u *i1480u;	/* back pointer */
		struct urb *urb;
		struct sk_buff *data;	/* i1480u_MAX_RX_PKT_SIZE each */
	} rx_buf[i1480u_RX_BUFS];	/* N bufs */

	spinlock_t tx_list_lock;	/* TX context */
	struct list_head tx_list;
	u8 tx_stream;

	struct stats lqe_stats, rssi_stats;	/* radio statistics */

	/* Options we can set from sysfs */
	struct wlp_options options;
	struct uwb_notifs_handler uwb_notifs_handler;
	struct edc tx_errors;
	struct edc rx_errors;
	struct wlp wlp;
#ifdef i1480u_FLOW_CONTROL
	struct urb *notif_urb;
	struct edc notif_edc;		/* error density counter */
	u8 notif_buffer[1];
#endif
	struct i1480u_tx_inflight tx_inflight;
};

/* Internal interfaces */
extern void i1480u_rx_cb(struct urb *urb);
extern int i1480u_rx_setup(struct i1480u *);
extern void i1480u_rx_release(struct i1480u *);
extern void i1480u_tx_release(struct i1480u *);
extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *,
			     struct uwb_dev_addr *);
extern void i1480u_stop_queue(struct wlp *);
extern void i1480u_start_queue(struct wlp *);
extern int i1480u_sysfs_setup(struct i1480u *);
extern void i1480u_sysfs_release(struct i1480u *);

/* netdev interface */
extern int i1480u_open(struct net_device *);
extern int i1480u_stop(struct net_device *);
extern int i1480u_hard_start_xmit(struct sk_buff *, struct net_device *);
extern void i1480u_tx_timeout(struct net_device *);
extern int i1480u_set_config(struct net_device *, struct ifmap *);
extern struct net_device_stats *i1480u_get_stats(struct net_device *);
extern int i1480u_change_mtu(struct net_device *, int);
extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs);

/* bandwidth allocation callback */
extern void  i1480u_bw_alloc_cb(struct uwb_rsv *);

/* Sys FS */
extern struct attribute_group i1480u_wlp_attr_group;

#endif /* #ifndef __i1480u_wlp_h__ */
Loading