You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
uwb: Remove the WLP subsystem and drivers
The only Wimedia LLC Protocol (WLP) hardware was an Intel i1480 chip with a beta release of firmware that was never commercially available as a product. This hardware and firmware is no longer available as Intel sold their UWB/WLP IP. I also see little prospect of other WLP capable hardware ever being available. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
-12
@@ -5995,9 +5995,6 @@ M: David Vrabel <david.vrabel@csr.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Supported
|
||||
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 +6530,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
|
||||
|
||||
+1
-19
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
|
||||
obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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
|
||||
@@ -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__ */
|
||||
@@ -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");
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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");
|
||||
@@ -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",
|
||||
ð_addr[0], ð_addr[1],
|
||||
ð_addr[2], ð_addr[3],
|
||||
ð_addr[4], ð_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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -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__ */
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user