Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/dvrabel/uwb

* 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/dvrabel/uwb:
  uwb: Orphan the UWB and WUSB subsystems
  uwb: Remove the WLP subsystem and drivers
This commit is contained in:
Linus Torvalds 2010-10-25 08:30:48 -07:00
commit c55960499f
22 changed files with 3 additions and 8555 deletions

View file

@ -1562,9 +1562,8 @@ F: net/ceph
F: include/linux/ceph
CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
M: David Vrabel <david.vrabel@csr.com>
L: linux-usb@vger.kernel.org
S: Supported
S: Orphan
F: Documentation/usb/WUSB-Design-overview.txt
F: Documentation/usb/wusb-cbaf
F: drivers/usb/host/hwa-hc.c
@ -5991,13 +5990,9 @@ F: Documentation/filesystems/ufs.txt
F: fs/ufs/
ULTRA-WIDEBAND (UWB) SUBSYSTEM:
M: David Vrabel <david.vrabel@csr.com>
L: linux-usb@vger.kernel.org
S: Supported
S: Orphan
F: drivers/uwb/
X: drivers/uwb/wlp/
X: drivers/uwb/i1480/i1480u-wlp/
X: drivers/uwb/i1480/i1480-wlp.h
F: include/linux/uwb.h
F: include/linux/uwb/
@ -6533,15 +6528,6 @@ F: include/linux/wimax/debug.h
F: include/net/wimax.h
F: net/wimax/
WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM
M: David Vrabel <david.vrabel@csr.com>
L: netdev@vger.kernel.org
S: Maintained
F: include/linux/wlp.h
F: drivers/uwb/wlp/
F: drivers/uwb/i1480/i1480u-wlp/
F: drivers/uwb/i1480/i1480-wlp.h
WISTRON LAPTOP BUTTON DRIVER
M: Miloslav Trmac <mitr@volny.cz>
S: Maintained

View file

@ -12,8 +12,7 @@ menuconfig UWB
technology using a wide spectrum (3.1-10.6GHz). It is
optimized for in-room use (480Mbps at 2 meters, 110Mbps at
10m). It serves as the transport layer for other protocols,
such as Wireless USB (WUSB), IP (WLP) and upcoming
Bluetooth and 1394
such as Wireless USB (WUSB).
The topology is peer to peer; however, higher level
protocols (such as WUSB) might impose a master/slave
@ -58,13 +57,6 @@ config UWB_WHCI
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.
config UWB_WLP
tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)"
depends on UWB && NET
help
This is a common library for drivers that implement
networking over UWB.
config UWB_I1480U
tristate "Support for Intel Wireless UWB Link 1480 HWA"
depends on UWB_HWA
@ -77,14 +69,4 @@ config UWB_I1480U
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.
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

View file

@ -1,5 +1,4 @@
obj-$(CONFIG_UWB) += uwb.o
obj-$(CONFIG_UWB_WLP) += wlp/
obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
obj-$(CONFIG_UWB_HWA) += hwa-rc.o
obj-$(CONFIG_UWB_I1480U) += i1480/

View file

@ -1,2 +1 @@
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/

View file

@ -1,200 +0,0 @@
/*
* 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__ */

View file

@ -1,8 +0,0 @@
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
i1480u-wlp-objs := \
lc.o \
netdev.o \
rx.o \
sysfs.o \
tx.o

View file

@ -1,283 +0,0 @@
/*
* 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;
/* 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 netdev_tx_t 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 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__ */

View file

@ -1,424 +0,0 @@
/*
* 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
*
* This implements a very simple network driver for the WLP USB
* device that is associated to a UWB (Ultra Wide Band) host.
*
* This is seen as an interface of a composite device. Once the UWB
* host has an association to another WLP capable device, the
* networking interface (aka WLP) can start to send packets back and
* forth.
*
* Limitations:
*
* - Hand cranked; can't ifup the interface until there is an association
*
* - BW allocation very simplistic [see i1480u_mas_set() and callees].
*
*
* ROADMAP:
*
* ENTRY POINTS (driver model):
*
* i1480u_driver_{exit,init}(): initialization of the driver.
*
* i1480u_probe(): called by the driver code when a device
* matching 'i1480u_id_table' is connected.
*
* This allocs a netdev instance, inits with
* i1480u_add(), then registers_netdev().
* i1480u_init()
* i1480u_add()
*
* i1480u_disconnect(): device has been disconnected/module
* is being removed.
* i1480u_rm()
*/
#include <linux/gfp.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include "i1480u-wlp.h"
static inline
void i1480u_init(struct i1480u *i1480u)
{
/* nothing so far... doesn't it suck? */
spin_lock_init(&i1480u->lock);
INIT_LIST_HEAD(&i1480u->tx_list);
spin_lock_init(&i1480u->tx_list_lock);
wlp_options_init(&i1480u->options);
edc_init(&i1480u->tx_errors);
edc_init(&i1480u->rx_errors);
#ifdef i1480u_FLOW_CONTROL
edc_init(&i1480u->notif_edc);
#endif
stats_init(&i1480u->lqe_stats);
stats_init(&i1480u->rssi_stats);
wlp_init(&i1480u->wlp);
}
/**
* Fill WLP device information structure
*
* The structure will contain a few character arrays, each ending with a
* null terminated string. Each string has to fit (excluding terminating
* character) into a specified range obtained from the WLP substack.
*
* It is still not clear exactly how this device information should be
* obtained. Until we find out we use the USB device descriptor as backup, some
* information elements have intuitive mappings, other not.
*/
static
void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info)
{
struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
struct usb_device *usb_dev = i1480u->usb_dev;
/* Treat device name and model name the same */
if (usb_dev->descriptor.iProduct) {
usb_string(usb_dev, usb_dev->descriptor.iProduct,
dev_info->name, sizeof(dev_info->name));
usb_string(usb_dev, usb_dev->descriptor.iProduct,
dev_info->model_name, sizeof(dev_info->model_name));
}
if (usb_dev->descriptor.iManufacturer)
usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
dev_info->manufacturer,
sizeof(dev_info->manufacturer));
scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x",
__le16_to_cpu(usb_dev->descriptor.bcdDevice));
if (usb_dev->descriptor.iSerialNumber)
usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
dev_info->serial, sizeof(dev_info->serial));
/* FIXME: where should we obtain category? */
dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER);
/* FIXME: Complete OUI and OUIsubdiv attributes */
}
#ifdef i1480u_FLOW_CONTROL
/**
* Callback for the notification endpoint
*
* This mostly controls the xon/xoff protocol. In case of hard error,
* we stop the queue. If not, we always retry.
*/
static
void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs)
{
struct i1480u *i1480u = urb->context;
struct usb_interface *usb_iface = i1480u->usb_iface;
struct device *dev = &usb_iface->dev;
int result;
switch (urb->status) {
case 0: /* Got valid data, do xon/xoff */
switch (i1480u->notif_buffer[0]) {
case 'N':
dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies);
netif_stop_queue(i1480u->net_dev);
break;
case 'A':
dev_err(dev, "XON STARTING queue at %lu\n", jiffies);
netif_start_queue(i1480u->net_dev);
break;
default:
dev_err(dev, "NEP: unknown data 0x%02hhx\n",
i1480u->notif_buffer[0]);
}
break;
case -ECONNRESET: /* Controlled situation ... */
case -ENOENT: /* we killed the URB... */
dev_err(dev, "NEP: URB reset/noent %d\n", urb->status);
goto error;
case -ESHUTDOWN: /* going away! */
dev_err(dev, "NEP: URB down %d\n", urb->status);
goto error;
default: /* Retry unless it gets ugly */
if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS,
EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "NEP: URB max acceptable errors "
"exceeded; resetting device\n");
goto error_reset;
}
dev_err(dev, "NEP: URB error %d\n", urb->status);
break;
}
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result < 0) {
dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n",
result);
goto error_reset;
}
return;
error_reset:
wlp_reset_all(&i1480-wlp);
error:
netif_stop_queue(i1480u->net_dev);
return;
}
#endif
static const struct net_device_ops i1480u_netdev_ops = {
.ndo_open = i1480u_open,
.ndo_stop = i1480u_stop,
.ndo_start_xmit = i1480u_hard_start_xmit,
.ndo_tx_timeout = i1480u_tx_timeout,
.ndo_set_config = i1480u_set_config,
.ndo_change_mtu = i1480u_change_mtu,
};
static
int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface)
{
int result = -ENODEV;
struct wlp *wlp = &i1480u->wlp;
struct usb_device *usb_dev = interface_to_usbdev(iface);
struct net_device *net_dev = i1480u->net_dev;
struct uwb_rc *rc;
struct uwb_dev *uwb_dev;
#ifdef i1480u_FLOW_CONTROL
struct usb_endpoint_descriptor *epd;
#endif
i1480u->usb_dev = usb_get_dev(usb_dev);
i1480u->usb_iface = iface;
rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev);
if (rc == NULL) {
dev_err(&iface->dev, "Cannot get associated UWB Radio "
"Controller\n");
goto out;
}
wlp->xmit_frame = i1480u_xmit_frame;
wlp->fill_device_info = i1480u_fill_device_info;
wlp->stop_queue = i1480u_stop_queue;
wlp->start_queue = i1480u_start_queue;
result = wlp_setup(wlp, rc, net_dev);
if (result < 0) {
dev_err(&iface->dev, "Cannot setup WLP\n");
goto error_wlp_setup;
}
result = 0;
ether_setup(net_dev); /* make it an etherdevice */
uwb_dev = &rc->uwb_dev;
/* FIXME: hookup address change notifications? */
memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data,
sizeof(net_dev->dev_addr));
net_dev->hard_header_len = sizeof(struct untd_hdr_cmp)
+ sizeof(struct wlp_tx_hdr)
+ WLP_DATA_HLEN
+ ETH_HLEN;
net_dev->mtu = 3500;
net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */
/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */
/* FIXME: multicast disabled */
net_dev->flags &= ~IFF_MULTICAST;
net_dev->features &= ~NETIF_F_SG;
net_dev->features &= ~NETIF_F_FRAGLIST;
/* All NETIF_F_*_CSUM disabled */
net_dev->features |= NETIF_F_HIGHDMA;
net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */
net_dev->netdev_ops = &i1480u_netdev_ops;
#ifdef i1480u_FLOW_CONTROL
/* Notification endpoint setup (submitted when we open the device) */
i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
if (i1480u->notif_urb == NULL) {
dev_err(&iface->dev, "Unable to allocate notification URB\n");
result = -ENOMEM;
goto error_urb_alloc;
}
epd = &iface->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(i1480u->notif_urb, usb_dev,
usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
i1480u->notif_buffer, sizeof(i1480u->notif_buffer),
i1480u_notif_cb, i1480u, epd->bInterval);
#endif
i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX;
i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
i1480u->tx_inflight.restart_ts = jiffies;
usb_set_intfdata(iface, i1480u);
return result;
#ifdef i1480u_FLOW_CONTROL
error_urb_alloc:
#endif
wlp_remove(wlp);
error_wlp_setup:
uwb_rc_put(rc);
out:
usb_put_dev(i1480u->usb_dev);
return result;
}
static void i1480u_rm(struct i1480u *i1480u)
{
struct uwb_rc *rc = i1480u->wlp.rc;
usb_set_intfdata(i1480u->usb_iface, NULL);
#ifdef i1480u_FLOW_CONTROL
usb_kill_urb(i1480u->notif_urb);
usb_free_urb(i1480u->notif_urb);
#endif
wlp_remove(&i1480u->wlp);
uwb_rc_put(rc);
usb_put_dev(i1480u->usb_dev);
}
/** Just setup @net_dev's i1480u private data */
static void i1480u_netdev_setup(struct net_device *net_dev)
{
struct i1480u *i1480u = netdev_priv(net_dev);
/* Initialize @i1480u */
memset(i1480u, 0, sizeof(*i1480u));
i1480u_init(i1480u);
}
/**
* Probe a i1480u interface and register it
*
* @iface: USB interface to link to
* @id: USB class/subclass/protocol id
* @returns: 0 if ok, < 0 errno code on error.
*
* Does basic housekeeping stuff and then allocs a netdev with space
* for the i1480u data. Initializes, registers in i1480u, registers in
* netdev, ready to go.
*/
static int i1480u_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
int result;
struct net_device *net_dev;
struct device *dev = &iface->dev;
struct i1480u *i1480u;
/* Allocate instance [calls i1480u_netdev_setup() on it] */
result = -ENOMEM;
net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup);
if (net_dev == NULL) {
dev_err(dev, "no memory for network device instance\n");
goto error_alloc_netdev;
}
SET_NETDEV_DEV(net_dev, dev);
i1480u = netdev_priv(net_dev);
i1480u->net_dev = net_dev;
result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */
if (result < 0) {
dev_err(dev, "cannot add i1480u device: %d\n", result);
goto error_i1480u_add;
}
result = register_netdev(net_dev); /* Okey dokey, bring it up */
if (result < 0) {
dev_err(dev, "cannot register network device: %d\n", result);
goto error_register_netdev;
}
i1480u_sysfs_setup(i1480u);
if (result < 0)
goto error_sysfs_init;
return 0;
error_sysfs_init:
unregister_netdev(net_dev);
error_register_netdev:
i1480u_rm(i1480u);
error_i1480u_add:
free_netdev(net_dev);
error_alloc_netdev:
return result;
}
/**
* Disconect a i1480u from the system.
*
* i1480u_stop() has been called before, so al the rx and tx contexts
* have been taken down already. Make sure the queue is stopped,
* unregister netdev and i1480u, free and kill.
*/
static void i1480u_disconnect(struct usb_interface *iface)
{
struct i1480u *i1480u;
struct net_device *net_dev;
i1480u = usb_get_intfdata(iface);
net_dev = i1480u->net_dev;
netif_stop_queue(net_dev);
#ifdef i1480u_FLOW_CONTROL
usb_kill_urb(i1480u->notif_urb);
#endif
i1480u_sysfs_release(i1480u);
unregister_netdev(net_dev);
i1480u_rm(i1480u);
free_netdev(net_dev);
}
static struct usb_device_id i1480u_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE \
| USB_DEVICE_ID_MATCH_DEV_INFO \
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x8086,
.idProduct = 0x0c3b,
.bDeviceClass = 0xef,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x02,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xff,
.bInterfaceProtocol = 0xff,
},
{},
};
MODULE_DEVICE_TABLE(usb, i1480u_id_table);
static struct usb_driver i1480u_driver = {
.name = KBUILD_MODNAME,
.probe = i1480u_probe,
.disconnect = i1480u_disconnect,
.id_table = i1480u_id_table,
};
static int __init i1480u_driver_init(void)
{
return usb_register(&i1480u_driver);
}
module_init(i1480u_driver_init);
static void __exit i1480u_driver_exit(void)
{
usb_deregister(&i1480u_driver);
}
module_exit(i1480u_driver_exit);
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB");
MODULE_LICENSE("GPL");

View file

@ -1,331 +0,0 @@
/*
* 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/slab.h>
#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);
}

View file

@ -1,474 +0,0 @@
/*
* 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.
*
*
* i1480u's RX handling is simple. i1480u will send the received
* network packets broken up in fragments; 1 to N fragments make a
* packet, we assemble them together and deliver the packet with netif_rx().
*
* Beacuse each USB transfer is a *single* fragment (except when the
* transfer contains a first fragment), each URB called thus
* back contains one or two fragments. So we queue N URBs, each with its own
* fragment buffer. When a URB is done, we process it (adding to the
* current skb from the fragment buffer until complete). Once
* processed, we requeue the URB. There is always a bunch of URBs
* ready to take data, so the intergap should be minimal.
*
* An URB's transfer buffer is the data field of a socket buffer. This
* reduces copying as data can be passed directly to network layer. If a
* complete packet or 1st fragment is received the URB's transfer buffer is
* taken away from it and used to send data to the network layer. In this
* case a new transfer buffer is allocated to the URB before being requeued.
* If a "NEXT" or "LAST" fragment is received, the fragment contents is
* appended to the RX packet under construction and the transfer buffer
* is reused. To be able to use this buffer to assemble complete packets
* we set each buffer's size to that of the MAX ethernet packet that can
* be received. There is thus room for improvement in memory usage.
*
* When the max tx fragment size increases, we should be able to read
* data into the skbs directly with very simple code.
*
* ROADMAP:
*
* ENTRY POINTS:
*
* i1480u_rx_setup(): setup RX context [from i1480u_open()]
*
* i1480u_rx_release(): release RX context [from i1480u_stop()]
*
* i1480u_rx_cb(): called when the RX USB URB receives a
* packet. It removes the header and pushes it up
* the Linux netdev stack with netif_rx().
*
* i1480u_rx_buffer()
* i1480u_drop() and i1480u_fix()
* i1480u_skb_deliver
*
*/
#include <linux/gfp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include "i1480u-wlp.h"
/*
* Setup the RX context
*
* Each URB is provided with a transfer_buffer that is the data field
* of a new socket buffer.
*/
int i1480u_rx_setup(struct i1480u *i1480u)
{
int result, cnt;
struct device *dev = &i1480u->usb_iface->dev;
struct net_device *net_dev = i1480u->net_dev;
struct usb_endpoint_descriptor *epd;
struct sk_buff *skb;
/* Alloc RX stuff */
i1480u->rx_skb = NULL; /* not in process of receiving packet */
result = -ENOMEM;
epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc;
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt];
rx_buf->i1480u = i1480u;
skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
if (!skb) {
dev_err(dev,
"RX: cannot allocate RX buffer %d\n", cnt);
result = -ENOMEM;
goto error;
}
skb->dev = net_dev;
skb->ip_summed = CHECKSUM_NONE;
skb_reserve(skb, 2);
rx_buf->data = skb;
rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
if (unlikely(rx_buf->urb == NULL)) {
dev_err(dev, "RX: cannot allocate URB %d\n", cnt);
result = -ENOMEM;
goto error;
}
usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev,
usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress),
rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2,
i1480u_rx_cb, rx_buf);
result = usb_submit_urb(rx_buf->urb, GFP_NOIO);
if (unlikely(result < 0)) {
dev_err(dev, "RX: cannot submit URB %d: %d\n",
cnt, result);
goto error;
}
}
return 0;
error:
i1480u_rx_release(i1480u);
return result;
}
/* Release resources associated to the rx context */
void i1480u_rx_release(struct i1480u *i1480u)
{
int cnt;
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
if (i1480u->rx_buf[cnt].data)
dev_kfree_skb(i1480u->rx_buf[cnt].data);
if (i1480u->rx_buf[cnt].urb) {
usb_kill_urb(i1480u->rx_buf[cnt].urb);
usb_free_urb(i1480u->rx_buf[cnt].urb);
}
}
if (i1480u->rx_skb != NULL)
dev_kfree_skb(i1480u->rx_skb);
}
static
void i1480u_rx_unlink_urbs(struct i1480u *i1480u)
{
int cnt;
for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
if (i1480u->rx_buf[cnt].urb)
usb_unlink_urb(i1480u->rx_buf[cnt].urb);
}
}
/* Fix an out-of-sequence packet */
#define i1480u_fix(i1480u, msg...) \
do { \
if (printk_ratelimit()) \
dev_err(&i1480u->usb_iface->dev, msg); \
dev_kfree_skb_irq(i1480u->rx_skb); \
i1480u->rx_skb = NULL; \
i1480u->rx_untd_pkt_size = 0; \
} while (0)
/* Drop an out-of-sequence packet */
#define i1480u_drop(i1480u, msg...) \
do { \
if (printk_ratelimit()) \
dev_err(&i1480u->usb_iface->dev, msg); \
i1480u->net_dev->stats.rx_dropped++; \
} while (0)
/* Finalizes setting up the SKB and delivers it
*
* We first pass the incoming frame to WLP substack for verification. It
* may also be a WLP association frame in which case WLP will take over the
* processing. If WLP does not take it over it will still verify it, if the
* frame is invalid the skb will be freed by WLP and we will not continue
* parsing.
* */
static
void i1480u_skb_deliver(struct i1480u *i1480u)
{
int should_parse;
struct net_device *net_dev = i1480u->net_dev;
struct device *dev = &i1480u->usb_iface->dev;
should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb,
&i1480u->rx_srcaddr);
if (!should_parse)
goto out;
i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev);
net_dev->stats.rx_packets++;
net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size;
netif_rx(i1480u->rx_skb); /* deliver */
out:
i1480u->rx_skb = NULL;
i1480u->rx_untd_pkt_size = 0;
}
/*
* Process a buffer of data received from the USB RX endpoint
*
* First fragment arrives with next or last fragment. All other fragments
* arrive alone.
*
* /me hates long functions.
*/
static
void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf)
{
unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */
size_t untd_hdr_size, untd_frg_size;
size_t i1480u_hdr_size;
struct wlp_rx_hdr *i1480u_hdr = NULL;
struct i1480u *i1480u = rx_buf->i1480u;
struct sk_buff *skb = rx_buf->data;
int size_left = rx_buf->urb->actual_length;
void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */
struct untd_hdr *untd_hdr;
struct net_device *net_dev = i1480u->net_dev;
struct device *dev = &i1480u->usb_iface->dev;
struct sk_buff *new_skb;
#if 0
dev_fnstart(dev,
"(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left);
dev_err(dev, "RX packet, %zu bytes\n", size_left);
dump_bytes(dev, ptr, size_left);
#endif
i1480u_hdr_size = sizeof(struct wlp_rx_hdr);
while (size_left > 0) {
if (pkt_completed) {
i1480u_drop(i1480u, "RX: fragment follows completed"
"packet in same buffer. Dropping\n");
break;
}
untd_hdr = ptr;
if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */
i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n");
goto out;
}
if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */
i1480u_drop(i1480u, "RX: TX bit set! Dropping\n");
goto out;
}
switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */
case i1480u_PKT_FRAG_1ST: {
struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr;
dev_dbg(dev, "1st fragment\n");
untd_hdr_size = sizeof(struct untd_hdr_1st);
if (i1480u->rx_skb != NULL)
i1480u_fix(i1480u, "RX: 1st fragment out of "
"sequence! Fixing\n");
if (size_left < untd_hdr_size + i1480u_hdr_size) {
i1480u_drop(i1480u, "RX: short 1st fragment! "
"Dropping\n");
goto out;
}
i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len)
- i1480u_hdr_size;
untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len);
if (size_left < untd_hdr_size + untd_frg_size) {
i1480u_drop(i1480u,
"RX: short payload! Dropping\n");
goto out;
}
i1480u->rx_skb = skb;
i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size;
i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size);
skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
rx_buf->data = NULL; /* need to create new buffer */
break;
}
case i1480u_PKT_FRAG_NXT: {
dev_dbg(dev, "nxt fragment\n");
untd_hdr_size = sizeof(struct untd_hdr_rst);
if (i1480u->rx_skb == NULL) {
i1480u_drop(i1480u, "RX: next fragment out of "
"sequence! Dropping\n");
goto out;
}
if (size_left < untd_hdr_size) {
i1480u_drop(i1480u, "RX: short NXT fragment! "
"Dropping\n");
goto out;
}
untd_frg_size = le16_to_cpu(untd_hdr->len);
if (size_left < untd_hdr_size + untd_frg_size) {
i1480u_drop(i1480u,
"RX: short payload! Dropping\n");
goto out;
}
memmove(skb_put(i1480u->rx_skb, untd_frg_size),
ptr + untd_hdr_size, untd_frg_size);
break;
}
case i1480u_PKT_FRAG_LST: {
dev_dbg(dev, "Lst fragment\n");
untd_hdr_size = sizeof(struct untd_hdr_rst);
if (i1480u->rx_skb == NULL) {
i1480u_drop(i1480u, "RX: last fragment out of "
"sequence! Dropping\n");
goto out;
}
if (size_left < untd_hdr_size) {
i1480u_drop(i1480u, "RX: short LST fragment! "
"Dropping\n");
goto out;
}
untd_frg_size = le16_to_cpu(untd_hdr->len);
if (size_left < untd_frg_size + untd_hdr_size) {
i1480u_drop(i1480u,
"RX: short payload! Dropping\n");
goto out;
}
memmove(skb_put(i1480u->rx_skb, untd_frg_size),
ptr + untd_hdr_size, untd_frg_size);
pkt_completed = 1;
break;
}
case i1480u_PKT_FRAG_CMP: {
dev_dbg(dev, "cmp fragment\n");
untd_hdr_size = sizeof(struct untd_hdr_cmp);
if (i1480u->rx_skb != NULL)
i1480u_fix(i1480u, "RX: fix out-of-sequence CMP"
" fragment!\n");
if (size_left < untd_hdr_size + i1480u_hdr_size) {
i1480u_drop(i1480u, "RX: short CMP fragment! "
"Dropping\n");
goto out;
}
i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len);
untd_frg_size = i1480u->rx_untd_pkt_size;
if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) {
i1480u_drop(i1480u,
"RX: short payload! Dropping\n");
goto out;
}
i1480u->rx_skb = skb;
i1480u_hdr = (void *) untd_hdr + untd_hdr_size;
i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size);
skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
rx_buf->data = NULL; /* for hand off skb to network stack */
pkt_completed = 1;
i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */
break;
}
default:
i1480u_drop(i1480u, "RX: unknown packet type %u! "
"Dropping\n", untd_hdr_type(untd_hdr));
goto out;
}
size_left -= untd_hdr_size + untd_frg_size;
if (size_left > 0)
ptr += untd_hdr_size + untd_frg_size;
}
if (pkt_completed)
i1480u_skb_deliver(i1480u);
out:
/* recreate needed RX buffers*/
if (rx_buf->data == NULL) {
/* buffer is being used to receive packet, create new */
new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
if (!new_skb) {
if (printk_ratelimit())
dev_err(dev,
"RX: cannot allocate RX buffer\n");
} else {
new_skb->dev = net_dev;
new_skb->ip_summed = CHECKSUM_NONE;
skb_reserve(new_skb, 2);
rx_buf->data = new_skb;
}
}
return;
}
/*
* Called when an RX URB has finished receiving or has found some kind
* of error condition.
*
* LIMITATIONS:
*
* - We read USB-transfers, each transfer contains a SINGLE fragment
* (can contain a complete packet, or a 1st, next, or last fragment
* of a packet).
* Looks like a transfer can contain more than one fragment (07/18/06)
*
* - Each transfer buffer is the size of the maximum packet size (minus
* headroom), i1480u_MAX_PKT_SIZE - 2
*
* - We always read the full USB-transfer, no partials.
*
* - Each transfer is read directly into a skb. This skb will be used to
* send data to the upper layers if it is the first fragment or a complete
* packet. In the other cases the data will be copied from the skb to
* another skb that is being prepared for the upper layers from a prev
* first fragment.
*
* It is simply too much of a pain. Gosh, there should be a unified
* SG infrastructure for *everything* [so that I could declare a SG
* buffer, pass it to USB for receiving, append some space to it if
* I wish, receive more until I have the whole chunk, adapt
* pointers on each fragment to remove hardware headers and then
* attach that to an skbuff and netif_rx()].
*/
void i1480u_rx_cb(struct urb *urb)
{
int result;
int do_parse_buffer = 1;
struct i1480u_rx_buf *rx_buf = urb->context;
struct i1480u *i1480u = rx_buf->i1480u;
struct device *dev = &i1480u->usb_iface->dev;
unsigned long flags;
u8 rx_buf_idx = rx_buf - i1480u->rx_buf;
switch (urb->status) {
case 0:
break;
case -ECONNRESET: /* Not an error, but a controlled situation; */
case -ENOENT: /* (we killed the URB)...so, no broadcast */
case -ESHUTDOWN: /* going away! */
dev_err(dev, "RX URB[%u]: goind down %d\n",
rx_buf_idx, urb->status);
goto error;
default:
dev_err(dev, "RX URB[%u]: unknown status %d\n",
rx_buf_idx, urb->status);
if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS,
EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "RX: max acceptable errors exceeded,"
" resetting device.\n");
i1480u_rx_unlink_urbs(i1480u);
wlp_reset_all(&i1480u->wlp);
goto error;
}
do_parse_buffer = 0;
break;
}
spin_lock_irqsave(&i1480u->lock, flags);
/* chew the data fragments, extract network packets */
if (do_parse_buffer) {
i1480u_rx_buffer(rx_buf);
if (rx_buf->data) {
rx_buf->urb->transfer_buffer = rx_buf->data->data;
result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC);
if (result < 0) {
dev_err(dev, "RX URB[%u]: cannot submit %d\n",
rx_buf_idx, result);
}
}
}
spin_unlock_irqrestore(&i1480u->lock, flags);
error:
return;
}

View file

@ -1,407 +0,0 @@
/*
* WUSB Wire Adapter: WLP interface
* Sysfs 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.
*
*
* FIXME: docs
*/
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/device.h>
#include "i1480u-wlp.h"
/**
*
* @dev: Class device from the net_device; assumed refcnted.
*
* Yes, I don't lock--we assume it is refcounted and I am getting a
* single byte value that is kind of atomic to read.
*/
ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf)
{
return sprintf(buf, "%u\n",
wlp_tx_hdr_phy_rate(&options->def_tx_hdr));
}
EXPORT_SYMBOL_GPL(uwb_phy_rate_show);
ssize_t uwb_phy_rate_store(struct wlp_options *options,
const char *buf, size_t size)
{
ssize_t result;
unsigned rate;
result = sscanf(buf, "%u\n", &rate);
if (result != 1) {
result = -EINVAL;
goto out;
}
result = -EINVAL;
if (rate >= UWB_PHY_RATE_INVALID)
goto out;
wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate);
result = 0;
out:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(uwb_phy_rate_store);
ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf)
{
return sprintf(buf, "%u\n",
wlp_tx_hdr_rts_cts(&options->def_tx_hdr));
}
EXPORT_SYMBOL_GPL(uwb_rts_cts_show);
ssize_t uwb_rts_cts_store(struct wlp_options *options,
const char *buf, size_t size)
{
ssize_t result;
unsigned value;
result = sscanf(buf, "%u\n", &value);
if (result != 1) {
result = -EINVAL;
goto out;
}
result = -EINVAL;
wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value);
result = 0;
out:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(uwb_rts_cts_store);
ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf)
{
return sprintf(buf, "%u\n",
wlp_tx_hdr_ack_policy(&options->def_tx_hdr));
}
EXPORT_SYMBOL_GPL(uwb_ack_policy_show);
ssize_t uwb_ack_policy_store(struct wlp_options *options,
const char *buf, size_t size)
{
ssize_t result;
unsigned value;
result = sscanf(buf, "%u\n", &value);
if (result != 1 || value > UWB_ACK_B_REQ) {
result = -EINVAL;
goto out;
}
wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value);
result = 0;
out:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(uwb_ack_policy_store);
/**
* Show the PCA base priority.
*
* We can access without locking, as the value is (for now) orthogonal
* to other values.
*/
ssize_t uwb_pca_base_priority_show(const struct wlp_options *options,
char *buf)
{
return sprintf(buf, "%u\n",
options->pca_base_priority);
}
EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show);
/**
* Set the PCA base priority.
*
* We can access without locking, as the value is (for now) orthogonal
* to other values.
*/
ssize_t uwb_pca_base_priority_store(struct wlp_options *options,
const char *buf, size_t size)
{
ssize_t result = -EINVAL;
u8 pca_base_priority;
result = sscanf(buf, "%hhu\n", &pca_base_priority);
if (result != 1) {
result = -EINVAL;
goto out;
}
result = -EINVAL;
if (pca_base_priority >= 8)
goto out;
options->pca_base_priority = pca_base_priority;
/* Update TX header if we are currently using PCA. */
if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0)
wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority);
result = 0;
out:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store);
/**
* Show current inflight values
*
* Will print the current MAX and THRESHOLD values for the basic flow
* control. In addition it will report how many times the TX queue needed
* to be restarted since the last time this query was made.
*/
static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight,
char *buf)
{
ssize_t result;
unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ;
unsigned long restart_count = atomic_read(&inflight->restart_count);
result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n"
"#read: threshold max inflight_count restarts "
"seconds restarts/sec\n"
"#write: threshold max\n",
inflight->threshold, inflight->max,
atomic_read(&inflight->count),
restart_count, sec_elapsed,
sec_elapsed == 0 ? 0 : restart_count/sec_elapsed);
inflight->restart_ts = jiffies;
atomic_set(&inflight->restart_count, 0);
return result;
}
static
ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight,
const char *buf, size_t size)
{
unsigned long in_threshold, in_max;
ssize_t result;
result = sscanf(buf, "%lu %lu", &in_threshold, &in_max);
if (result != 2)
return -EINVAL;
if (in_max <= in_threshold)
return -EINVAL;
inflight->max = in_max;
inflight->threshold = in_threshold;
return size;
}
/*
* Glue (or function adaptors) for accesing info on sysfs
*
* [we need this indirection because the PCI driver does almost the
* same]
*
* Linux 2.6.21 changed how 'struct netdevice' does attributes (from
* having a 'struct class_dev' to having a 'struct device'). That is
* quite of a pain.
*
* So we try to abstract that here. i1480u_SHOW() and i1480u_STORE()
* create adaptors for extracting the 'struct i1480u' from a 'struct
* dev' and calling a function for doing a sysfs operation (as we have
* them factorized already). i1480u_ATTR creates the attribute file
* (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a
* class_device_attr_NAME or device_attr_NAME (for group registration).
*/
#define i1480u_SHOW(name, fn, param) \
static ssize_t i1480u_show_##name(struct device *dev, \
struct device_attribute *attr,\
char *buf) \
{ \
struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
return fn(&i1480u->param, buf); \
}
#define i1480u_STORE(name, fn, param) \
static ssize_t i1480u_store_##name(struct device *dev, \
struct device_attribute *attr,\
const char *buf, size_t size)\
{ \
struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
return fn(&i1480u->param, buf, size); \
}
#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \
i1480u_show_##name,\
i1480u_store_##name)
#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \
S_IRUGO, \
i1480u_show_##name, NULL)
#define i1480u_ATTR_NAME(a) (dev_attr_##a)
/*
* Sysfs adaptors
*/
i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options);
i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options);
i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR);
i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options);
i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options);
i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR);
i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options);
i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options);
i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR);
i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options);
i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options);
i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_eda, wlp_eda_show, wlp);
i1480u_STORE(wlp_eda, wlp_eda_store, wlp);
i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp);
i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp);
i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp);
i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp);
i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp);
i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp);
i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp);
i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp);
i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp);
i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp);
i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp);
i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp);
i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp);
i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp);
i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp);
i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp);
i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp);
i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp);
i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp);
i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp);
i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR);
i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp);
i1480u_ATTR_SHOW(wlp_neighborhood);
i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss);
i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss);
i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR);
/*
* Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over
* the last 256 received WLP frames (ECMA-368 13.3).
*
* [the -7dB that have to be substracted from the LQI to make the LQE
* are already taken into account].
*/
i1480u_SHOW(wlp_lqe, stats_show, lqe_stats);
i1480u_STORE(wlp_lqe, stats_store, lqe_stats);
i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR);
/*
* Show the Receive Signal Strength Indicator averaged over all the
* received WLP frames (ECMA-368 13.3). Still is not clear what
* this value is, but is kind of a percentage of the signal strength
* at the antenna.
*/
i1480u_SHOW(wlp_rssi, stats_show, rssi_stats);
i1480u_STORE(wlp_rssi, stats_store, rssi_stats);
i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR);
/**
* 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.
*/
i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight);
i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight);
i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR);
static struct attribute *i1480u_attrs[] = {
&i1480u_ATTR_NAME(uwb_phy_rate).attr,
&i1480u_ATTR_NAME(uwb_rts_cts).attr,
&i1480u_ATTR_NAME(uwb_ack_policy).attr,
&i1480u_ATTR_NAME(uwb_pca_base_priority).attr,
&i1480u_ATTR_NAME(wlp_lqe).attr,
&i1480u_ATTR_NAME(wlp_rssi).attr,
&i1480u_ATTR_NAME(wlp_eda).attr,
&i1480u_ATTR_NAME(wlp_uuid).attr,
&i1480u_ATTR_NAME(wlp_dev_name).attr,
&i1480u_ATTR_NAME(wlp_dev_manufacturer).attr,
&i1480u_ATTR_NAME(wlp_dev_model_name).attr,
&i1480u_ATTR_NAME(wlp_dev_model_nr).attr,
&i1480u_ATTR_NAME(wlp_dev_serial).attr,
&i1480u_ATTR_NAME(wlp_dev_prim_category).attr,
&i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr,
&i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr,
&i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr,
&i1480u_ATTR_NAME(wlp_neighborhood).attr,
&i1480u_ATTR_NAME(wss_activate).attr,
&i1480u_ATTR_NAME(wlp_tx_inflight).attr,
NULL,
};
static struct attribute_group i1480u_attr_group = {
.name = NULL, /* we want them in the same directory */
.attrs = i1480u_attrs,
};
int i1480u_sysfs_setup(struct i1480u *i1480u)
{
int result;
struct device *dev = &i1480u->usb_iface->dev;
result = sysfs_create_group(&i1480u->net_dev->dev.kobj,
&i1480u_attr_group);
if (result < 0)
dev_err(dev, "cannot initialize sysfs attributes: %d\n",
result);
return result;
}
void i1480u_sysfs_release(struct i1480u *i1480u)
{
sysfs_remove_group(&i1480u->net_dev->dev.kobj,
&i1480u_attr_group);
}

View file

@ -1,584 +0,0 @@
/*
* 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 <linux/slab.h>
#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);
net_dev->stats.tx_packets++;
net_dev->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
*/
netdev_tx_t 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);
net_dev->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);
}
}

View file

@ -1,10 +0,0 @@
obj-$(CONFIG_UWB_WLP) := wlp.o
wlp-objs := \
driver.o \
eda.o \
messages.o \
sysfs.o \
txrx.o \
wlp-lc.o \
wss-lc.o

View file

@ -1,43 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*
* Life cycle of WLP substack
*
* FIXME: Docs
*/
#include <linux/module.h>
static int __init wlp_subsys_init(void)
{
return 0;
}
module_init(wlp_subsys_init);
static void __exit wlp_subsys_exit(void)
{
return;
}
module_exit(wlp_subsys_exit);
MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>");
MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)");
MODULE_LICENSE("GPL");

View file

@ -1,415 +0,0 @@
/*
* WUSB Wire Adapter: WLP interface
* Ethernet to device address cache
*
* 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.
*
*
* We need to be able to map ethernet addresses to device addresses
* and back because there is not explicit relationship between the eth
* addresses used in the ETH frames and the device addresses (no, it
* would not have been simpler to force as ETH address the MBOA MAC
* address...no, not at all :).
*
* A device has one MBOA MAC address and one device address. It is possible
* for a device to have more than one virtual MAC address (although a
* virtual address can be the same as the MBOA MAC address). The device
* address is guaranteed to be unique among the devices in the extended
* beacon group (see ECMA 17.1.1). We thus use the device address as index
* to this cache. We do allow searching based on virtual address as this
* is how Ethernet frames will be addressed.
*
* We need to support virtual EUI-48. Although, right now the virtual
* EUI-48 will always be the same as the MAC SAP address. The EDA cache
* entry thus contains a MAC SAP address as well as the virtual address
* (used to map the network stack address to a neighbor). When we move
* to support more than one virtual MAC on a host then this organization
* will have to change. Perhaps a neighbor has a list of WSSs, each with a
* tag and virtual EUI-48.
*
* On data transmission
* it is used to determine if the neighbor is connected and what WSS it
* belongs to. With this we know what tag to add to the WLP frame. Storing
* the WSS in the EDA cache may be overkill because we only support one
* WSS. Hopefully we will support more than one WSS at some point.
* On data reception it is used to determine the WSS based on
* the tag and address of the transmitting neighbor.
*/
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
/* FIXME: cache is not purged, only on device close */
/* FIXME: does not scale, change to dynamic array */
/*
* Initialize the EDA cache
*
* @returns 0 if ok, < 0 errno code on error
*
* Call when the interface is being brought up
*
* NOTE: Keep it as a separate function as the implementation will
* change and be more complex.
*/
void wlp_eda_init(struct wlp_eda *eda)
{
INIT_LIST_HEAD(&eda->cache);
spin_lock_init(&eda->lock);
}
/*
* Release the EDA cache
*
* @returns 0 if ok, < 0 errno code on error
*
* Called when the interface is brought down
*/
void wlp_eda_release(struct wlp_eda *eda)
{
unsigned long flags;
struct wlp_eda_node *itr, *next;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
list_del(&itr->list_node);
kfree(itr);
}
spin_unlock_irqrestore(&eda->lock, flags);
}
/*
* Add an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*
* An address mapping is initially created when the neighbor device is seen
* for the first time (it is "onair"). At this time the neighbor is not
* connected or associated with a WSS so we only populate the Ethernet and
* Device address fields.
*
*/
int wlp_eda_create_node(struct wlp_eda *eda,
const unsigned char eth_addr[ETH_ALEN],
const struct uwb_dev_addr *dev_addr)
{
int result = 0;
struct wlp_eda_node *itr;
unsigned long flags;
BUG_ON(dev_addr == NULL || eth_addr == NULL);
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
printk(KERN_ERR "EDA cache already contains entry "
"for neighbor %02x:%02x\n",
dev_addr->data[1], dev_addr->data[0]);
result = -EEXIST;
goto out_unlock;
}
}
itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
if (itr != NULL) {
memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
itr->dev_addr = *dev_addr;
list_add(&itr->list_node, &eda->cache);
} else
result = -ENOMEM;
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Remove entry from EDA cache
*
* This is done when the device goes off air.
*/
void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
{
struct wlp_eda_node *itr, *next;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
list_del(&itr->list_node);
kfree(itr);
break;
}
}
spin_unlock_irqrestore(&eda->lock, flags);
}
/*
* Update an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*/
int wlp_eda_update_node(struct wlp_eda *eda,
const struct uwb_dev_addr *dev_addr,
struct wlp_wss *wss,
const unsigned char virt_addr[ETH_ALEN],
const u8 tag, const enum wlp_wss_connect state)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
/* Found it, update it */
itr->wss = wss;
memcpy(itr->virt_addr, virt_addr,
sizeof(itr->virt_addr));
itr->tag = tag;
itr->state = state;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Update only state field of an address mapping
*
* @returns 0 if ok, < 0 errno code on error
*/
int wlp_eda_update_node_state(struct wlp_eda *eda,
const struct uwb_dev_addr *dev_addr,
const enum wlp_wss_connect state)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
/* Found it, update it */
itr->state = state;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Return contents of EDA cache entry
*
* @dev_addr: index to EDA cache
* @eda_entry: pointer to where contents of EDA cache will be copied
*/
int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
struct wlp_eda_node *eda_entry)
{
int result = -ENOENT;
struct wlp_eda_node *itr;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
*eda_entry = *itr;
result = 0;
goto out_unlock;
}
}
/* Not found */
out_unlock:
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Execute function for every element in the cache
*
* @function: function to execute on element of cache (must be atomic)
* @priv: private data of function
* @returns: result of first function that failed, or last function
* executed if no function failed.
*
* Stop executing when function returns error for any element in cache.
*
* IMPORTANT: We are using a spinlock here: the function executed on each
* element has to be atomic.
*/
int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
void *priv)
{
int result = 0;
struct wlp *wlp = container_of(eda, struct wlp, eda);
struct wlp_eda_node *entry;
unsigned long flags;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(entry, &eda->cache, list_node) {
result = (*function)(wlp, entry, priv);
if (result < 0)
break;
}
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
/*
* Execute function for single element in the cache (return dev addr)
*
* @virt_addr: index into EDA cache used to determine which element to
* execute the function on
* @dev_addr: device address of element in cache will be returned using
* @dev_addr
* @function: function to execute on element of cache (must be atomic)
* @priv: private data of function
* @returns: result of function
*
* IMPORTANT: We are using a spinlock here: the function executed on the
* element has to be atomic.
*/
int wlp_eda_for_virtual(struct wlp_eda *eda,
const unsigned char virt_addr[ETH_ALEN],
struct uwb_dev_addr *dev_addr,
wlp_eda_for_each_f function,
void *priv)
{
int result = 0;
struct wlp *wlp = container_of(eda, struct wlp, eda);
struct wlp_eda_node *itr;
unsigned long flags;
int found = 0;
spin_lock_irqsave(&eda->lock, flags);
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(itr->virt_addr, virt_addr,
sizeof(itr->virt_addr))) {
result = (*function)(wlp, itr, priv);
*dev_addr = itr->dev_addr;
found = 1;
break;
}
}
if (!found)
result = -ENODEV;
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
"WLP_WSS_CONNECTED",
"WLP_WSS_CONNECT_FAILED",
};
static const char *wlp_wss_connect_state_str(unsigned id)
{
if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
return "unknown WSS connection state";
return __wlp_wss_connect_state[id];
}
/*
* View EDA cache from user space
*
* A debugging feature to give user visibility into the EDA cache. Also
* used to display members of WSS to user (called from wlp_wss_members_show())
*/
ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
struct wlp_eda_node *entry;
unsigned long flags;
struct wlp_eda *eda = &wlp->eda;
spin_lock_irqsave(&eda->lock, flags);
result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
"tag state virt_addr\n");
list_for_each_entry(entry, &eda->cache, list_node) {
result += scnprintf(buf + result, PAGE_SIZE - result,
"%pM %02x:%02x %p 0x%02x %s %pM\n",
entry->eth_addr,
entry->dev_addr.data[1],
entry->dev_addr.data[0], entry->wss,
entry->tag,
wlp_wss_connect_state_str(entry->state),
entry->virt_addr);
if (result >= PAGE_SIZE)
break;
}
spin_unlock_irqrestore(&eda->lock, flags);
return result;
}
EXPORT_SYMBOL_GPL(wlp_eda_show);
/*
* Add new EDA cache entry based on user input in sysfs
*
* Should only be used for debugging.
*
* The WSS is assumed to be the only WSS supported. This needs to be
* redesigned when we support more than one WSS.
*/
ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
struct wlp_eda *eda = &wlp->eda;
u8 eth_addr[6];
struct uwb_dev_addr dev_addr;
u8 tag;
unsigned state;
result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
"%02hhx:%02hhx %02hhx %u\n",
&eth_addr[0], &eth_addr[1],
&eth_addr[2], &eth_addr[3],
&eth_addr[4], &eth_addr[5],
&dev_addr.data[1], &dev_addr.data[0], &tag, &state);
switch (result) {
case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
/*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
result = -ENOSYS;
break;
case 10:
state = state >= 1 ? 1 : 0;
result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
if (result < 0 && result != -EEXIST)
goto error;
/* Set virtual addr to be same as MAC */
result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
eth_addr, tag, state);
if (result < 0)
goto error;
break;
default: /* bad format */
result = -EINVAL;
}
error:
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_eda_store);

File diff suppressed because it is too large Load diff

View file

@ -1,708 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* sysfs functions
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*
*/
#include <linux/wlp.h>
#include "wlp-internal.h"
static
size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
struct wlp_wssid_e *wssid_e)
{
size_t used = 0;
used += scnprintf(buf, bufsize, " WSS: ");
used += wlp_wss_uuid_print(buf + used, bufsize - used,
&wssid_e->wssid);
if (wssid_e->info != NULL) {
used += scnprintf(buf + used, bufsize - used, " ");
used += uwb_mac_addr_print(buf + used, bufsize - used,
&wssid_e->info->bcast);
used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
wssid_e->info->accept_enroll,
wssid_e->info->sec_status,
wssid_e->info->name);
}
return used;
}
/**
* Print out information learned from neighbor discovery
*
* Some fields being printed may not be included in the device discovery
* information (it is not mandatory). We are thus careful how the
* information is printed to ensure it is clear to the user what field is
* being referenced.
* The information being printed is for one time use - temporary storage is
* cleaned after it is printed.
*
* Ideally sysfs output should be on one line. The information printed here
* contain a few strings so it will be hard to parse if they are all
* printed on the same line - without agreeing on a standard field
* separator.
*/
static
ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
size_t bufsize)
{
size_t used = 0;
struct wlp_neighbor_e *neighb;
struct wlp_wssid_e *wssid_e;
mutex_lock(&wlp->nbmutex);
used = scnprintf(buf, bufsize, "#Neighbor information\n"
"#uuid dev_addr\n"
"# Device Name:\n# Model Name:\n# Manufacturer:\n"
"# Model Nr:\n# Serial:\n"
"# Pri Dev type: CategoryID OUI OUISubdiv "
"SubcategoryID\n"
"# WSS: WSSID WSS_name accept_enroll sec_status "
"bcast\n"
"# WSS: WSSID WSS_name accept_enroll sec_status "
"bcast\n\n");
list_for_each_entry(neighb, &wlp->neighbors, node) {
if (bufsize - used <= 0)
goto out;
used += wlp_wss_uuid_print(buf + used, bufsize - used,
&neighb->uuid);
buf[used++] = ' ';
used += uwb_dev_addr_print(buf + used, bufsize - used,
&neighb->uwb_dev->dev_addr);
if (neighb->info != NULL)
used += scnprintf(buf + used, bufsize - used,
"\n Device Name: %s\n"
" Model Name: %s\n"
" Manufacturer:%s \n"
" Model Nr: %s\n"
" Serial: %s\n"
" Pri Dev type: "
"%u %02x:%02x:%02x %u %u\n",
neighb->info->name,
neighb->info->model_name,
neighb->info->manufacturer,
neighb->info->model_nr,
neighb->info->serial,
neighb->info->prim_dev_type.category,
neighb->info->prim_dev_type.OUI[0],
neighb->info->prim_dev_type.OUI[1],
neighb->info->prim_dev_type.OUI[2],
neighb->info->prim_dev_type.OUIsubdiv,
neighb->info->prim_dev_type.subID);
list_for_each_entry(wssid_e, &neighb->wssid, node) {
used += wlp_wss_wssid_e_print(buf + used,
bufsize - used,
wssid_e);
}
buf[used++] = '\n';
wlp_remove_neighbor_tmp_info(neighb);
}
out:
mutex_unlock(&wlp->nbmutex);
return used;
}
/**
* Show properties of all WSS in neighborhood.
*
* Will trigger a complete discovery of WSS activated by this device and
* its neighbors.
*/
ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
{
wlp_discover(wlp);
return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
}
EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
static
ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
size_t bufsize)
{
ssize_t result;
result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
result += scnprintf(buf + result, bufsize - result, " ");
result += uwb_mac_addr_print(buf + result, bufsize - result,
&wss->bcast);
result += scnprintf(buf + result, bufsize - result,
" 0x%02x %u ", wss->hash, wss->secure_status);
result += wlp_wss_key_print(buf + result, bufsize - result,
wss->master_key);
result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
wss->tag);
result += uwb_mac_addr_print(buf + result, bufsize - result,
&wss->virtual_addr);
result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
result += scnprintf(buf + result, bufsize - result,
"\n\n#WSSID\n#WSS broadcast address\n"
"#WSS hash\n#WSS secure status\n"
"#WSS master key\n#WSS local tag\n"
"#WSS local virtual EUI-48\n#WSS name\n");
return result;
}
/**
* Show which WSS is activated.
*/
ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
if (wss->state >= WLP_WSS_STATE_ACTIVE)
result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
else
result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
result += scnprintf(buf + result, PAGE_SIZE - result,
"\n\n"
"# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
"NAME #create new WSS\n"
"# echo WSSID [DEV ADDR] #enroll in and activate "
"existing WSS, can request registrar\n"
"#\n"
"# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
"# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
"# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
"# NAME is the text string identifying the WSS\n"
"# DEV ADDR is the device address of neighbor "
"that should be registrar. Eg. 32:AB\n");
mutex_unlock(&wss->mutex);
out:
return result;
}
EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
/**
* Create/activate a new WSS or enroll/activate in neighboring WSS
*
* The user can provide the WSSID of a WSS in which it wants to enroll.
* Only the WSSID is necessary if the WSS have been discovered before. If
* the WSS has not been discovered before, or the user wants to use a
* particular neighbor as its registrar, then the user can also provide a
* device address or the neighbor that will be used as registrar.
*
* A new WSS is created when the user provides a WSSID, secure status, and
* WSS name.
*/
ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
const char *buf, size_t size)
{
ssize_t result = -EINVAL;
struct wlp_uuid wssid;
struct uwb_dev_addr dev;
struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
char name[65];
unsigned sec_status, accept;
memset(name, 0, sizeof(name));
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx:%02hhx",
&wssid.data[0] , &wssid.data[1],
&wssid.data[2] , &wssid.data[3],
&wssid.data[4] , &wssid.data[5],
&wssid.data[6] , &wssid.data[7],
&wssid.data[8] , &wssid.data[9],
&wssid.data[10], &wssid.data[11],
&wssid.data[12], &wssid.data[13],
&wssid.data[14], &wssid.data[15],
&dev.data[1], &dev.data[0]);
if (result == 16 || result == 17) {
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%u %u %64c",
&wssid.data[0] , &wssid.data[1],
&wssid.data[2] , &wssid.data[3],
&wssid.data[4] , &wssid.data[5],
&wssid.data[6] , &wssid.data[7],
&wssid.data[8] , &wssid.data[9],
&wssid.data[10], &wssid.data[11],
&wssid.data[12], &wssid.data[13],
&wssid.data[14], &wssid.data[15],
&sec_status, &accept, name);
if (result == 16)
result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
else if (result == 19) {
sec_status = sec_status == 0 ? 0 : 1;
accept = accept == 0 ? 0 : 1;
/* We read name using %c, so the newline needs to be
* removed */
if (strlen(name) != sizeof(name) - 1)
name[strlen(name) - 1] = '\0';
result = wlp_wss_create_activate(wss, &wssid, name,
sec_status, accept);
} else
result = -EINVAL;
} else if (result == 18)
result = wlp_wss_enroll_activate(wss, &wssid, &dev);
else
result = -EINVAL;
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
/**
* Show the UUID of this host
*/
ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
buf[result++] = '\n';
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_uuid_show);
/**
* Store a new UUID for this host
*
* According to the spec this should be encoded as an octet string in the
* order the octets are shown in string representation in RFC 4122 (WLP
* 0.99 [Table 6])
*
* We do not check value provided by user.
*/
ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
struct wlp_uuid uuid;
mutex_lock(&wlp->mutex);
result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx "
"%02hhx %02hhx %02hhx %02hhx ",
&uuid.data[0] , &uuid.data[1],
&uuid.data[2] , &uuid.data[3],
&uuid.data[4] , &uuid.data[5],
&uuid.data[6] , &uuid.data[7],
&uuid.data[8] , &uuid.data[9],
&uuid.data[10], &uuid.data[11],
&uuid.data[12], &uuid.data[13],
&uuid.data[14], &uuid.data[15]);
if (result != 16) {
result = -EINVAL;
goto error;
}
wlp->uuid = uuid;
error:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_uuid_store);
/**
* Show contents of members of device information structure
*/
#define wlp_dev_info_show(type) \
ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \
{ \
ssize_t result = 0; \
mutex_lock(&wlp->mutex); \
if (wlp->dev_info == NULL) { \
result = __wlp_setup_device_info(wlp); \
if (result < 0) \
goto out; \
} \
result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
out: \
mutex_unlock(&wlp->mutex); \
return result; \
} \
EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
wlp_dev_info_show(name)
wlp_dev_info_show(model_name)
wlp_dev_info_show(model_nr)
wlp_dev_info_show(manufacturer)
wlp_dev_info_show(serial)
/**
* Store contents of members of device information structure
*/
#define wlp_dev_info_store(type, len) \
ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
{ \
ssize_t result; \
char format[10]; \
mutex_lock(&wlp->mutex); \
if (wlp->dev_info == NULL) { \
result = __wlp_alloc_device_info(wlp); \
if (result < 0) \
goto out; \
} \
memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \
sprintf(format, "%%%uc", len); \
result = sscanf(buf, format, wlp->dev_info->type); \
out: \
mutex_unlock(&wlp->mutex); \
return result < 0 ? result : size; \
} \
EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
wlp_dev_info_store(name, 32)
wlp_dev_info_store(manufacturer, 64)
wlp_dev_info_store(model_name, 32)
wlp_dev_info_store(model_nr, 32)
wlp_dev_info_store(serial, 32)
static
const char *__wlp_dev_category[] = {
[WLP_DEV_CAT_COMPUTER] = "Computer",
[WLP_DEV_CAT_INPUT] = "Input device",
[WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
"Copier",
[WLP_DEV_CAT_CAMERA] = "Camera",
[WLP_DEV_CAT_STORAGE] = "Storage Network",
[WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
[WLP_DEV_CAT_DISPLAY] = "Display",
[WLP_DEV_CAT_MULTIM] = "Multimedia device",
[WLP_DEV_CAT_GAMING] = "Gaming device",
[WLP_DEV_CAT_TELEPHONE] = "Telephone",
[WLP_DEV_CAT_OTHER] = "Other",
};
static
const char *wlp_dev_category_str(unsigned cat)
{
if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
|| cat == WLP_DEV_CAT_OTHER)
return __wlp_dev_category[cat];
return "unknown category";
}
ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%s\n",
wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
u16 cat;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%hu", &cat);
if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
|| cat == WLP_DEV_CAT_OTHER)
wlp->dev_info->prim_dev_type.category = cat;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
wlp->dev_info->prim_dev_type.OUI[0],
wlp->dev_info->prim_dev_type.OUI[1],
wlp->dev_info->prim_dev_type.OUI[2]);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
{
ssize_t result;
u8 OUI[3];
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%hhx:%hhx:%hhx",
&OUI[0], &OUI[1], &OUI[2]);
if (result != 3) {
result = -EINVAL;
goto out;
} else
memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%u\n",
wlp->dev_info->prim_dev_type.OUIsubdiv);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
unsigned sub;
u8 max_sub = ~0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%u", &sub);
if (sub <= max_sub)
wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
{
ssize_t result = 0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_setup_device_info(wlp);
if (result < 0)
goto out;
}
result = scnprintf(buf, PAGE_SIZE, "%u\n",
wlp->dev_info->prim_dev_type.subID);
out:
mutex_unlock(&wlp->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
size_t size)
{
ssize_t result;
unsigned sub;
__le16 max_sub = ~0;
mutex_lock(&wlp->mutex);
if (wlp->dev_info == NULL) {
result = __wlp_alloc_device_info(wlp);
if (result < 0)
goto out;
}
result = sscanf(buf, "%u", &sub);
if (sub <= max_sub)
wlp->dev_info->prim_dev_type.subID = sub;
else
result = -EINVAL;
out:
mutex_unlock(&wlp->mutex);
return result < 0 ? result : size;
}
EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
/**
* Subsystem implementation for interaction with individual WSS via sysfs
*
* Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
*/
#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
#define attr_to_wlp_wss_attr(_attr) \
container_of(_attr, struct wlp_wss_attribute, attr)
/**
* Sysfs subsystem: forward read calls
*
* Sysfs operation for forwarding read call to the show method of the
* attribute owner
*/
static
ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
ssize_t ret = -EIO;
if (wss_attr->show)
ret = wss_attr->show(wss, buf);
return ret;
}
/**
* Sysfs subsystem: forward write calls
*
* Sysfs operation for forwarding write call to the store method of the
* attribute owner
*/
static
ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
ssize_t ret = -EIO;
if (wss_attr->store)
ret = wss_attr->store(wss, buf, count);
return ret;
}
static const struct sysfs_ops wss_sysfs_ops = {
.show = wlp_wss_attr_show,
.store = wlp_wss_attr_store,
};
struct kobj_type wss_ktype = {
.release = wlp_wss_release,
.sysfs_ops = &wss_sysfs_ops,
};
/**
* Sysfs files for individual WSS
*/
/**
* Print static properties of this WSS
*
* The name of a WSS may not be null teminated. It's max size is 64 bytes
* so we copy it to a larger array just to make sure we print sane data.
*/
static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
mutex_unlock(&wss->mutex);
out:
return result;
}
WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
/**
* Print all connected members of this WSS
* The EDA cache contains all members of WSS neighborhood.
*/
static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
return wlp_eda_show(wlp, buf);
}
WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
static
const char *__wlp_strstate[] = {
"none",
"partially enrolled",
"enrolled",
"active",
"connected",
};
static const char *wlp_wss_strstate(unsigned state)
{
if (state >= ARRAY_SIZE(__wlp_strstate))
return "unknown state";
return __wlp_strstate[state];
}
/*
* Print current state of this WSS
*/
static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
{
int result = 0;
if (mutex_lock_interruptible(&wss->mutex))
goto out;
result = scnprintf(buf, PAGE_SIZE, "%s\n",
wlp_wss_strstate(wss->state));
mutex_unlock(&wss->mutex);
out:
return result;
}
WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
static
struct attribute *wss_attrs[] = {
&wss_attr_properties.attr,
&wss_attr_members.attr,
&wss_attr_state.attr,
NULL,
};
struct attribute_group wss_attr_group = {
.name = NULL, /* we want them in the same directory */
.attrs = wss_attrs,
};

View file

@ -1,354 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* Message exchange infrastructure
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*
*/
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
/*
* Direct incoming association msg to correct parsing routine
*
* We only expect D1, E1, C1, C3 messages as new. All other incoming
* association messages should form part of an established session that is
* handled elsewhere.
* The handling of these messages often require calling sleeping functions
* - this cannot be done in interrupt context. We use the kernel's
* workqueue to handle these messages.
*/
static
void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_frame_assoc *assoc = (void *) skb->data;
struct wlp_assoc_frame_ctx *frame_ctx;
frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC);
if (frame_ctx == NULL) {
dev_err(dev, "WLP: Unable to allocate memory for association "
"frame handling.\n");
kfree_skb(skb);
return;
}
frame_ctx->wlp = wlp;
frame_ctx->skb = skb;
frame_ctx->src = *src;
switch (assoc->type) {
case WLP_ASSOC_D1:
INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame);
schedule_work(&frame_ctx->ws);
break;
case WLP_ASSOC_E1:
kfree_skb(skb); /* Temporary until we handle it */
kfree(frame_ctx); /* Temporary until we handle it */
break;
case WLP_ASSOC_C1:
INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame);
schedule_work(&frame_ctx->ws);
break;
case WLP_ASSOC_C3:
INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame);
schedule_work(&frame_ctx->ws);
break;
default:
dev_err(dev, "Received unexpected association frame. "
"Type = %d \n", assoc->type);
kfree_skb(skb);
kfree(frame_ctx);
break;
}
}
/*
* Process incoming association frame
*
* Although it could be possible to deal with some incoming association
* messages without creating a new session we are keeping things simple. We
* do not accept new association messages if there is a session in progress
* and the messages do not belong to that session.
*
* If an association message arrives that causes the creation of a session
* (WLP_ASSOC_E1) while we are in the process of creating a session then we
* rely on the neighbor mutex to protect the data. That is, the new session
* will not be started until the previous is completed.
*/
static
void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_frame_assoc *assoc = (void *) skb->data;
struct wlp_session *session = wlp->session;
u8 version;
if (wlp_get_version(wlp, &assoc->version, &version,
sizeof(assoc->version)) < 0)
goto error;
if (version != WLP_VERSION) {
dev_err(dev, "Unsupported WLP version in association "
"message.\n");
goto error;
}
if (session != NULL) {
/* Function that created this session is still holding the
* &wlp->mutex to protect this session. */
if (assoc->type == session->exp_message ||
assoc->type == WLP_ASSOC_F0) {
if (!memcmp(&session->neighbor_addr, src,
sizeof(*src))) {
session->data = skb;
(session->cb)(wlp);
} else {
dev_err(dev, "Received expected message from "
"unexpected source. Expected message "
"%d or F0 from %02x:%02x, but received "
"it from %02x:%02x. Dropping.\n",
session->exp_message,
session->neighbor_addr.data[1],
session->neighbor_addr.data[0],
src->data[1], src->data[0]);
goto error;
}
} else {
dev_err(dev, "Association already in progress. "
"Dropping.\n");
goto error;
}
} else {
wlp_direct_assoc_frame(wlp, skb, src);
}
return;
error:
kfree_skb(skb);
}
/*
* Verify incoming frame is from connected neighbor, prep to pass to WLP client
*
* Verification proceeds according to WLP 0.99 [7.3.1]. The source address
* is used to determine which neighbor is sending the frame and the WSS tag
* is used to know to which WSS the frame belongs (we only support one WSS
* so this test is straight forward).
* With the WSS found we need to ensure that we are connected before
* allowing the exchange of data frames.
*/
static
int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = -EINVAL;
struct wlp_eda_node eda_entry;
struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data;
/*verify*/
result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Incoming frame is from unknown "
"neighbor %02x:%02x.\n", src->data[1],
src->data[0]);
goto out;
}
if (hdr->tag != eda_entry.tag) {
if (printk_ratelimit())
dev_err(dev, "WLP: Tag of incoming frame from "
"%02x:%02x does not match expected tag. "
"Received 0x%02x, expected 0x%02x. \n",
src->data[1], src->data[0], hdr->tag,
eda_entry.tag);
result = -EINVAL;
goto out;
}
if (eda_entry.state != WLP_WSS_CONNECTED) {
if (printk_ratelimit())
dev_err(dev, "WLP: Incoming frame from "
"%02x:%02x does is not from connected WSS.\n",
src->data[1], src->data[0]);
result = -EINVAL;
goto out;
}
/*prep*/
skb_pull(skb, sizeof(*hdr));
out:
return result;
}
/*
* Receive a WLP frame from device
*
* @returns: 1 if calling function should free the skb
* 0 if it successfully handled skb and freed it
* 0 if error occured, will free skb in this case
*/
int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb,
struct uwb_dev_addr *src)
{
unsigned len = skb->len;
void *ptr = skb->data;
struct wlp_frame_hdr *hdr;
int result = 0;
if (len < sizeof(*hdr)) {
dev_err(dev, "Not enough data to parse WLP header.\n");
result = -EINVAL;
goto out;
}
hdr = ptr;
if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) {
dev_err(dev, "Not a WLP frame type.\n");
result = -EINVAL;
goto out;
}
switch (hdr->type) {
case WLP_FRAME_STANDARD:
if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) {
dev_err(dev, "Not enough data to parse Standard "
"WLP header.\n");
goto out;
}
result = wlp_verify_prep_rx_frame(wlp, skb, src);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Verification of frame "
"from neighbor %02x:%02x failed.\n",
src->data[1], src->data[0]);
goto out;
}
result = 1;
break;
case WLP_FRAME_ABBREVIATED:
dev_err(dev, "Abbreviated frame received. FIXME?\n");
kfree_skb(skb);
break;
case WLP_FRAME_CONTROL:
dev_err(dev, "Control frame received. FIXME?\n");
kfree_skb(skb);
break;
case WLP_FRAME_ASSOCIATION:
if (len < sizeof(struct wlp_frame_assoc)) {
dev_err(dev, "Not enough data to parse Association "
"WLP header.\n");
goto out;
}
wlp_receive_assoc_frame(wlp, skb, src);
break;
default:
dev_err(dev, "Invalid frame received.\n");
result = -EINVAL;
break;
}
out:
if (result < 0) {
kfree_skb(skb);
result = 0;
}
return result;
}
EXPORT_SYMBOL_GPL(wlp_receive_frame);
/*
* Verify frame from network stack, prepare for further transmission
*
* @skb: the socket buffer that needs to be prepared for transmission (it
* is in need of a WLP header). If this is a broadcast frame we take
* over the entire transmission.
* If it is a unicast the WSS connection should already be established
* and transmission will be done by the calling function.
* @dst: On return this will contain the device address to which the
* frame is destined.
* @returns: 0 on success no tx : WLP header successfully applied to skb buffer,
* calling function can proceed with tx
* 1 on success with tx : WLP will take over transmission of this
* frame
* <0 on error
*
* The network stack (WLP client) is attempting to transmit a frame. We can
* only transmit data if a local WSS is at least active (connection will be
* done here if this is a broadcast frame and neighbor also has the WSS
* active).
*
* The frame can be either broadcast or unicast. Broadcast in a WSS is
* supported via multicast, but we don't support multicast yet (until
* devices start to support MAB IEs). If a broadcast frame needs to be
* transmitted it is treated as a unicast frame to each neighbor. In this
* case the WLP takes over transmission of the skb and returns 1
* to the caller to indicate so. Also, in this case, if a neighbor has the
* same WSS activated but is not connected then the WSS connection will be
* done at this time. The neighbor's virtual address will be learned at
* this time.
*
* The destination address in a unicast frame is the virtual address of the
* neighbor. This address only becomes known when a WSS connection is
* established. We thus rely on a broadcast frame to trigger the setup of
* WSS connections to all neighbors before we are able to send unicast
* frames to them. This seems reasonable as IP would usually use ARP first
* before any unicast frames are sent.
*
* If we are already connected to the neighbor (neighbor's virtual address
* is known) we just prepare the WLP header and the caller will continue to
* send the frame.
*
* A failure in this function usually indicates something that cannot be
* fixed automatically. So, if this function fails (@return < 0) the calling
* function should not retry to send the frame as it will very likely keep
* failing.
*
*/
int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp,
struct sk_buff *skb, struct uwb_dev_addr *dst)
{
int result = -EINVAL;
struct ethhdr *eth_hdr = (void *) skb->data;
if (is_multicast_ether_addr(eth_hdr->h_dest)) {
result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "Unable to handle broadcast "
"frame from WLP client.\n");
goto out;
}
dev_kfree_skb_irq(skb);
result = 1;
/* Frame will be transmitted by WLP. */
} else {
result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst,
wlp_wss_prep_hdr, skb);
if (unlikely(result < 0)) {
if (printk_ratelimit())
dev_err(dev, "Unable to prepare "
"skb for transmission. \n");
goto out;
}
}
out:
return result;
}
EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame);

View file

@ -1,224 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
* Internal API
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*/
#ifndef __WLP_INTERNAL_H__
#define __WLP_INTERNAL_H__
/**
* State of WSS connection
*
* A device needs to connect to a neighbor in an activated WSS before data
* can be transmitted. The spec also distinguishes between a new connection
* attempt and a connection attempt after previous connection attempts. The
* state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99
* [7.2.6]
*/
enum wlp_wss_connect {
WLP_WSS_UNCONNECTED = 0,
WLP_WSS_CONNECTED,
WLP_WSS_CONNECT_FAILED,
};
extern struct kobj_type wss_ktype;
extern struct attribute_group wss_attr_group;
/* This should be changed to a dynamic array where entries are sorted
* by eth_addr and search is done in a binary form
*
* Although thinking twice about it: this technologie's maximum reach
* is 10 meters...unless you want to pack too much stuff in around
* your radio controller/WLP device, the list will probably not be
* too big.
*
* In any case, there is probably some data structure in the kernel
* than we could reused for that already.
*
* The below structure is really just good while we support one WSS per
* host.
*/
struct wlp_eda_node {
struct list_head list_node;
unsigned char eth_addr[ETH_ALEN];
struct uwb_dev_addr dev_addr;
struct wlp_wss *wss;
unsigned char virt_addr[ETH_ALEN];
u8 tag;
enum wlp_wss_connect state;
};
typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *);
extern void wlp_eda_init(struct wlp_eda *);
extern void wlp_eda_release(struct wlp_eda *);
extern int wlp_eda_create_node(struct wlp_eda *,
const unsigned char eth_addr[ETH_ALEN],
const struct uwb_dev_addr *);
extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *);
extern int wlp_eda_update_node(struct wlp_eda *,
const struct uwb_dev_addr *,
struct wlp_wss *,
const unsigned char virt_addr[ETH_ALEN],
const u8, const enum wlp_wss_connect);
extern int wlp_eda_update_node_state(struct wlp_eda *,
const struct uwb_dev_addr *,
const enum wlp_wss_connect);
extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *,
struct wlp_eda_node *);
extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *);
extern int wlp_eda_for_virtual(struct wlp_eda *,
const unsigned char eth_addr[ETH_ALEN],
struct uwb_dev_addr *,
wlp_eda_for_each_f , void *);
extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *);
extern size_t wlp_wss_key_print(char *, size_t, u8 *);
/* Function called when no more references to WSS exists */
extern void wlp_wss_release(struct kobject *);
extern void wlp_wss_reset(struct wlp_wss *);
extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *,
char *, unsigned, unsigned);
extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *,
struct uwb_dev_addr *);
extern ssize_t wlp_discover(struct wlp *);
extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *,
struct wlp_wss *, struct wlp_uuid *);
extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *,
struct uwb_dev_addr *);
struct wlp_assoc_conn_ctx {
struct work_struct ws;
struct wlp *wlp;
struct sk_buff *skb;
struct wlp_eda_node eda_entry;
};
extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *);
extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *);
/* Message handling */
struct wlp_assoc_frame_ctx {
struct work_struct ws;
struct wlp *wlp;
struct sk_buff *skb;
struct uwb_dev_addr src;
};
extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *);
extern void wlp_handle_d1_frame(struct work_struct *);
extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *,
struct wlp_neighbor_e *);
extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *,
struct wlp_neighbor_e *,
struct wlp_uuid *);
extern void wlp_handle_c1_frame(struct work_struct *);
extern void wlp_handle_c3_frame(struct work_struct *);
extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *,
struct wlp_uuid *, u8 *,
struct uwb_mac_addr *);
extern int wlp_parse_f0(struct wlp *, struct sk_buff *);
extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *,
struct uwb_dev_addr *, enum wlp_assoc_type);
extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *,
u8 *, ssize_t);
extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *,
struct wlp_uuid *, ssize_t);
extern int __wlp_alloc_device_info(struct wlp *);
extern int __wlp_setup_device_info(struct wlp *);
extern struct wlp_wss_attribute wss_attribute_properties;
extern struct wlp_wss_attribute wss_attribute_members;
extern struct wlp_wss_attribute wss_attribute_state;
static inline
size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x",
uuid->data[0], uuid->data[1],
uuid->data[2], uuid->data[3],
uuid->data[4], uuid->data[5],
uuid->data[6], uuid->data[7],
uuid->data[8], uuid->data[9],
uuid->data[10], uuid->data[11],
uuid->data[12], uuid->data[13],
uuid->data[14], uuid->data[15]);
return result;
}
/**
* FIXME: How should a nonce be displayed?
*/
static inline
size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x",
nonce->data[0], nonce->data[1],
nonce->data[2], nonce->data[3],
nonce->data[4], nonce->data[5],
nonce->data[6], nonce->data[7],
nonce->data[8], nonce->data[9],
nonce->data[10], nonce->data[11],
nonce->data[12], nonce->data[13],
nonce->data[14], nonce->data[15]);
return result;
}
static inline
void wlp_session_cb(struct wlp *wlp)
{
struct completion *completion = wlp->session->cb_priv;
complete(completion);
}
static inline
int wlp_uuid_is_set(struct wlp_uuid *uuid)
{
struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00} };
if (!memcmp(uuid, &zero_uuid, sizeof(*uuid)))
return 0;
return 1;
}
#endif /* __WLP_INTERNAL_H__ */

View file

@ -1,560 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2005-2006 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*/
#include <linux/wlp.h>
#include <linux/slab.h>
#include "wlp-internal.h"
static
void wlp_neighbor_init(struct wlp_neighbor_e *neighbor)
{
INIT_LIST_HEAD(&neighbor->wssid);
}
/**
* Create area for device information storage
*
* wlp->mutex must be held
*/
int __wlp_alloc_device_info(struct wlp *wlp)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
BUG_ON(wlp->dev_info != NULL);
wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
if (wlp->dev_info == NULL) {
dev_err(dev, "WLP: Unable to allocate memory for "
"device information.\n");
return -ENOMEM;
}
return 0;
}
/**
* Fill in device information using function provided by driver
*
* wlp->mutex must be held
*/
static
void __wlp_fill_device_info(struct wlp *wlp)
{
wlp->fill_device_info(wlp, wlp->dev_info);
}
/**
* Setup device information
*
* Allocate area for device information and populate it.
*
* wlp->mutex must be held
*/
int __wlp_setup_device_info(struct wlp *wlp)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
result = __wlp_alloc_device_info(wlp);
if (result < 0) {
dev_err(dev, "WLP: Unable to allocate area for "
"device information.\n");
return result;
}
__wlp_fill_device_info(wlp);
return 0;
}
/**
* Remove information about neighbor stored temporarily
*
* Information learned during discovey should only be stored when the
* device enrolls in the neighbor's WSS. We do need to store this
* information temporarily in order to present it to the user.
*
* We are only interested in keeping neighbor WSS information if that
* neighbor is accepting enrollment.
*
* should be called with wlp->nbmutex held
*/
void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor)
{
struct wlp_wssid_e *wssid_e, *next;
u8 keep;
if (!list_empty(&neighbor->wssid)) {
list_for_each_entry_safe(wssid_e, next, &neighbor->wssid,
node) {
if (wssid_e->info != NULL) {
keep = wssid_e->info->accept_enroll;
kfree(wssid_e->info);
wssid_e->info = NULL;
if (!keep) {
list_del(&wssid_e->node);
kfree(wssid_e);
}
}
}
}
if (neighbor->info != NULL) {
kfree(neighbor->info);
neighbor->info = NULL;
}
}
/*
* Populate WLP neighborhood cache with neighbor information
*
* A new neighbor is found. If it is discoverable then we add it to the
* neighborhood cache.
*
*/
static
int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev)
{
int result = 0;
int discoverable;
struct wlp_neighbor_e *neighbor;
/*
* FIXME:
* Use contents of WLP IE found in beacon cache to determine if
* neighbor is discoverable.
* The device does not support WLP IE yet so this still needs to be
* done. Until then we assume all devices are discoverable.
*/
discoverable = 1; /* will be changed when FIXME disappears */
if (discoverable) {
/* Add neighbor to cache for discovery */
neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL);
if (neighbor == NULL) {
dev_err(&dev->dev, "Unable to create memory for "
"new neighbor. \n");
result = -ENOMEM;
goto error_no_mem;
}
wlp_neighbor_init(neighbor);
uwb_dev_get(dev);
neighbor->uwb_dev = dev;
list_add(&neighbor->node, &wlp->neighbors);
}
error_no_mem:
return result;
}
/**
* Remove one neighbor from cache
*/
static
void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor)
{
struct wlp_wssid_e *wssid_e, *next_wssid_e;
list_for_each_entry_safe(wssid_e, next_wssid_e,
&neighbor->wssid, node) {
list_del(&wssid_e->node);
kfree(wssid_e);
}
uwb_dev_put(neighbor->uwb_dev);
list_del(&neighbor->node);
kfree(neighbor);
}
/**
* Clear entire neighborhood cache.
*/
static
void __wlp_neighbors_release(struct wlp *wlp)
{
struct wlp_neighbor_e *neighbor, *next;
if (list_empty(&wlp->neighbors))
return;
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
__wlp_neighbor_release(neighbor);
}
}
static
void wlp_neighbors_release(struct wlp *wlp)
{
mutex_lock(&wlp->nbmutex);
__wlp_neighbors_release(wlp);
mutex_unlock(&wlp->nbmutex);
}
/**
* Send D1 message to neighbor, receive D2 message
*
* @neighbor: neighbor to which D1 message will be sent
* @wss: if not NULL, it is an enrollment request for this WSS
* @wssid: if wss not NULL, this is the wssid of the WSS in which we
* want to enroll
*
* A D1/D2 exchange is done for one of two reasons: discovery or
* enrollment. If done for discovery the D1 message is sent to the neighbor
* and the contents of the D2 response is stored in a temporary cache.
* If done for enrollment the @wss and @wssid are provided also. In this
* case the D1 message is sent to the neighbor, the D2 response is parsed
* for enrollment of the WSS with wssid.
*
* &wss->mutex is held
*/
static
int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
struct wlp_wss *wss, struct wlp_uuid *wssid)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct sk_buff *skb;
struct wlp_frame_assoc *resp;
struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
mutex_lock(&wlp->mutex);
if (!wlp_uuid_is_set(&wlp->uuid)) {
dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
"proceed.\n");
result = -ENXIO;
goto out;
}
/* Send D1 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1);
if (result < 0) {
dev_err(dev, "Unable to send D1 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_D2;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for D2/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
result = -ETIMEDOUT;
dev_err(dev, "Timeout while sending D1 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto error_session;
}
if (result < 0) {
dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_session;
}
/* Parse message in session->data: it will be either D2 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: Unable to parse F0 from neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
result = -EINVAL;
goto error_resp_parse;
}
if (wss == NULL) {
/* Discovery */
result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse D2 message from "
"neighbor %02x:%02x for discovery.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_resp_parse;
}
} else {
/* Enrollment */
result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor,
wssid);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse D2 message from "
"neighbor %02x:%02x for enrollment.\n",
dev_addr->data[1], dev_addr->data[0]);
goto error_resp_parse;
}
}
error_resp_parse:
kfree_skb(skb);
error_session:
wlp->session = NULL;
out:
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Enroll into WSS of provided WSSID by using neighbor as registrar
*
* &wss->mutex is held
*/
int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
struct wlp_wss *wss, struct wlp_uuid *wssid)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
char buf[WLP_WSS_UUID_STRSIZE];
struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid);
if (result < 0) {
dev_err(dev, "WLP: D1/D2 message exchange for enrollment "
"failed. result = %d \n", result);
goto out;
}
if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
dev_err(dev, "WLP: Unable to enroll into WSS %s using "
"neighbor %02x:%02x. \n", buf,
dev_addr->data[1], dev_addr->data[0]);
result = -EINVAL;
goto out;
}
if (wss->secure_status == WLP_WSS_SECURE) {
dev_err(dev, "FIXME: need to complete secure enrollment.\n");
result = -EINVAL;
goto error;
} else {
wss->state = WLP_WSS_STATE_ENROLLED;
dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS "
"%s using neighbor %02x:%02x. \n",
buf, dev_addr->data[1], dev_addr->data[0]);
}
out:
return result;
error:
wlp_wss_reset(wss);
return result;
}
/**
* Discover WSS information of neighbor's active WSS
*/
static
int wlp_discover_neighbor(struct wlp *wlp,
struct wlp_neighbor_e *neighbor)
{
return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL);
}
/**
* Each neighbor in the neighborhood cache is discoverable. Discover it.
*
* Discovery is done through sending of D1 association frame and parsing
* the D2 association frame response. Only wssid from D2 will be included
* in neighbor cache, rest is just displayed to user and forgotten.
*
* The discovery is not done in parallel. This is simple and enables us to
* maintain only one association context.
*
* The discovery of one neighbor does not affect the other, but if the
* discovery of a neighbor fails it is removed from the neighborhood cache.
*/
static
int wlp_discover_all_neighbors(struct wlp *wlp)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor, *next;
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
result = wlp_discover_neighbor(wlp, neighbor);
if (result < 0) {
dev_err(dev, "WLP: Unable to discover neighbor "
"%02x:%02x, removing from neighborhood. \n",
neighbor->uwb_dev->dev_addr.data[1],
neighbor->uwb_dev->dev_addr.data[0]);
__wlp_neighbor_release(neighbor);
}
}
return result;
}
static int wlp_add_neighbor_helper(struct device *dev, void *priv)
{
struct wlp *wlp = priv;
struct uwb_dev *uwb_dev = to_uwb_dev(dev);
return wlp_add_neighbor(wlp, uwb_dev);
}
/**
* Discover WLP neighborhood
*
* Will send D1 association frame to all devices in beacon group that have
* discoverable bit set in WLP IE. D2 frames will be received, information
* displayed to user in @buf. Partial information (from D2 association
* frame) will be cached to assist with future association
* requests.
*
* The discovery of the WLP neighborhood is triggered by the user. This
* should occur infrequently and we thus free current cache and re-allocate
* memory if needed.
*
* If one neighbor fails during initial discovery (determining if it is a
* neighbor or not), we fail all - note that interaction with neighbor has
* not occured at this point so if a failure occurs we know something went wrong
* locally. We thus undo everything.
*/
ssize_t wlp_discover(struct wlp *wlp)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
mutex_lock(&wlp->nbmutex);
/* Clear current neighborhood cache. */
__wlp_neighbors_release(wlp);
/* Determine which devices in neighborhood. Repopulate cache. */
result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp);
if (result < 0) {
/* May have partial neighbor information, release all. */
__wlp_neighbors_release(wlp);
goto error_dev_for_each;
}
/* Discover the properties of devices in neighborhood. */
result = wlp_discover_all_neighbors(wlp);
/* In case of failure we still print our partial results. */
if (result < 0) {
dev_err(dev, "Unable to fully discover neighborhood. \n");
result = 0;
}
error_dev_for_each:
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Handle events from UWB stack
*
* We handle events conservatively. If a neighbor goes off the air we
* remove it from the neighborhood. If an association process is in
* progress this function will block waiting for the nbmutex to become
* free. The association process will thus be allowed to complete before it
* is removed.
*/
static
void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev,
enum uwb_notifs event)
{
struct wlp *wlp = _wlp;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor, *next;
int result;
switch (event) {
case UWB_NOTIF_ONAIR:
result = wlp_eda_create_node(&wlp->eda,
uwb_dev->mac_addr.data,
&uwb_dev->dev_addr);
if (result < 0)
dev_err(dev, "WLP: Unable to add new neighbor "
"%02x:%02x to EDA cache.\n",
uwb_dev->dev_addr.data[1],
uwb_dev->dev_addr.data[0]);
break;
case UWB_NOTIF_OFFAIR:
wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr);
mutex_lock(&wlp->nbmutex);
list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
if (neighbor->uwb_dev == uwb_dev)
__wlp_neighbor_release(neighbor);
}
mutex_unlock(&wlp->nbmutex);
break;
default:
dev_err(dev, "don't know how to handle event %d from uwb\n",
event);
}
}
static void wlp_channel_changed(struct uwb_pal *pal, int channel)
{
struct wlp *wlp = container_of(pal, struct wlp, pal);
if (channel < 0)
netif_carrier_off(wlp->ndev);
else
netif_carrier_on(wlp->ndev);
}
int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev)
{
int result;
BUG_ON(wlp->fill_device_info == NULL);
BUG_ON(wlp->xmit_frame == NULL);
BUG_ON(wlp->stop_queue == NULL);
BUG_ON(wlp->start_queue == NULL);
wlp->rc = rc;
wlp->ndev = ndev;
wlp_eda_init(&wlp->eda);/* Set up address cache */
wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb;
wlp->uwb_notifs_handler.data = wlp;
uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
uwb_pal_init(&wlp->pal);
wlp->pal.rc = rc;
wlp->pal.channel_changed = wlp_channel_changed;
result = uwb_pal_register(&wlp->pal);
if (result < 0)
uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
return result;
}
EXPORT_SYMBOL_GPL(wlp_setup);
void wlp_remove(struct wlp *wlp)
{
wlp_neighbors_release(wlp);
uwb_pal_unregister(&wlp->pal);
uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
wlp_eda_release(&wlp->eda);
mutex_lock(&wlp->mutex);
if (wlp->dev_info != NULL)
kfree(wlp->dev_info);
mutex_unlock(&wlp->mutex);
wlp->rc = NULL;
}
EXPORT_SYMBOL_GPL(wlp_remove);
/**
* wlp_reset_all - reset the WLP hardware
* @wlp: the WLP device to reset.
*
* This schedules a full hardware reset of the WLP device. The radio
* controller and any other PALs will also be reset.
*/
void wlp_reset_all(struct wlp *wlp)
{
uwb_rc_reset_all(wlp->rc);
}
EXPORT_SYMBOL_GPL(wlp_reset_all);

View file

@ -1,959 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2007 Intel Corporation
* Reinette Chatre <reinette.chatre@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.
*
*
* Implementation of the WLP association protocol.
*
* FIXME: Docs
*
* A UWB network interface will configure a WSS through wlp_wss_setup() after
* the interface has been assigned a MAC address, typically after
* "ifconfig" has been called. When the interface goes down it should call
* wlp_wss_remove().
*
* When the WSS is ready for use the user interacts via sysfs to create,
* discover, and activate WSS.
*
* wlp_wss_enroll_activate()
*
* wlp_wss_create_activate()
* wlp_wss_set_wssid_hash()
* wlp_wss_comp_wssid_hash()
* wlp_wss_sel_bcast_addr()
* wlp_wss_sysfs_add()
*
* Called when no more references to WSS exist:
* wlp_wss_release()
* wlp_wss_reset()
*/
#include <linux/etherdevice.h> /* for is_valid_ether_addr */
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/wlp.h>
#include "wlp-internal.h"
size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key)
{
size_t result;
result = scnprintf(buf, bufsize,
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x",
key[0], key[1], key[2], key[3],
key[4], key[5], key[6], key[7],
key[8], key[9], key[10], key[11],
key[12], key[13], key[14], key[15]);
return result;
}
/**
* Compute WSSID hash
* WLP Draft 0.99 [7.2.1]
*
* The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR
* of all octets in the WSSID.
*/
static
u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid)
{
return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2]
^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5]
^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8]
^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11]
^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14]
^ wssid->data[15];
}
/**
* Select a multicast EUI-48 for the WSS broadcast address.
* WLP Draft 0.99 [7.2.1]
*
* Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP
* range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive.
*
* This address is currently hardcoded.
* FIXME?
*/
static
struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss)
{
struct uwb_mac_addr bcast = {
.data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 }
};
return bcast;
}
/**
* Clear the contents of the WSS structure - all except kobj, mutex, virtual
*
* We do not want to reinitialize - the internal kobj should not change as
* it still points to the parent received during setup. The mutex should
* remain also. We thus just reset values individually.
* The virutal address assigned to WSS will remain the same for the
* lifetime of the WSS. We only reset the fields that can change during its
* lifetime.
*/
void wlp_wss_reset(struct wlp_wss *wss)
{
memset(&wss->wssid, 0, sizeof(wss->wssid));
wss->hash = 0;
memset(&wss->name[0], 0, sizeof(wss->name));
memset(&wss->bcast, 0, sizeof(wss->bcast));
wss->secure_status = WLP_WSS_UNSECURE;
memset(&wss->master_key[0], 0, sizeof(wss->master_key));
wss->tag = 0;
wss->state = WLP_WSS_STATE_NONE;
}
/**
* Create sysfs infrastructure for WSS
*
* The WSS is configured to have the interface as parent (see wlp_wss_setup())
* a new sysfs directory that includes wssid as its name is created in the
* interface's sysfs directory. The group of files interacting with WSS are
* created also.
*/
static
int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result;
result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str);
if (result < 0)
return result;
wss->kobj.ktype = &wss_ktype;
result = kobject_init_and_add(&wss->kobj,
&wss_ktype, wss->kobj.parent, "wlp");
if (result < 0) {
dev_err(dev, "WLP: Cannot register WSS kobject.\n");
goto error_kobject_register;
}
result = sysfs_create_group(&wss->kobj, &wss_attr_group);
if (result < 0) {
dev_err(dev, "WLP: Cannot register WSS attributes: %d\n",
result);
goto error_sysfs_create_group;
}
return 0;
error_sysfs_create_group:
kobject_put(&wss->kobj); /* will free name if needed */
return result;
error_kobject_register:
kfree(wss->kobj.name);
wss->kobj.name = NULL;
wss->kobj.ktype = NULL;
return result;
}
/**
* Release WSS
*
* No more references exist to this WSS. We should undo everything that was
* done in wlp_wss_create_activate() except removing the group. The group
* is not removed because an object can be unregistered before the group is
* created. We also undo any additional operations on the WSS after this
* (addition of members).
*
* If memory was allocated for the kobject's name then it will
* be freed by the kobject system during this time.
*
* The EDA cache is removed and reinitialized when the WSS is removed. We
* thus loose knowledge of members of this WSS at that time and need not do
* it here.
*/
void wlp_wss_release(struct kobject *kobj)
{
struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj);
wlp_wss_reset(wss);
}
/**
* Enroll into a WSS using provided neighbor as registrar
*
* First search the neighborhood information to learn which neighbor is
* referred to, next proceed with enrollment.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *dest)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor;
int result = -ENXIO;
struct uwb_dev_addr *dev_addr;
mutex_lock(&wlp->nbmutex);
list_for_each_entry(neighbor, &wlp->neighbors, node) {
dev_addr = &neighbor->uwb_dev->dev_addr;
if (!memcmp(dest, dev_addr, sizeof(*dest))) {
result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid);
break;
}
}
if (result == -ENXIO)
dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n",
dest->data[1], dest->data[0]);
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Enroll into a WSS previously discovered
*
* User provides WSSID of WSS, search for neighbor that has this WSS
* activated and attempt to enroll.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_neighbor_e *neighbor;
struct wlp_wssid_e *wssid_e;
char buf[WLP_WSS_UUID_STRSIZE];
int result = -ENXIO;
mutex_lock(&wlp->nbmutex);
list_for_each_entry(neighbor, &wlp->neighbors, node) {
list_for_each_entry(wssid_e, &neighbor->wssid, node) {
if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) {
result = wlp_enroll_neighbor(wlp, neighbor,
wss, wssid);
if (result == 0) /* enrollment success */
goto out;
break;
}
}
}
out:
if (result == -ENXIO) {
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf);
}
mutex_unlock(&wlp->nbmutex);
return result;
}
/**
* Enroll into WSS with provided WSSID, registrar may be provided
*
* @wss: out WSS that will be enrolled
* @wssid: wssid of neighboring WSS that we want to enroll in
* @devaddr: registrar can be specified, will be broadcast (ff:ff) if any
* neighbor can be used as registrar.
*
* &wss->mutex is held
*/
static
int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *devaddr)
{
int result;
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
char buf[WLP_WSS_UUID_STRSIZE];
struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
wlp_wss_uuid_print(buf, sizeof(buf), wssid);
if (wss->state != WLP_WSS_STATE_NONE) {
dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf);
result = -EEXIST;
goto error;
}
if (!memcmp(&bcast, devaddr, sizeof(bcast)))
result = wlp_wss_enroll_discovered(wss, wssid);
else
result = wlp_wss_enroll_target(wss, wssid, devaddr);
if (result < 0) {
dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n",
buf, result);
goto error;
}
dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf);
result = wlp_wss_sysfs_add(wss, buf);
if (result < 0) {
dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n");
wlp_wss_reset(wss);
}
error:
return result;
}
/**
* Activate given WSS
*
* Prior to activation a WSS must be enrolled. To activate a WSS a device
* includes the WSS hash in the WLP IE in its beacon in each superframe.
* WLP 0.99 [7.2.5].
*
* The WSS tag is also computed at this time. We only support one activated
* WSS so we can use the hash as a tag - there will never be a conflict.
*
* We currently only support one activated WSS so only one WSS hash is
* included in the WLP IE.
*/
static
int wlp_wss_activate(struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
struct uwb_rc *uwb_rc = wlp->rc;
int result;
struct {
struct wlp_ie wlp_ie;
u8 hash; /* only include one hash */
} ie_data;
BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED);
wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid);
wss->tag = wss->hash;
memset(&ie_data, 0, sizeof(ie_data));
ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP;
ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr);
wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash));
ie_data.hash = wss->hash;
result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr,
sizeof(ie_data));
if (result < 0) {
dev_err(dev, "WLP: Unable to add WLP IE to beacon. "
"result = %d.\n", result);
goto error_wlp_ie;
}
wss->state = WLP_WSS_STATE_ACTIVE;
result = 0;
error_wlp_ie:
return result;
}
/**
* Enroll in and activate WSS identified by provided WSSID
*
* The neighborhood cache should contain a list of all neighbors and the
* WSS they have activated. Based on that cache we search which neighbor we
* can perform the association process with. The user also has option to
* specify which neighbor it prefers as registrar.
* Successful enrollment is followed by activation.
* Successful activation will create the sysfs directory containing
* specific information regarding this WSS.
*/
int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
struct uwb_dev_addr *devaddr)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
char buf[WLP_WSS_UUID_STRSIZE];
mutex_lock(&wss->mutex);
result = wlp_wss_enroll(wss, wssid, devaddr);
if (result < 0) {
wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf);
goto error_enroll;
}
result = wlp_wss_activate(wss);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment "
"result = %d \n", result);
/* Undo enrollment */
wlp_wss_reset(wss);
goto error_activate;
}
error_activate:
error_enroll:
mutex_unlock(&wss->mutex);
return result;
}
/**
* Create, enroll, and activate a new WSS
*
* @wssid: new wssid provided by user
* @name: WSS name requested by used.
* @sec_status: security status requested by user
*
* A user requested the creation of a new WSS. All operations are done
* locally. The new WSS will be stored locally, the hash will be included
* in the WLP IE, and the sysfs infrastructure for this WSS will be
* created.
*/
int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
char *name, unsigned sec_status, unsigned accept)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
char buf[WLP_WSS_UUID_STRSIZE];
result = wlp_wss_uuid_print(buf, sizeof(buf), wssid);
if (!mutex_trylock(&wss->mutex)) {
dev_err(dev, "WLP: WLP association session in progress.\n");
return -EBUSY;
}
if (wss->state != WLP_WSS_STATE_NONE) {
dev_err(dev, "WLP: WSS already exists. Not creating new.\n");
result = -EEXIST;
goto out;
}
if (wss->kobj.parent == NULL) {
dev_err(dev, "WLP: WSS parent not ready. Is network interface "
"up?\n");
result = -ENXIO;
goto out;
}
if (sec_status == WLP_WSS_SECURE) {
dev_err(dev, "WLP: FIXME Creation of secure WSS not "
"supported yet.\n");
result = -EINVAL;
goto out;
}
wss->wssid = *wssid;
memcpy(wss->name, name, sizeof(wss->name));
wss->bcast = wlp_wss_sel_bcast_addr(wss);
wss->secure_status = sec_status;
wss->accept_enroll = accept;
/*wss->virtual_addr is initialized in call to wlp_wss_setup*/
/* sysfs infrastructure */
result = wlp_wss_sysfs_add(wss, buf);
if (result < 0) {
dev_err(dev, "Cannot set up sysfs for WSS kobject.\n");
wlp_wss_reset(wss);
goto out;
} else
result = 0;
wss->state = WLP_WSS_STATE_ENROLLED;
result = wlp_wss_activate(wss);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate WSS. Undoing "
"enrollment\n");
wlp_wss_reset(wss);
goto out;
}
result = 0;
out:
mutex_unlock(&wss->mutex);
return result;
}
/**
* Determine if neighbor has WSS activated
*
* @returns: 1 if neighbor has WSS activated, zero otherwise
*
* This can be done in two ways:
* - send a C1 frame, parse C2/F0 response
* - examine the WLP IE sent by the neighbor
*
* The WLP IE is not fully supported in hardware so we use the C1/C2 frame
* exchange to determine if a WSS is activated. Using the WLP IE should be
* faster and should be used when it becomes possible.
*/
int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct sk_buff *skb;
struct wlp_frame_assoc *resp;
struct wlp_uuid wssid;
mutex_lock(&wlp->mutex);
/* Send C1 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1);
if (result < 0) {
dev_err(dev, "Unable to send C1 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
result = 0;
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_C2;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for C2/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
dev_err(dev, "Timeout while sending C1 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto out;
}
if (result < 0) {
dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = 0;
goto out;
}
/* Parse message in session->data: it will be either C2 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: unable to parse incoming F0 "
"frame from neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = 0;
goto error_resp_parse;
}
/* WLP version and message type fields have already been parsed */
result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid,
skb->len - sizeof(*resp));
if (result < 0) {
dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n");
result = 0;
goto error_resp_parse;
}
if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)))
result = 1;
else {
dev_err(dev, "WLP: Received a C2 frame without matching "
"WSSID.\n");
result = 0;
}
error_resp_parse:
kfree_skb(skb);
out:
wlp->session = NULL;
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Activate connection with neighbor by updating EDA cache
*
* @wss: local WSS to which neighbor wants to connect
* @dev_addr: neighbor's address
* @wssid: neighbor's WSSID - must be same as our WSS's WSSID
* @tag: neighbor's WSS tag used to identify frames transmitted by it
* @virt_addr: neighbor's virtual EUI-48
*/
static
int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr,
struct wlp_uuid *wssid, u8 *tag,
struct uwb_mac_addr *virt_addr)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) {
/* Update EDA cache */
result = wlp_eda_update_node(&wlp->eda, dev_addr, wss,
(void *) virt_addr->data, *tag,
WLP_WSS_CONNECTED);
if (result < 0)
dev_err(dev, "WLP: Unable to update EDA cache "
"with new connected neighbor information.\n");
} else {
dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n");
result = -EINVAL;
}
return result;
}
/**
* Connect to WSS neighbor
*
* Use C3/C4 exchange to determine if neighbor has WSS activated and
* retrieve the WSS tag and virtual EUI-48 of the neighbor.
*/
static
int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss,
struct uwb_dev_addr *dev_addr)
{
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct wlp_uuid wssid;
u8 tag;
struct uwb_mac_addr virt_addr;
DECLARE_COMPLETION_ONSTACK(completion);
struct wlp_session session;
struct wlp_frame_assoc *resp;
struct sk_buff *skb;
mutex_lock(&wlp->mutex);
/* Send C3 association frame */
result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3);
if (result < 0) {
dev_err(dev, "Unable to send C3 frame to neighbor "
"%02x:%02x (%d)\n", dev_addr->data[1],
dev_addr->data[0], result);
goto out;
}
/* Create session, wait for response */
session.exp_message = WLP_ASSOC_C4;
session.cb = wlp_session_cb;
session.cb_priv = &completion;
session.neighbor_addr = *dev_addr;
BUG_ON(wlp->session != NULL);
wlp->session = &session;
/* Wait for C4/F0 frame */
result = wait_for_completion_interruptible_timeout(&completion,
WLP_PER_MSG_TIMEOUT * HZ);
if (result == 0) {
dev_err(dev, "Timeout while sending C3 to neighbor "
"%02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
result = -ETIMEDOUT;
goto out;
}
if (result < 0) {
dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
goto out;
}
/* Parse message in session->data: it will be either C4 or F0 */
skb = session.data;
resp = (void *) skb->data;
if (resp->type == WLP_ASSOC_F0) {
result = wlp_parse_f0(wlp, skb);
if (result < 0)
dev_err(dev, "WLP: unable to parse incoming F0 "
"frame from neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
result = -EINVAL;
goto error_resp_parse;
}
result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
if (result < 0) {
dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n");
goto error_resp_parse;
}
result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag,
&virt_addr);
if (result < 0) {
dev_err(dev, "WLP: Unable to activate connection to "
"neighbor %02x:%02x.\n", dev_addr->data[1],
dev_addr->data[0]);
goto error_resp_parse;
}
error_resp_parse:
kfree_skb(skb);
out:
/* Record that we unsuccessfully tried to connect to this neighbor */
if (result < 0)
wlp_eda_update_node_state(&wlp->eda, dev_addr,
WLP_WSS_CONNECT_FAILED);
wlp->session = NULL;
mutex_unlock(&wlp->mutex);
return result;
}
/**
* Connect to neighbor with common WSS, send pending frame
*
* This function is scheduled when a frame is destined to a neighbor with
* which we do not have a connection. A copy of the EDA cache entry is
* provided - not the actual cache entry (because it is protected by a
* spinlock).
*
* First determine if neighbor has the same WSS activated, connect if it
* does. The C3/C4 exchange is dual purpose to determine if neighbor has
* WSS activated and proceed with the connection.
*
* The frame that triggered the connection setup is sent after connection
* setup.
*
* network queue is stopped - we need to restart when done
*
*/
static
void wlp_wss_connect_send(struct work_struct *ws)
{
struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws,
struct wlp_assoc_conn_ctx,
ws);
struct wlp *wlp = conn_ctx->wlp;
struct sk_buff *skb = conn_ctx->skb;
struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
struct wlp_wss *wss = &wlp->wss;
int result;
struct device *dev = &wlp->rc->uwb_dev.dev;
mutex_lock(&wss->mutex);
if (wss->state < WLP_WSS_STATE_ACTIVE) {
if (printk_ratelimit())
dev_err(dev, "WLP: Attempting to connect with "
"WSS that is not active or connected.\n");
dev_kfree_skb(skb);
goto out;
}
/* Establish connection - send C3 rcv C4 */
result = wlp_wss_connect_neighbor(wlp, wss, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to establish connection "
"with neighbor %02x:%02x.\n",
dev_addr->data[1], dev_addr->data[0]);
dev_kfree_skb(skb);
goto out;
}
/* EDA entry changed, update the local copy being used */
result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Cannot find EDA entry for "
"neighbor %02x:%02x \n",
dev_addr->data[1], dev_addr->data[0]);
}
result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to prepare frame header for "
"transmission (neighbor %02x:%02x). \n",
dev_addr->data[1], dev_addr->data[0]);
dev_kfree_skb(skb);
goto out;
}
BUG_ON(wlp->xmit_frame == NULL);
result = wlp->xmit_frame(wlp, skb, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to transmit frame: %d\n",
result);
if (result == -ENXIO)
dev_err(dev, "WLP: Is network interface up? \n");
/* We could try again ... */
dev_kfree_skb(skb);/*we need to free if tx fails */
}
out:
kfree(conn_ctx);
BUG_ON(wlp->start_queue == NULL);
wlp->start_queue(wlp);
mutex_unlock(&wss->mutex);
}
/**
* Add WLP header to outgoing skb
*
* @eda_entry: pointer to neighbor's entry in the EDA cache
* @_skb: skb containing data destined to the neighbor
*/
int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
unsigned char *eth_addr = eda_entry->eth_addr;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
struct sk_buff *skb = _skb;
struct wlp_frame_std_abbrv_hdr *std_hdr;
if (eda_entry->state == WLP_WSS_CONNECTED) {
/* Add WLP header */
BUG_ON(skb_headroom(skb) < sizeof(*std_hdr));
std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr));
std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
std_hdr->hdr.type = WLP_FRAME_STANDARD;
std_hdr->tag = eda_entry->wss->tag;
} else {
if (printk_ratelimit())
dev_err(dev, "WLP: Destination neighbor (Ethernet: "
"%pM, Dev: %02x:%02x) is not connected.\n",
eth_addr, dev_addr->data[1], dev_addr->data[0]);
result = -EINVAL;
}
return result;
}
/**
* Prepare skb for neighbor: connect if not already and prep WLP header
*
* This function is called in interrupt context, but it needs to sleep. We
* temporarily stop the net queue to establish the WLP connection.
* Setup of the WLP connection and restart of queue is scheduled
* on the default work queue.
*
* run with eda->lock held (spinlock)
*/
int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
int result = 0;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct sk_buff *skb = _skb;
struct wlp_assoc_conn_ctx *conn_ctx;
if (eda_entry->state == WLP_WSS_UNCONNECTED) {
/* We don't want any more packets while we set up connection */
BUG_ON(wlp->stop_queue == NULL);
wlp->stop_queue(wlp);
conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC);
if (conn_ctx == NULL) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to allocate memory "
"for connection handling.\n");
result = -ENOMEM;
goto out;
}
conn_ctx->wlp = wlp;
conn_ctx->skb = skb;
conn_ctx->eda_entry = *eda_entry;
INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send);
schedule_work(&conn_ctx->ws);
result = 1;
} else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) {
/* Previous connection attempts failed, don't retry - see
* conditions for connection in WLP 0.99 [7.6.2] */
if (printk_ratelimit())
dev_err(dev, "Could not connect to neighbor "
"previously. Not retrying. \n");
result = -ENONET;
goto out;
} else /* eda_entry->state == WLP_WSS_CONNECTED */
result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
out:
return result;
}
/**
* Emulate broadcast: copy skb, send copy to neighbor (connect if not already)
*
* We need to copy skbs in the case where we emulate broadcast through
* unicast. We copy instead of clone because we are modifying the data of
* the frame after copying ... clones share data so we cannot emulate
* broadcast using clones.
*
* run with eda->lock held (spinlock)
*/
int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry,
void *_skb)
{
int result = -ENOMEM;
struct device *dev = &wlp->rc->uwb_dev.dev;
struct sk_buff *skb = _skb;
struct sk_buff *copy;
struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
copy = skb_copy(skb, GFP_ATOMIC);
if (copy == NULL) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to copy skb for "
"transmission.\n");
goto out;
}
result = wlp_wss_connect_prep(wlp, eda_entry, copy);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to connect/send skb "
"to neighbor.\n");
dev_kfree_skb_irq(copy);
goto out;
} else if (result == 1)
/* Frame will be transmitted separately */
goto out;
BUG_ON(wlp->xmit_frame == NULL);
result = wlp->xmit_frame(wlp, copy, dev_addr);
if (result < 0) {
if (printk_ratelimit())
dev_err(dev, "WLP: Unable to transmit frame: %d\n",
result);
if ((result == -ENXIO) && printk_ratelimit())
dev_err(dev, "WLP: Is network interface up? \n");
/* We could try again ... */
dev_kfree_skb_irq(copy);/*we need to free if tx fails */
}
out:
return result;
}
/**
* Setup WSS
*
* Should be called by network driver after the interface has been given a
* MAC address.
*/
int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
struct device *dev = &wlp->rc->uwb_dev.dev;
int result = 0;
mutex_lock(&wss->mutex);
wss->kobj.parent = &net_dev->dev.kobj;
if (!is_valid_ether_addr(net_dev->dev_addr)) {
dev_err(dev, "WLP: Invalid MAC address. Cannot use for"
"virtual.\n");
result = -EINVAL;
goto out;
}
memcpy(wss->virtual_addr.data, net_dev->dev_addr,
sizeof(wss->virtual_addr.data));
out:
mutex_unlock(&wss->mutex);
return result;
}
EXPORT_SYMBOL_GPL(wlp_wss_setup);
/**
* Remove WSS
*
* Called by client that configured WSS through wlp_wss_setup(). This
* function is called when client no longer needs WSS, eg. client shuts
* down.
*
* We remove the WLP IE from the beacon before initiating local cleanup.
*/
void wlp_wss_remove(struct wlp_wss *wss)
{
struct wlp *wlp = container_of(wss, struct wlp, wss);
mutex_lock(&wss->mutex);
if (wss->state == WLP_WSS_STATE_ACTIVE)
uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP);
if (wss->state != WLP_WSS_STATE_NONE) {
sysfs_remove_group(&wss->kobj, &wss_attr_group);
kobject_put(&wss->kobj);
}
wss->kobj.parent = NULL;
memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr));
/* Cleanup EDA cache */
wlp_eda_release(&wlp->eda);
wlp_eda_init(&wlp->eda);
mutex_unlock(&wss->mutex);
}
EXPORT_SYMBOL_GPL(wlp_wss_remove);

View file

@ -1,736 +0,0 @@
/*
* WiMedia Logical Link Control Protocol (WLP)
*
* Copyright (C) 2005-2006 Intel Corporation
* Reinette Chatre <reinette.chatre@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
*
* - Does not (yet) include support for WLP control frames
* WLP Draft 0.99 [6.5].
*
* A visual representation of the data structures.
*
* wssidB wssidB
* ^ ^
* | |
* wssidA wssidA
* wlp interface { ^ ^
* ... | |
* ... ... wssid wssid ...
* wlp --- ... | |
* }; neighbors --> neighbA --> neighbB
* ...
* wss
* ...
* eda cache --> neighborA --> neighborB --> neighborC ...
*/
#ifndef __LINUX__WLP_H_
#define __LINUX__WLP_H_
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/uwb.h>
/**
* WLP Protocol ID
* WLP Draft 0.99 [6.2]
*
* The MUX header for all WLP frames
*/
#define WLP_PROTOCOL_ID 0x0100
/**
* WLP Version
* WLP version placed in the association frames (WLP 0.99 [6.6])
*/
#define WLP_VERSION 0x10
/**
* Bytes needed to print UUID as string
*/
#define WLP_WSS_UUID_STRSIZE 48
/**
* Bytes needed to print nonce as string
*/
#define WLP_WSS_NONCE_STRSIZE 48
/**
* Size used for WLP name size
*
* The WSS name is set to 65 bytes, 1 byte larger than the maximum
* allowed by the WLP spec. This is to have a null terminated string
* for display to the user. A maximum of 64 bytes will still be used
* when placing the WSS name field in association frames.
*/
#define WLP_WSS_NAME_SIZE 65
/**
* Number of bytes added by WLP to data frame
*
* A data frame transmitted from a host will be placed in a Standard or
* Abbreviated WLP frame. These have an extra 4 bytes of header (struct
* wlp_frame_std_abbrv_hdr).
* When the stack sends this data frame for transmission it needs to ensure
* there is enough headroom for this header.
*/
#define WLP_DATA_HLEN 4
/**
* State of device regarding WLP Service Set
*
* WLP_WSS_STATE_NONE: the host does not participate in any WSS
* WLP_WSS_STATE_PART_ENROLLED: used as part of the enrollment sequence
* ("Partial Enroll"). This state is used to
* indicate the first part of enrollment that is
* unsecure. If the WSS is unsecure then the
* state will promptly go to WLP_WSS_STATE_ENROLLED,
* if the WSS is not secure then the enrollment
* procedure is a few more steps before we are
* enrolled.
* WLP_WSS_STATE_ENROLLED: the host is enrolled in a WSS
* WLP_WSS_STATE_ACTIVE: WSS is activated
* WLP_WSS_STATE_CONNECTED: host is connected to neighbor in WSS
*
*/
enum wlp_wss_state {
WLP_WSS_STATE_NONE = 0,
WLP_WSS_STATE_PART_ENROLLED,
WLP_WSS_STATE_ENROLLED,
WLP_WSS_STATE_ACTIVE,
WLP_WSS_STATE_CONNECTED,
};
/**
* WSS Secure status
* WLP 0.99 Table 6
*
* Set to one if the WSS is secure, zero if it is not secure
*/
enum wlp_wss_sec_status {
WLP_WSS_UNSECURE = 0,
WLP_WSS_SECURE,
};
/**
* WLP frame type
* WLP Draft 0.99 [6.2 Table 1]
*/
enum wlp_frame_type {
WLP_FRAME_STANDARD = 0,
WLP_FRAME_ABBREVIATED,
WLP_FRAME_CONTROL,
WLP_FRAME_ASSOCIATION,
};
/**
* WLP Association Message Type
* WLP Draft 0.99 [6.6.1.2 Table 8]
*/
enum wlp_assoc_type {
WLP_ASSOC_D1 = 2,
WLP_ASSOC_D2 = 3,
WLP_ASSOC_M1 = 4,
WLP_ASSOC_M2 = 5,
WLP_ASSOC_M3 = 7,
WLP_ASSOC_M4 = 8,
WLP_ASSOC_M5 = 9,
WLP_ASSOC_M6 = 10,
WLP_ASSOC_M7 = 11,
WLP_ASSOC_M8 = 12,
WLP_ASSOC_F0 = 14,
WLP_ASSOC_E1 = 32,
WLP_ASSOC_E2 = 33,
WLP_ASSOC_C1 = 34,
WLP_ASSOC_C2 = 35,
WLP_ASSOC_C3 = 36,
WLP_ASSOC_C4 = 37,
};
/**
* WLP Attribute Type
* WLP Draft 0.99 [6.6.1 Table 6]
*/
enum wlp_attr_type {
WLP_ATTR_AUTH = 0x1005, /* Authenticator */
WLP_ATTR_DEV_NAME = 0x1011, /* Device Name */
WLP_ATTR_DEV_PWD_ID = 0x1012, /* Device Password ID */
WLP_ATTR_E_HASH1 = 0x1014, /* E-Hash1 */
WLP_ATTR_E_HASH2 = 0x1015, /* E-Hash2 */
WLP_ATTR_E_SNONCE1 = 0x1016, /* E-SNonce1 */
WLP_ATTR_E_SNONCE2 = 0x1017, /* E-SNonce2 */
WLP_ATTR_ENCR_SET = 0x1018, /* Encrypted Settings */
WLP_ATTR_ENRL_NONCE = 0x101A, /* Enrollee Nonce */
WLP_ATTR_KEYWRAP_AUTH = 0x101E, /* Key Wrap Authenticator */
WLP_ATTR_MANUF = 0x1021, /* Manufacturer */
WLP_ATTR_MSG_TYPE = 0x1022, /* Message Type */
WLP_ATTR_MODEL_NAME = 0x1023, /* Model Name */
WLP_ATTR_MODEL_NR = 0x1024, /* Model Number */
WLP_ATTR_PUB_KEY = 0x1032, /* Public Key */
WLP_ATTR_REG_NONCE = 0x1039, /* Registrar Nonce */
WLP_ATTR_R_HASH1 = 0x103D, /* R-Hash1 */
WLP_ATTR_R_HASH2 = 0x103E, /* R-Hash2 */
WLP_ATTR_R_SNONCE1 = 0x103F, /* R-SNonce1 */
WLP_ATTR_R_SNONCE2 = 0x1040, /* R-SNonce2 */
WLP_ATTR_SERIAL = 0x1042, /* Serial number */
WLP_ATTR_UUID_E = 0x1047, /* UUID-E */
WLP_ATTR_UUID_R = 0x1048, /* UUID-R */
WLP_ATTR_PRI_DEV_TYPE = 0x1054, /* Primary Device Type */
WLP_ATTR_SEC_DEV_TYPE = 0x1055, /* Secondary Device Type */
WLP_ATTR_PORT_DEV = 0x1056, /* Portable Device */
WLP_ATTR_APP_EXT = 0x1058, /* Application Extension */
WLP_ATTR_WLP_VER = 0x2000, /* WLP Version */
WLP_ATTR_WSSID = 0x2001, /* WSSID */
WLP_ATTR_WSS_NAME = 0x2002, /* WSS Name */
WLP_ATTR_WSS_SEC_STAT = 0x2003, /* WSS Secure Status */
WLP_ATTR_WSS_BCAST = 0x2004, /* WSS Broadcast Address */
WLP_ATTR_WSS_M_KEY = 0x2005, /* WSS Master Key */
WLP_ATTR_ACC_ENRL = 0x2006, /* Accepting Enrollment */
WLP_ATTR_WSS_INFO = 0x2007, /* WSS Information */
WLP_ATTR_WSS_SEL_MTHD = 0x2008, /* WSS Selection Method */
WLP_ATTR_ASSC_MTHD_LIST = 0x2009, /* Association Methods List */
WLP_ATTR_SEL_ASSC_MTHD = 0x200A, /* Selected Association Method */
WLP_ATTR_ENRL_HASH_COMM = 0x200B, /* Enrollee Hash Commitment */
WLP_ATTR_WSS_TAG = 0x200C, /* WSS Tag */
WLP_ATTR_WSS_VIRT = 0x200D, /* WSS Virtual EUI-48 */
WLP_ATTR_WLP_ASSC_ERR = 0x200E, /* WLP Association Error */
WLP_ATTR_VNDR_EXT = 0x200F, /* Vendor Extension */
};
/**
* WLP Category ID of primary/secondary device
* WLP Draft 0.99 [6.6.1.8 Table 12]
*/
enum wlp_dev_category_id {
WLP_DEV_CAT_COMPUTER = 1,
WLP_DEV_CAT_INPUT,
WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER,
WLP_DEV_CAT_CAMERA,
WLP_DEV_CAT_STORAGE,
WLP_DEV_CAT_INFRASTRUCTURE,
WLP_DEV_CAT_DISPLAY,
WLP_DEV_CAT_MULTIM,
WLP_DEV_CAT_GAMING,
WLP_DEV_CAT_TELEPHONE,
WLP_DEV_CAT_OTHER = 65535,
};
/**
* WLP WSS selection method
* WLP Draft 0.99 [6.6.1.6 Table 10]
*/
enum wlp_wss_sel_mthd {
WLP_WSS_ENRL_SELECT = 1, /* Enrollee selects */
WLP_WSS_REG_SELECT, /* Registrar selects */
};
/**
* WLP association error values
* WLP Draft 0.99 [6.6.1.5 Table 9]
*/
enum wlp_assc_error {
WLP_ASSOC_ERROR_NONE,
WLP_ASSOC_ERROR_AUTH, /* Authenticator Failure */
WLP_ASSOC_ERROR_ROGUE, /* Rogue activity suspected */
WLP_ASSOC_ERROR_BUSY, /* Device busy */
WLP_ASSOC_ERROR_LOCK, /* Setup Locked */
WLP_ASSOC_ERROR_NOT_READY, /* Registrar not ready */
WLP_ASSOC_ERROR_INV, /* Invalid WSS selection */
WLP_ASSOC_ERROR_MSG_TIME, /* Message timeout */
WLP_ASSOC_ERROR_ENR_TIME, /* Enrollment session timeout */
WLP_ASSOC_ERROR_PW, /* Device password invalid */
WLP_ASSOC_ERROR_VER, /* Unsupported version */
WLP_ASSOC_ERROR_INT, /* Internal error */
WLP_ASSOC_ERROR_UNDEF, /* Undefined error */
WLP_ASSOC_ERROR_NUM, /* Numeric comparison failure */
WLP_ASSOC_ERROR_WAIT, /* Waiting for user input */
};
/**
* WLP Parameters
* WLP 0.99 [7.7]
*/
enum wlp_parameters {
WLP_PER_MSG_TIMEOUT = 15, /* Seconds to wait for response to
association message. */
};
/**
* WLP IE
*
* The WLP IE should be included in beacons by all devices.
*
* The driver can set only a few of the fields in this information element,
* most fields are managed by the device self. When the driver needs to set
* a field it will only provide values for the fields of interest, the rest
* will be filled with zeroes. The fields of interest are:
*
* Element ID
* Length
* Capabilities (only to include WSSID Hash list length)
* WSSID Hash List fields
*
* WLP 0.99 [6.7]
*
* Only the fields that will be used are detailed in this structure, rest
* are not detailed or marked as "notused".
*/
struct wlp_ie {
struct uwb_ie_hdr hdr;
__le16 capabilities;
__le16 cycle_param;
__le16 acw_anchor_addr;
u8 wssid_hash_list[];
} __packed;
static inline int wlp_ie_hash_length(struct wlp_ie *ie)
{
return (le16_to_cpu(ie->capabilities) >> 12) & 0xf;
}
static inline void wlp_ie_set_hash_length(struct wlp_ie *ie, int hash_length)
{
u16 caps = le16_to_cpu(ie->capabilities);
caps = (caps & ~(0xf << 12)) | (hash_length << 12);
ie->capabilities = cpu_to_le16(caps);
}
/**
* WLP nonce
* WLP Draft 0.99 [6.6.1 Table 6]
*
* A 128-bit random number often used (E-SNonce1, E-SNonce2, Enrollee
* Nonce, Registrar Nonce, R-SNonce1, R-SNonce2). It is passed to HW so
* it is packed.
*/
struct wlp_nonce {
u8 data[16];
} __packed;
/**
* WLP UUID
* WLP Draft 0.99 [6.6.1 Table 6]
*
* Universally Unique Identifier (UUID) encoded as an octet string in the
* order the octets are shown in string representation in RFC4122. A UUID
* is often used (UUID-E, UUID-R, WSSID). It is passed to HW so it is packed.
*/
struct wlp_uuid {
u8 data[16];
} __packed;
/**
* Primary and secondary device type attributes
* WLP Draft 0.99 [6.6.1.8]
*/
struct wlp_dev_type {
enum wlp_dev_category_id category:16;
u8 OUI[3];
u8 OUIsubdiv;
__le16 subID;
} __packed;
/**
* WLP frame header
* WLP Draft 0.99 [6.2]
*/
struct wlp_frame_hdr {
__le16 mux_hdr; /* WLP_PROTOCOL_ID */
enum wlp_frame_type type:8;
} __packed;
/**
* WLP attribute field header
* WLP Draft 0.99 [6.6.1]
*
* Header of each attribute found in an association frame
*/
struct wlp_attr_hdr {
__le16 type;
__le16 length;
} __packed;
/**
* Device information commonly used together
*
* Each of these device information elements has a specified range in which it
* should fit (WLP 0.99 [Table 6]). This range provided in the spec does not
* include the termination null '\0' character (when used in the
* association protocol the attribute fields are accompanied
* with a "length" field so the full range from the spec can be used for
* the value). We thus allocate an extra byte to be able to store a string
* of max length with a terminating '\0'.
*/
struct wlp_device_info {
char name[33];
char model_name[33];
char manufacturer[65];
char model_nr[33];
char serial[33];
struct wlp_dev_type prim_dev_type;
};
/**
* Macros for the WLP attributes
*
* There are quite a few attributes (total is 43). The attribute layout can be
* in one of three categories: one value, an array, an enum forced to 8 bits.
* These macros help with their definitions.
*/
#define wlp_attr(type, name) \
struct wlp_attr_##name { \
struct wlp_attr_hdr hdr; \
type name; \
} __packed;
#define wlp_attr_array(type, name) \
struct wlp_attr_##name { \
struct wlp_attr_hdr hdr; \
type name[]; \
} __packed;
/**
* WLP association attribute fields
* WLP Draft 0.99 [6.6.1 Table 6]
*
* Attributes appear in same order as the Table in the spec
* FIXME Does not define all attributes yet
*/
/* Device name: Friendly name of sending device */
wlp_attr_array(u8, dev_name)
/* Enrollee Nonce: Random number generated by enrollee for an enrollment
* session */
wlp_attr(struct wlp_nonce, enonce)
/* Manufacturer name: Name of manufacturer of the sending device */
wlp_attr_array(u8, manufacturer)
/* WLP Message Type */
wlp_attr(u8, msg_type)
/* WLP Model name: Model name of sending device */
wlp_attr_array(u8, model_name)
/* WLP Model number: Model number of sending device */
wlp_attr_array(u8, model_nr)
/* Registrar Nonce: Random number generated by registrar for an enrollment
* session */
wlp_attr(struct wlp_nonce, rnonce)
/* Serial number of device */
wlp_attr_array(u8, serial)
/* UUID of enrollee */
wlp_attr(struct wlp_uuid, uuid_e)
/* UUID of registrar */
wlp_attr(struct wlp_uuid, uuid_r)
/* WLP Primary device type */
wlp_attr(struct wlp_dev_type, prim_dev_type)
/* WLP Secondary device type */
wlp_attr(struct wlp_dev_type, sec_dev_type)
/* WLP protocol version */
wlp_attr(u8, version)
/* WLP service set identifier */
wlp_attr(struct wlp_uuid, wssid)
/* WLP WSS name */
wlp_attr_array(u8, wss_name)
/* WLP WSS Secure Status */
wlp_attr(u8, wss_sec_status)
/* WSS Broadcast Address */
wlp_attr(struct uwb_mac_addr, wss_bcast)
/* WLP Accepting Enrollment */
wlp_attr(u8, accept_enrl)
/**
* WSS information attributes
* WLP Draft 0.99 [6.6.3 Table 15]
*/
struct wlp_wss_info {
struct wlp_attr_wssid wssid;
struct wlp_attr_wss_name name;
struct wlp_attr_accept_enrl accept;
struct wlp_attr_wss_sec_status sec_stat;
struct wlp_attr_wss_bcast bcast;
} __packed;
/* WLP WSS Information */
wlp_attr_array(struct wlp_wss_info, wss_info)
/* WLP WSS Selection method */
wlp_attr(u8, wss_sel_mthd)
/* WLP WSS tag */
wlp_attr(u8, wss_tag)
/* WSS Virtual Address */
wlp_attr(struct uwb_mac_addr, wss_virt)
/* WLP association error */
wlp_attr(u8, wlp_assc_err)
/**
* WLP standard and abbreviated frames
*
* WLP Draft 0.99 [6.3] and [6.4]
*
* The difference between the WLP standard frame and the WLP
* abbreviated frame is that the standard frame includes the src
* and dest addresses from the Ethernet header, the abbreviated frame does
* not.
* The src/dest (as well as the type/length and client data) are already
* defined as part of the Ethernet header, we do not do this here.
* From this perspective the standard and abbreviated frames appear the
* same - they will be treated differently though.
*
* The size of this header is also captured in WLP_DATA_HLEN to enable
* interfaces to prepare their headroom.
*/
struct wlp_frame_std_abbrv_hdr {
struct wlp_frame_hdr hdr;
u8 tag;
} __packed;
/**
* WLP association frames
*
* WLP Draft 0.99 [6.6]
*/
struct wlp_frame_assoc {
struct wlp_frame_hdr hdr;
enum wlp_assoc_type type:8;
struct wlp_attr_version version;
struct wlp_attr_msg_type msg_type;
u8 attr[];
} __packed;
/* Ethernet to dev address mapping */
struct wlp_eda {
spinlock_t lock;
struct list_head cache; /* Eth<->Dev Addr cache */
};
/**
* WSS information temporary storage
*
* This information is only stored temporarily during discovery. It should
* not be stored unless the device is enrolled in the advertised WSS. This
* is done mainly because we follow the letter of the spec in this regard.
* See WLP 0.99 [7.2.3].
* When the device does become enrolled in a WSS the WSS information will
* be stored as part of the more comprehensive struct wlp_wss.
*/
struct wlp_wss_tmp_info {
char name[WLP_WSS_NAME_SIZE];
u8 accept_enroll;
u8 sec_status;
struct uwb_mac_addr bcast;
};
struct wlp_wssid_e {
struct list_head node;
struct wlp_uuid wssid;
struct wlp_wss_tmp_info *info;
};
/**
* A cache entry of WLP neighborhood
*
* @node: head of list is wlp->neighbors
* @wssid: list of wssids of this neighbor, element is wlp_wssid_e
* @info: temporary storage for information learned during discovery. This
* storage is used together with the wssid_e temporary storage
* during discovery.
*/
struct wlp_neighbor_e {
struct list_head node;
struct wlp_uuid uuid;
struct uwb_dev *uwb_dev;
struct list_head wssid; /* Elements are wlp_wssid_e */
struct wlp_device_info *info;
};
struct wlp;
/**
* Information for an association session in progress.
*
* @exp_message: The type of the expected message. Both this message and a
* F0 message (which can be sent in response to any
* association frame) will be accepted as a valid message for
* this session.
* @cb: The function that will be called upon receipt of this
* message.
* @cb_priv: Private data of callback
* @data: Data used in association process (always a sk_buff?)
* @neighbor: Address of neighbor with which association session is in
* progress.
*/
struct wlp_session {
enum wlp_assoc_type exp_message;
void (*cb)(struct wlp *);
void *cb_priv;
void *data;
struct uwb_dev_addr neighbor_addr;
};
/**
* WLP Service Set
*
* @mutex: used to protect entire WSS structure.
*
* @name: The WSS name is set to 65 bytes, 1 byte larger than the maximum
* allowed by the WLP spec. This is to have a null terminated string
* for display to the user. A maximum of 64 bytes will still be used
* when placing the WSS name field in association frames.
*
* @accept_enroll: Accepting enrollment: Set to one if registrar is
* accepting enrollment in WSS, or zero otherwise.
*
* Global and local information for each WSS in which we are enrolled.
* WLP 0.99 Section 7.2.1 and Section 7.2.2
*/
struct wlp_wss {
struct mutex mutex;
struct kobject kobj;
/* Global properties. */
struct wlp_uuid wssid;
u8 hash;
char name[WLP_WSS_NAME_SIZE];
struct uwb_mac_addr bcast;
u8 secure_status:1;
u8 master_key[16];
/* Local properties. */
u8 tag;
struct uwb_mac_addr virtual_addr;
/* Extra */
u8 accept_enroll:1;
enum wlp_wss_state state;
};
/**
* WLP main structure
* @mutex: protect changes to WLP structure. We only allow changes to the
* uuid, so currently this mutex only protects this field.
*/
struct wlp {
struct mutex mutex;
struct uwb_rc *rc; /* UWB radio controller */
struct net_device *ndev;
struct uwb_pal pal;
struct wlp_eda eda;
struct wlp_uuid uuid;
struct wlp_session *session;
struct wlp_wss wss;
struct mutex nbmutex; /* Neighbor mutex protects neighbors list */
struct list_head neighbors; /* Elements are wlp_neighbor_e */
struct uwb_notifs_handler uwb_notifs_handler;
struct wlp_device_info *dev_info;
void (*fill_device_info)(struct wlp *wlp, struct wlp_device_info *info);
int (*xmit_frame)(struct wlp *, struct sk_buff *,
struct uwb_dev_addr *);
void (*stop_queue)(struct wlp *);
void (*start_queue)(struct wlp *);
};
/* sysfs */
struct wlp_wss_attribute {
struct attribute attr;
ssize_t (*show)(struct wlp_wss *wss, char *buf);
ssize_t (*store)(struct wlp_wss *wss, const char *buf, size_t count);
};
#define WSS_ATTR(_name, _mode, _show, _store) \
static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode, \
_show, _store)
extern int wlp_setup(struct wlp *, struct uwb_rc *, struct net_device *ndev);
extern void wlp_remove(struct wlp *);
extern ssize_t wlp_neighborhood_show(struct wlp *, char *);
extern int wlp_wss_setup(struct net_device *, struct wlp_wss *);
extern void wlp_wss_remove(struct wlp_wss *);
extern ssize_t wlp_wss_activate_show(struct wlp_wss *, char *);
extern ssize_t wlp_wss_activate_store(struct wlp_wss *, const char *, size_t);
extern ssize_t wlp_eda_show(struct wlp *, char *);
extern ssize_t wlp_eda_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_uuid_show(struct wlp *, char *);
extern ssize_t wlp_uuid_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_name_show(struct wlp *, char *);
extern ssize_t wlp_dev_name_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_manufacturer_show(struct wlp *, char *);
extern ssize_t wlp_dev_manufacturer_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_model_name_show(struct wlp *, char *);
extern ssize_t wlp_dev_model_name_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_model_nr_show(struct wlp *, char *);
extern ssize_t wlp_dev_model_nr_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_serial_show(struct wlp *, char *);
extern ssize_t wlp_dev_serial_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_prim_category_show(struct wlp *, char *);
extern ssize_t wlp_dev_prim_category_store(struct wlp *, const char *,
size_t);
extern ssize_t wlp_dev_prim_OUI_show(struct wlp *, char *);
extern ssize_t wlp_dev_prim_OUI_store(struct wlp *, const char *, size_t);
extern ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *, char *);
extern ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *, const char *,
size_t);
extern ssize_t wlp_dev_prim_subcat_show(struct wlp *, char *);
extern ssize_t wlp_dev_prim_subcat_store(struct wlp *, const char *,
size_t);
extern int wlp_receive_frame(struct device *, struct wlp *, struct sk_buff *,
struct uwb_dev_addr *);
extern int wlp_prepare_tx_frame(struct device *, struct wlp *,
struct sk_buff *, struct uwb_dev_addr *);
void wlp_reset_all(struct wlp *wlp);
/**
* Initialize WSS
*/
static inline
void wlp_wss_init(struct wlp_wss *wss)
{
mutex_init(&wss->mutex);
}
static inline
void wlp_init(struct wlp *wlp)
{
INIT_LIST_HEAD(&wlp->neighbors);
mutex_init(&wlp->mutex);
mutex_init(&wlp->nbmutex);
wlp_wss_init(&wlp->wss);
}
#endif /* #ifndef __LINUX__WLP_H_ */