diff options
| author | David Vrabel <david.vrabel@csr.com> | 2010-10-25 08:57:32 -0400 |
|---|---|---|
| committer | David Vrabel <david.vrabel@csr.com> | 2010-10-25 09:03:45 -0400 |
| commit | 446396bfab00392010ebc36b9ccf859935b0f17b (patch) | |
| tree | 7612ca4ed9f6cbd9e4341e7c3deae1634d3d87d8 | |
| parent | 229aebb873e29726b91e076161649cf45154b0bf (diff) | |
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>
| -rw-r--r-- | MAINTAINERS | 12 | ||||
| -rw-r--r-- | drivers/uwb/Kconfig | 20 | ||||
| -rw-r--r-- | drivers/uwb/Makefile | 1 | ||||
| -rw-r--r-- | drivers/uwb/i1480/Makefile | 1 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480-wlp.h | 200 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/Makefile | 8 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h | 283 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/lc.c | 424 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/netdev.c | 331 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/rx.c | 474 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/sysfs.c | 407 | ||||
| -rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/tx.c | 584 | ||||
| -rw-r--r-- | drivers/uwb/wlp/Makefile | 10 | ||||
| -rw-r--r-- | drivers/uwb/wlp/driver.c | 43 | ||||
| -rw-r--r-- | drivers/uwb/wlp/eda.c | 415 | ||||
| -rw-r--r-- | drivers/uwb/wlp/messages.c | 1798 | ||||
| -rw-r--r-- | drivers/uwb/wlp/sysfs.c | 708 | ||||
| -rw-r--r-- | drivers/uwb/wlp/txrx.c | 354 | ||||
| -rw-r--r-- | drivers/uwb/wlp/wlp-internal.h | 224 | ||||
| -rw-r--r-- | drivers/uwb/wlp/wlp-lc.c | 560 | ||||
| -rw-r--r-- | drivers/uwb/wlp/wss-lc.c | 959 | ||||
| -rw-r--r-- | include/linux/wlp.h | 736 |
22 files changed, 1 insertions, 8551 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 69aa8fe060b3..3a3df3f28d37 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -5995,9 +5995,6 @@ M: David Vrabel <david.vrabel@csr.com> | |||
| 5995 | L: linux-usb@vger.kernel.org | 5995 | L: linux-usb@vger.kernel.org |
| 5996 | S: Supported | 5996 | S: Supported |
| 5997 | F: drivers/uwb/ | 5997 | F: drivers/uwb/ |
| 5998 | X: drivers/uwb/wlp/ | ||
| 5999 | X: drivers/uwb/i1480/i1480u-wlp/ | ||
| 6000 | X: drivers/uwb/i1480/i1480-wlp.h | ||
| 6001 | F: include/linux/uwb.h | 5998 | F: include/linux/uwb.h |
| 6002 | F: include/linux/uwb/ | 5999 | F: include/linux/uwb/ |
| 6003 | 6000 | ||
| @@ -6533,15 +6530,6 @@ F: include/linux/wimax/debug.h | |||
| 6533 | F: include/net/wimax.h | 6530 | F: include/net/wimax.h |
| 6534 | F: net/wimax/ | 6531 | F: net/wimax/ |
| 6535 | 6532 | ||
| 6536 | WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM | ||
| 6537 | M: David Vrabel <david.vrabel@csr.com> | ||
| 6538 | L: netdev@vger.kernel.org | ||
| 6539 | S: Maintained | ||
| 6540 | F: include/linux/wlp.h | ||
| 6541 | F: drivers/uwb/wlp/ | ||
| 6542 | F: drivers/uwb/i1480/i1480u-wlp/ | ||
| 6543 | F: drivers/uwb/i1480/i1480-wlp.h | ||
| 6544 | |||
| 6545 | WISTRON LAPTOP BUTTON DRIVER | 6533 | WISTRON LAPTOP BUTTON DRIVER |
| 6546 | M: Miloslav Trmac <mitr@volny.cz> | 6534 | M: Miloslav Trmac <mitr@volny.cz> |
| 6547 | S: Maintained | 6535 | S: Maintained |
diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig index bac8e7a6f17b..d100f54ed650 100644 --- a/drivers/uwb/Kconfig +++ b/drivers/uwb/Kconfig | |||
| @@ -12,8 +12,7 @@ menuconfig UWB | |||
| 12 | technology using a wide spectrum (3.1-10.6GHz). It is | 12 | technology using a wide spectrum (3.1-10.6GHz). It is |
| 13 | optimized for in-room use (480Mbps at 2 meters, 110Mbps at | 13 | optimized for in-room use (480Mbps at 2 meters, 110Mbps at |
| 14 | 10m). It serves as the transport layer for other protocols, | 14 | 10m). It serves as the transport layer for other protocols, |
| 15 | such as Wireless USB (WUSB), IP (WLP) and upcoming | 15 | such as Wireless USB (WUSB). |
| 16 | Bluetooth and 1394 | ||
| 17 | 16 | ||
| 18 | The topology is peer to peer; however, higher level | 17 | The topology is peer to peer; however, higher level |
| 19 | protocols (such as WUSB) might impose a master/slave | 18 | protocols (such as WUSB) might impose a master/slave |
| @@ -58,13 +57,6 @@ config UWB_WHCI | |||
| 58 | To compile this driver select Y (built in) or M (module). It | 57 | To compile this driver select Y (built in) or M (module). It |
| 59 | is safe to select any even if you do not have the hardware. | 58 | is safe to select any even if you do not have the hardware. |
| 60 | 59 | ||
| 61 | config UWB_WLP | ||
| 62 | tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)" | ||
| 63 | depends on UWB && NET | ||
| 64 | help | ||
| 65 | This is a common library for drivers that implement | ||
| 66 | networking over UWB. | ||
| 67 | |||
| 68 | config UWB_I1480U | 60 | config UWB_I1480U |
| 69 | tristate "Support for Intel Wireless UWB Link 1480 HWA" | 61 | tristate "Support for Intel Wireless UWB Link 1480 HWA" |
| 70 | depends on UWB_HWA | 62 | depends on UWB_HWA |
| @@ -77,14 +69,4 @@ config UWB_I1480U | |||
| 77 | To compile this driver select Y (built in) or M (module). It | 69 | To compile this driver select Y (built in) or M (module). It |
| 78 | is safe to select any even if you do not have the hardware. | 70 | is safe to select any even if you do not have the hardware. |
| 79 | 71 | ||
| 80 | config UWB_I1480U_WLP | ||
| 81 | tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface" | ||
| 82 | depends on UWB_I1480U && UWB_WLP && NET | ||
| 83 | help | ||
| 84 | This driver enables WLP support for the i1480 when connected via | ||
| 85 | USB. WLP is the WiMedia Link Protocol, or IP over UWB. | ||
| 86 | |||
| 87 | To compile this driver select Y (built in) or M (module). It | ||
| 88 | is safe to select any even if you don't have the hardware. | ||
| 89 | |||
| 90 | endif # UWB | 72 | endif # UWB |
diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index 2f98d080fe78..d47dd6e2942c 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | obj-$(CONFIG_UWB) += uwb.o | 1 | obj-$(CONFIG_UWB) += uwb.o |
| 2 | obj-$(CONFIG_UWB_WLP) += wlp/ | ||
| 3 | obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o | 2 | obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o |
| 4 | obj-$(CONFIG_UWB_HWA) += hwa-rc.o | 3 | obj-$(CONFIG_UWB_HWA) += hwa-rc.o |
| 5 | obj-$(CONFIG_UWB_I1480U) += i1480/ | 4 | obj-$(CONFIG_UWB_I1480U) += i1480/ |
diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile index 212bbc7d4c32..d69da1684cfb 100644 --- a/drivers/uwb/i1480/Makefile +++ b/drivers/uwb/i1480/Makefile | |||
| @@ -1,2 +1 @@ | |||
| 1 | obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o | obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o | |
| 2 | obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/ | ||
diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h deleted file mode 100644 index 18a8b0e4567b..000000000000 --- a/drivers/uwb/i1480/i1480-wlp.h +++ /dev/null | |||
| @@ -1,200 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Intel 1480 Wireless UWB Link | ||
| 3 | * WLP specific definitions | ||
| 4 | * | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License version | ||
| 11 | * 2 as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 21 | * 02110-1301, USA. | ||
| 22 | * | ||
| 23 | * | ||
| 24 | * FIXME: docs | ||
| 25 | */ | ||
| 26 | |||
| 27 | #ifndef __i1480_wlp_h__ | ||
| 28 | #define __i1480_wlp_h__ | ||
| 29 | |||
| 30 | #include <linux/spinlock.h> | ||
| 31 | #include <linux/list.h> | ||
| 32 | #include <linux/uwb.h> | ||
| 33 | #include <linux/if_ether.h> | ||
| 34 | #include <asm/byteorder.h> | ||
| 35 | |||
| 36 | /* New simplified header format? */ | ||
| 37 | #undef WLP_HDR_FMT_2 /* FIXME: rename */ | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Values of the Delivery ID & Type field when PCA or DRP | ||
| 41 | * | ||
| 42 | * The Delivery ID & Type field in the WLP TX header indicates whether | ||
| 43 | * the frame is PCA or DRP. This is done based on the high level bit of | ||
| 44 | * this field. | ||
| 45 | * We use this constant to test if the traffic is PCA or DRP as follows: | ||
| 46 | * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP) | ||
| 47 | * this is DRP traffic | ||
| 48 | * else | ||
| 49 | * this is PCA traffic | ||
| 50 | */ | ||
| 51 | enum deliver_id_type_bit { | ||
| 52 | WLP_DRP = 8, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * WLP TX header | ||
| 57 | * | ||
| 58 | * Indicates UWB/WLP-specific transmission parameters for a network | ||
| 59 | * packet. | ||
| 60 | */ | ||
| 61 | struct wlp_tx_hdr { | ||
| 62 | /* dword 0 */ | ||
| 63 | struct uwb_dev_addr dstaddr; | ||
| 64 | u8 key_index; | ||
| 65 | u8 mac_params; | ||
| 66 | /* dword 1 */ | ||
| 67 | u8 phy_params; | ||
| 68 | #ifndef WLP_HDR_FMT_2 | ||
| 69 | u8 reserved; | ||
| 70 | __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */ | ||
| 71 | /* dword 2 */ | ||
| 72 | u8 oui2; /* if all LE, it could be merged */ | ||
| 73 | __le16 prid; | ||
| 74 | #endif | ||
| 75 | } __attribute__((packed)); | ||
| 76 | |||
| 77 | static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr) | ||
| 78 | { | ||
| 79 | return hdr->mac_params & 0x0f; | ||
| 80 | } | ||
| 81 | |||
| 82 | static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr) | ||
| 83 | { | ||
| 84 | return (hdr->mac_params >> 4) & 0x07; | ||
| 85 | } | ||
| 86 | |||
| 87 | static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr) | ||
| 88 | { | ||
| 89 | return (hdr->mac_params >> 7) & 0x01; | ||
| 90 | } | ||
| 91 | |||
| 92 | static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id) | ||
| 93 | { | ||
| 94 | hdr->mac_params = (hdr->mac_params & ~0x0f) | id; | ||
| 95 | } | ||
| 96 | |||
| 97 | static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr, | ||
| 98 | enum uwb_ack_pol policy) | ||
| 99 | { | ||
| 100 | hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts) | ||
| 104 | { | ||
| 105 | hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7); | ||
| 106 | } | ||
| 107 | |||
| 108 | static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr) | ||
| 109 | { | ||
| 110 | return hdr->phy_params & 0x0f; | ||
| 111 | } | ||
| 112 | |||
| 113 | static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr) | ||
| 114 | { | ||
| 115 | return (hdr->phy_params >> 4) & 0x0f; | ||
| 116 | } | ||
| 117 | |||
| 118 | static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate) | ||
| 119 | { | ||
| 120 | hdr->phy_params = (hdr->phy_params & ~0x0f) | rate; | ||
| 121 | } | ||
| 122 | |||
| 123 | static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr) | ||
| 124 | { | ||
| 125 | hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4); | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | /** | ||
| 130 | * WLP RX header | ||
| 131 | * | ||
| 132 | * Provides UWB/WLP-specific transmission data for a received | ||
| 133 | * network packet. | ||
| 134 | */ | ||
| 135 | struct wlp_rx_hdr { | ||
| 136 | /* dword 0 */ | ||
| 137 | struct uwb_dev_addr dstaddr; | ||
| 138 | struct uwb_dev_addr srcaddr; | ||
| 139 | /* dword 1 */ | ||
| 140 | u8 LQI; | ||
| 141 | s8 RSSI; | ||
| 142 | u8 reserved3; | ||
| 143 | #ifndef WLP_HDR_FMT_2 | ||
| 144 | u8 oui0; | ||
| 145 | /* dword 2 */ | ||
| 146 | __le16 oui12; | ||
| 147 | __le16 prid; | ||
| 148 | #endif | ||
| 149 | } __attribute__((packed)); | ||
| 150 | |||
| 151 | |||
| 152 | /** User configurable options for WLP */ | ||
| 153 | struct wlp_options { | ||
| 154 | struct mutex mutex; /* access to user configurable options*/ | ||
| 155 | struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */ | ||
| 156 | u8 pca_base_priority; | ||
| 157 | u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/ | ||
| 158 | }; | ||
| 159 | |||
| 160 | |||
| 161 | static inline | ||
| 162 | void wlp_options_init(struct wlp_options *options) | ||
| 163 | { | ||
| 164 | mutex_init(&options->mutex); | ||
| 165 | wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM); | ||
| 166 | wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1); | ||
| 167 | /* FIXME: default to phy caps */ | ||
| 168 | wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480); | ||
| 169 | #ifndef WLP_HDR_FMT_2 | ||
| 170 | options->def_tx_hdr.prid = cpu_to_le16(0x0000); | ||
| 171 | #endif | ||
| 172 | } | ||
| 173 | |||
| 174 | |||
| 175 | /* sysfs helpers */ | ||
| 176 | |||
| 177 | extern ssize_t uwb_pca_base_priority_store(struct wlp_options *, | ||
| 178 | const char *, size_t); | ||
| 179 | extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *); | ||
| 180 | extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t); | ||
| 181 | extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *); | ||
| 182 | extern ssize_t uwb_ack_policy_store(struct wlp_options *, | ||
| 183 | const char *, size_t); | ||
| 184 | extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *); | ||
| 185 | extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t); | ||
| 186 | extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *); | ||
| 187 | extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t); | ||
| 188 | extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *); | ||
| 189 | |||
| 190 | |||
| 191 | /** Simple bandwidth allocation (temporary and too simple) */ | ||
| 192 | struct wlp_bw_allocs { | ||
| 193 | const char *name; | ||
| 194 | struct { | ||
| 195 | u8 mask, stream; | ||
| 196 | } tx, rx; | ||
| 197 | }; | ||
| 198 | |||
| 199 | |||
| 200 | #endif /* #ifndef __i1480_wlp_h__ */ | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/Makefile b/drivers/uwb/i1480/i1480u-wlp/Makefile deleted file mode 100644 index fe6709b8e68b..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/Makefile +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o | ||
| 2 | |||
| 3 | i1480u-wlp-objs := \ | ||
| 4 | lc.o \ | ||
| 5 | netdev.o \ | ||
| 6 | rx.o \ | ||
| 7 | sysfs.o \ | ||
| 8 | tx.o | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h deleted file mode 100644 index 2e31f536a347..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h +++ /dev/null | |||
| @@ -1,283 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Intel 1480 Wireless UWB Link USB | ||
| 3 | * Header formats, constants, general internal interfaces | ||
| 4 | * | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License version | ||
| 11 | * 2 as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 21 | * 02110-1301, USA. | ||
| 22 | * | ||
| 23 | * | ||
| 24 | * This is not an standard interface. | ||
| 25 | * | ||
| 26 | * FIXME: docs | ||
| 27 | * | ||
| 28 | * i1480u-wlp is pretty simple: two endpoints, one for tx, one for | ||
| 29 | * rx. rx is polled. Network packets (ethernet, whatever) are wrapped | ||
| 30 | * in i1480 TX or RX headers (for sending over the air), and these | ||
| 31 | * packets are wrapped in UNTD headers (for sending to the WLP UWB | ||
| 32 | * controller). | ||
| 33 | * | ||
| 34 | * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets | ||
| 35 | * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the | ||
| 36 | * i1480 packet is broken in chunks/packets: | ||
| 37 | * | ||
| 38 | * UNTD-1st.hdr + i1480.hdr + payload | ||
| 39 | * UNTD-next.hdr + payload | ||
| 40 | * ... | ||
| 41 | * UNTD-last.hdr + payload | ||
| 42 | * | ||
| 43 | * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE. | ||
| 44 | * | ||
| 45 | * All HW structures and bitmaps are little endian, so we need to play | ||
| 46 | * ugly tricks when defining bitfields. Hoping for the day GCC | ||
| 47 | * implements __attribute__((endian(1234))). | ||
| 48 | * | ||
| 49 | * FIXME: ROADMAP to the whole implementation | ||
| 50 | */ | ||
| 51 | |||
| 52 | #ifndef __i1480u_wlp_h__ | ||
| 53 | #define __i1480u_wlp_h__ | ||
| 54 | |||
| 55 | #include <linux/usb.h> | ||
| 56 | #include <linux/netdevice.h> | ||
| 57 | #include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */ | ||
| 58 | #include <linux/wlp.h> | ||
| 59 | #include "../i1480-wlp.h" | ||
| 60 | |||
| 61 | #undef i1480u_FLOW_CONTROL /* Enable flow control code */ | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Basic flow control | ||
| 65 | */ | ||
| 66 | enum { | ||
| 67 | i1480u_TX_INFLIGHT_MAX = 1000, | ||
| 68 | i1480u_TX_INFLIGHT_THRESHOLD = 100, | ||
| 69 | }; | ||
| 70 | |||
| 71 | /** Maximum size of a transaction that we can tx/rx */ | ||
| 72 | enum { | ||
| 73 | /* Maximum packet size computed as follows: max UNTD header (8) + | ||
| 74 | * i1480 RX header (8) + max Ethernet header and payload (4096) + | ||
| 75 | * Padding added by skb_reserve (2) to make post Ethernet payload | ||
| 76 | * start on 16 byte boundary*/ | ||
| 77 | i1480u_MAX_RX_PKT_SIZE = 4114, | ||
| 78 | i1480u_MAX_FRG_SIZE = 512, | ||
| 79 | i1480u_RX_BUFS = 9, | ||
| 80 | }; | ||
| 81 | |||
| 82 | |||
| 83 | /** | ||
| 84 | * UNTD packet type | ||
| 85 | * | ||
| 86 | * We need to fragment any payload whose UNTD packet is going to be | ||
| 87 | * bigger than i1480u_MAX_FRG_SIZE. | ||
| 88 | */ | ||
| 89 | enum i1480u_pkt_type { | ||
| 90 | i1480u_PKT_FRAG_1ST = 0x1, | ||
| 91 | i1480u_PKT_FRAG_NXT = 0x0, | ||
| 92 | i1480u_PKT_FRAG_LST = 0x2, | ||
| 93 | i1480u_PKT_FRAG_CMP = 0x3 | ||
| 94 | }; | ||
| 95 | enum { | ||
| 96 | i1480u_PKT_NONE = 0x4, | ||
| 97 | }; | ||
| 98 | |||
| 99 | /** USB Network Transfer Descriptor - common */ | ||
| 100 | struct untd_hdr { | ||
| 101 | u8 type; | ||
| 102 | __le16 len; | ||
| 103 | } __attribute__((packed)); | ||
| 104 | |||
| 105 | static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr) | ||
| 106 | { | ||
| 107 | return hdr->type & 0x03; | ||
| 108 | } | ||
| 109 | |||
| 110 | static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr) | ||
| 111 | { | ||
| 112 | return (hdr->type >> 2) & 0x01; | ||
| 113 | } | ||
| 114 | |||
| 115 | static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type) | ||
| 116 | { | ||
| 117 | hdr->type = (hdr->type & ~0x03) | type; | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx) | ||
| 121 | { | ||
| 122 | hdr->type = (hdr->type & ~0x04) | (rx_tx << 2); | ||
| 123 | } | ||
| 124 | |||
| 125 | |||
| 126 | /** | ||
| 127 | * USB Network Transfer Descriptor - Complete Packet | ||
| 128 | * | ||
| 129 | * This is for a packet that is smaller (header + payload) than | ||
| 130 | * i1480u_MAX_FRG_SIZE. | ||
| 131 | * | ||
| 132 | * @hdr.total_len is the size of the payload; the payload doesn't | ||
| 133 | * count this header nor the padding, but includes the size of i1480 | ||
| 134 | * header. | ||
| 135 | */ | ||
| 136 | struct untd_hdr_cmp { | ||
| 137 | struct untd_hdr hdr; | ||
| 138 | u8 padding; | ||
| 139 | } __attribute__((packed)); | ||
| 140 | |||
| 141 | |||
| 142 | /** | ||
| 143 | * USB Network Transfer Descriptor - First fragment | ||
| 144 | * | ||
| 145 | * @hdr.len is the size of the *whole packet* (excluding UNTD | ||
| 146 | * headers); @fragment_len is the size of the payload (excluding UNTD | ||
| 147 | * headers, but including i1480 headers). | ||
| 148 | */ | ||
| 149 | struct untd_hdr_1st { | ||
| 150 | struct untd_hdr hdr; | ||
| 151 | __le16 fragment_len; | ||
| 152 | u8 padding[3]; | ||
| 153 | } __attribute__((packed)); | ||
| 154 | |||
| 155 | |||
| 156 | /** | ||
| 157 | * USB Network Transfer Descriptor - Next / Last [Rest] | ||
| 158 | * | ||
| 159 | * @hdr.len is the size of the payload, not including headrs. | ||
| 160 | */ | ||
| 161 | struct untd_hdr_rst { | ||
| 162 | struct untd_hdr hdr; | ||
| 163 | u8 padding; | ||
| 164 | } __attribute__((packed)); | ||
| 165 | |||
| 166 | |||
| 167 | /** | ||
| 168 | * Transmission context | ||
| 169 | * | ||
| 170 | * Wraps all the stuff needed to track a pending/active tx | ||
| 171 | * operation. | ||
| 172 | */ | ||
| 173 | struct i1480u_tx { | ||
| 174 | struct list_head list_node; | ||
| 175 | struct i1480u *i1480u; | ||
| 176 | struct urb *urb; | ||
| 177 | |||
| 178 | struct sk_buff *skb; | ||
| 179 | struct wlp_tx_hdr *wlp_tx_hdr; | ||
| 180 | |||
| 181 | void *buf; /* if NULL, no new buf was used */ | ||
| 182 | size_t buf_size; | ||
| 183 | }; | ||
| 184 | |||
| 185 | /** | ||
| 186 | * Basic flow control | ||
| 187 | * | ||
| 188 | * We maintain a basic flow control counter. "count" how many TX URBs are | ||
| 189 | * outstanding. Only allow "max" | ||
| 190 | * TX URBs to be outstanding. If this value is reached the queue will be | ||
| 191 | * stopped. The queue will be restarted when there are | ||
| 192 | * "threshold" URBs outstanding. | ||
| 193 | * Maintain a counter of how many time the TX queue needed to be restarted | ||
| 194 | * due to the "max" being exceeded and the "threshold" reached again. The | ||
| 195 | * timestamp "restart_ts" is to keep track from when the counter was last | ||
| 196 | * queried (see sysfs handling of file wlp_tx_inflight). | ||
| 197 | */ | ||
| 198 | struct i1480u_tx_inflight { | ||
| 199 | atomic_t count; | ||
| 200 | unsigned long max; | ||
| 201 | unsigned long threshold; | ||
| 202 | unsigned long restart_ts; | ||
| 203 | atomic_t restart_count; | ||
| 204 | }; | ||
| 205 | |||
| 206 | /** | ||
| 207 | * Instance of a i1480u WLP interface | ||
| 208 | * | ||
| 209 | * Keeps references to the USB device that wraps it, as well as it's | ||
| 210 | * interface and associated UWB host controller. As well, it also | ||
| 211 | * keeps a link to the netdevice for integration into the networking | ||
| 212 | * stack. | ||
| 213 | * We maintian separate error history for the tx and rx endpoints because | ||
| 214 | * the implementation does not rely on locking - having one shared | ||
| 215 | * structure between endpoints may cause problems. Adding locking to the | ||
| 216 | * implementation will have higher cost than adding a separate structure. | ||
| 217 | */ | ||
| 218 | struct i1480u { | ||
| 219 | struct usb_device *usb_dev; | ||
| 220 | struct usb_interface *usb_iface; | ||
| 221 | struct net_device *net_dev; | ||
| 222 | |||
| 223 | spinlock_t lock; | ||
| 224 | |||
| 225 | /* RX context handling */ | ||
| 226 | struct sk_buff *rx_skb; | ||
| 227 | struct uwb_dev_addr rx_srcaddr; | ||
| 228 | size_t rx_untd_pkt_size; | ||
| 229 | struct i1480u_rx_buf { | ||
| 230 | struct i1480u *i1480u; /* back pointer */ | ||
| 231 | struct urb *urb; | ||
| 232 | struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */ | ||
| 233 | } rx_buf[i1480u_RX_BUFS]; /* N bufs */ | ||
| 234 | |||
| 235 | spinlock_t tx_list_lock; /* TX context */ | ||
| 236 | struct list_head tx_list; | ||
| 237 | u8 tx_stream; | ||
| 238 | |||
| 239 | struct stats lqe_stats, rssi_stats; /* radio statistics */ | ||
| 240 | |||
| 241 | /* Options we can set from sysfs */ | ||
| 242 | struct wlp_options options; | ||
| 243 | struct uwb_notifs_handler uwb_notifs_handler; | ||
| 244 | struct edc tx_errors; | ||
| 245 | struct edc rx_errors; | ||
| 246 | struct wlp wlp; | ||
| 247 | #ifdef i1480u_FLOW_CONTROL | ||
| 248 | struct urb *notif_urb; | ||
| 249 | struct edc notif_edc; /* error density counter */ | ||
| 250 | u8 notif_buffer[1]; | ||
| 251 | #endif | ||
| 252 | struct i1480u_tx_inflight tx_inflight; | ||
| 253 | }; | ||
| 254 | |||
| 255 | /* Internal interfaces */ | ||
| 256 | extern void i1480u_rx_cb(struct urb *urb); | ||
| 257 | extern int i1480u_rx_setup(struct i1480u *); | ||
| 258 | extern void i1480u_rx_release(struct i1480u *); | ||
| 259 | extern void i1480u_tx_release(struct i1480u *); | ||
| 260 | extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *, | ||
| 261 | struct uwb_dev_addr *); | ||
| 262 | extern void i1480u_stop_queue(struct wlp *); | ||
| 263 | extern void i1480u_start_queue(struct wlp *); | ||
| 264 | extern int i1480u_sysfs_setup(struct i1480u *); | ||
| 265 | extern void i1480u_sysfs_release(struct i1480u *); | ||
| 266 | |||
| 267 | /* netdev interface */ | ||
| 268 | extern int i1480u_open(struct net_device *); | ||
| 269 | extern int i1480u_stop(struct net_device *); | ||
| 270 | extern netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *, | ||
| 271 | struct net_device *); | ||
| 272 | extern void i1480u_tx_timeout(struct net_device *); | ||
| 273 | extern int i1480u_set_config(struct net_device *, struct ifmap *); | ||
| 274 | extern int i1480u_change_mtu(struct net_device *, int); | ||
| 275 | extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs); | ||
| 276 | |||
| 277 | /* bandwidth allocation callback */ | ||
| 278 | extern void i1480u_bw_alloc_cb(struct uwb_rsv *); | ||
| 279 | |||
| 280 | /* Sys FS */ | ||
| 281 | extern struct attribute_group i1480u_wlp_attr_group; | ||
| 282 | |||
| 283 | #endif /* #ifndef __i1480u_wlp_h__ */ | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c deleted file mode 100644 index def778cf2216..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/lc.c +++ /dev/null | |||
| @@ -1,424 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Driver for the Linux Network stack. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: docs | ||
| 24 | * | ||
| 25 | * This implements a very simple network driver for the WLP USB | ||
| 26 | * device that is associated to a UWB (Ultra Wide Band) host. | ||
| 27 | * | ||
| 28 | * This is seen as an interface of a composite device. Once the UWB | ||
| 29 | * host has an association to another WLP capable device, the | ||
| 30 | * networking interface (aka WLP) can start to send packets back and | ||
| 31 | * forth. | ||
| 32 | * | ||
| 33 | * Limitations: | ||
| 34 | * | ||
| 35 | * - Hand cranked; can't ifup the interface until there is an association | ||
| 36 | * | ||
| 37 | * - BW allocation very simplistic [see i1480u_mas_set() and callees]. | ||
| 38 | * | ||
| 39 | * | ||
| 40 | * ROADMAP: | ||
| 41 | * | ||
| 42 | * ENTRY POINTS (driver model): | ||
| 43 | * | ||
| 44 | * i1480u_driver_{exit,init}(): initialization of the driver. | ||
| 45 | * | ||
| 46 | * i1480u_probe(): called by the driver code when a device | ||
| 47 | * matching 'i1480u_id_table' is connected. | ||
| 48 | * | ||
| 49 | * This allocs a netdev instance, inits with | ||
| 50 | * i1480u_add(), then registers_netdev(). | ||
| 51 | * i1480u_init() | ||
| 52 | * i1480u_add() | ||
| 53 | * | ||
| 54 | * i1480u_disconnect(): device has been disconnected/module | ||
| 55 | * is being removed. | ||
| 56 | * i1480u_rm() | ||
| 57 | */ | ||
| 58 | #include <linux/gfp.h> | ||
| 59 | #include <linux/if_arp.h> | ||
| 60 | #include <linux/etherdevice.h> | ||
| 61 | |||
| 62 | #include "i1480u-wlp.h" | ||
| 63 | |||
| 64 | |||
| 65 | |||
| 66 | static inline | ||
| 67 | void i1480u_init(struct i1480u *i1480u) | ||
| 68 | { | ||
| 69 | /* nothing so far... doesn't it suck? */ | ||
| 70 | spin_lock_init(&i1480u->lock); | ||
| 71 | INIT_LIST_HEAD(&i1480u->tx_list); | ||
| 72 | spin_lock_init(&i1480u->tx_list_lock); | ||
| 73 | wlp_options_init(&i1480u->options); | ||
| 74 | edc_init(&i1480u->tx_errors); | ||
| 75 | edc_init(&i1480u->rx_errors); | ||
| 76 | #ifdef i1480u_FLOW_CONTROL | ||
| 77 | edc_init(&i1480u->notif_edc); | ||
| 78 | #endif | ||
| 79 | stats_init(&i1480u->lqe_stats); | ||
| 80 | stats_init(&i1480u->rssi_stats); | ||
| 81 | wlp_init(&i1480u->wlp); | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Fill WLP device information structure | ||
| 86 | * | ||
| 87 | * The structure will contain a few character arrays, each ending with a | ||
| 88 | * null terminated string. Each string has to fit (excluding terminating | ||
| 89 | * character) into a specified range obtained from the WLP substack. | ||
| 90 | * | ||
| 91 | * It is still not clear exactly how this device information should be | ||
| 92 | * obtained. Until we find out we use the USB device descriptor as backup, some | ||
| 93 | * information elements have intuitive mappings, other not. | ||
| 94 | */ | ||
| 95 | static | ||
| 96 | void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info) | ||
| 97 | { | ||
| 98 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
| 99 | struct usb_device *usb_dev = i1480u->usb_dev; | ||
| 100 | /* Treat device name and model name the same */ | ||
| 101 | if (usb_dev->descriptor.iProduct) { | ||
| 102 | usb_string(usb_dev, usb_dev->descriptor.iProduct, | ||
| 103 | dev_info->name, sizeof(dev_info->name)); | ||
| 104 | usb_string(usb_dev, usb_dev->descriptor.iProduct, | ||
| 105 | dev_info->model_name, sizeof(dev_info->model_name)); | ||
| 106 | } | ||
| 107 | if (usb_dev->descriptor.iManufacturer) | ||
| 108 | usb_string(usb_dev, usb_dev->descriptor.iManufacturer, | ||
| 109 | dev_info->manufacturer, | ||
| 110 | sizeof(dev_info->manufacturer)); | ||
| 111 | scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x", | ||
| 112 | __le16_to_cpu(usb_dev->descriptor.bcdDevice)); | ||
| 113 | if (usb_dev->descriptor.iSerialNumber) | ||
| 114 | usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, | ||
| 115 | dev_info->serial, sizeof(dev_info->serial)); | ||
| 116 | /* FIXME: where should we obtain category? */ | ||
| 117 | dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER); | ||
| 118 | /* FIXME: Complete OUI and OUIsubdiv attributes */ | ||
| 119 | } | ||
| 120 | |||
| 121 | #ifdef i1480u_FLOW_CONTROL | ||
| 122 | /** | ||
| 123 | * Callback for the notification endpoint | ||
| 124 | * | ||
| 125 | * This mostly controls the xon/xoff protocol. In case of hard error, | ||
| 126 | * we stop the queue. If not, we always retry. | ||
| 127 | */ | ||
| 128 | static | ||
| 129 | void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs) | ||
| 130 | { | ||
| 131 | struct i1480u *i1480u = urb->context; | ||
| 132 | struct usb_interface *usb_iface = i1480u->usb_iface; | ||
| 133 | struct device *dev = &usb_iface->dev; | ||
| 134 | int result; | ||
| 135 | |||
| 136 | switch (urb->status) { | ||
| 137 | case 0: /* Got valid data, do xon/xoff */ | ||
| 138 | switch (i1480u->notif_buffer[0]) { | ||
| 139 | case 'N': | ||
| 140 | dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies); | ||
| 141 | netif_stop_queue(i1480u->net_dev); | ||
| 142 | break; | ||
| 143 | case 'A': | ||
| 144 | dev_err(dev, "XON STARTING queue at %lu\n", jiffies); | ||
| 145 | netif_start_queue(i1480u->net_dev); | ||
| 146 | break; | ||
| 147 | default: | ||
| 148 | dev_err(dev, "NEP: unknown data 0x%02hhx\n", | ||
| 149 | i1480u->notif_buffer[0]); | ||
| 150 | } | ||
| 151 | break; | ||
| 152 | case -ECONNRESET: /* Controlled situation ... */ | ||
| 153 | case -ENOENT: /* we killed the URB... */ | ||
| 154 | dev_err(dev, "NEP: URB reset/noent %d\n", urb->status); | ||
| 155 | goto error; | ||
| 156 | case -ESHUTDOWN: /* going away! */ | ||
| 157 | dev_err(dev, "NEP: URB down %d\n", urb->status); | ||
| 158 | goto error; | ||
| 159 | default: /* Retry unless it gets ugly */ | ||
| 160 | if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS, | ||
| 161 | EDC_ERROR_TIMEFRAME)) { | ||
| 162 | dev_err(dev, "NEP: URB max acceptable errors " | ||
| 163 | "exceeded; resetting device\n"); | ||
| 164 | goto error_reset; | ||
| 165 | } | ||
| 166 | dev_err(dev, "NEP: URB error %d\n", urb->status); | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
| 170 | if (result < 0) { | ||
| 171 | dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n", | ||
| 172 | result); | ||
| 173 | goto error_reset; | ||
| 174 | } | ||
| 175 | return; | ||
| 176 | |||
| 177 | error_reset: | ||
| 178 | wlp_reset_all(&i1480-wlp); | ||
| 179 | error: | ||
| 180 | netif_stop_queue(i1480u->net_dev); | ||
| 181 | return; | ||
| 182 | } | ||
| 183 | #endif | ||
| 184 | |||
| 185 | static const struct net_device_ops i1480u_netdev_ops = { | ||
| 186 | .ndo_open = i1480u_open, | ||
| 187 | .ndo_stop = i1480u_stop, | ||
| 188 | .ndo_start_xmit = i1480u_hard_start_xmit, | ||
| 189 | .ndo_tx_timeout = i1480u_tx_timeout, | ||
| 190 | .ndo_set_config = i1480u_set_config, | ||
| 191 | .ndo_change_mtu = i1480u_change_mtu, | ||
| 192 | }; | ||
| 193 | |||
| 194 | static | ||
| 195 | int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) | ||
| 196 | { | ||
| 197 | int result = -ENODEV; | ||
| 198 | struct wlp *wlp = &i1480u->wlp; | ||
| 199 | struct usb_device *usb_dev = interface_to_usbdev(iface); | ||
| 200 | struct net_device *net_dev = i1480u->net_dev; | ||
| 201 | struct uwb_rc *rc; | ||
| 202 | struct uwb_dev *uwb_dev; | ||
| 203 | #ifdef i1480u_FLOW_CONTROL | ||
| 204 | struct usb_endpoint_descriptor *epd; | ||
| 205 | #endif | ||
| 206 | |||
| 207 | i1480u->usb_dev = usb_get_dev(usb_dev); | ||
| 208 | i1480u->usb_iface = iface; | ||
| 209 | rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev); | ||
| 210 | if (rc == NULL) { | ||
| 211 | dev_err(&iface->dev, "Cannot get associated UWB Radio " | ||
| 212 | "Controller\n"); | ||
| 213 | goto out; | ||
| 214 | } | ||
| 215 | wlp->xmit_frame = i1480u_xmit_frame; | ||
| 216 | wlp->fill_device_info = i1480u_fill_device_info; | ||
| 217 | wlp->stop_queue = i1480u_stop_queue; | ||
| 218 | wlp->start_queue = i1480u_start_queue; | ||
| 219 | result = wlp_setup(wlp, rc, net_dev); | ||
| 220 | if (result < 0) { | ||
| 221 | dev_err(&iface->dev, "Cannot setup WLP\n"); | ||
| 222 | goto error_wlp_setup; | ||
| 223 | } | ||
| 224 | result = 0; | ||
| 225 | ether_setup(net_dev); /* make it an etherdevice */ | ||
| 226 | uwb_dev = &rc->uwb_dev; | ||
| 227 | /* FIXME: hookup address change notifications? */ | ||
| 228 | |||
| 229 | memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data, | ||
| 230 | sizeof(net_dev->dev_addr)); | ||
| 231 | |||
| 232 | net_dev->hard_header_len = sizeof(struct untd_hdr_cmp) | ||
| 233 | + sizeof(struct wlp_tx_hdr) | ||
| 234 | + WLP_DATA_HLEN | ||
| 235 | + ETH_HLEN; | ||
| 236 | net_dev->mtu = 3500; | ||
| 237 | net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */ | ||
| 238 | |||
| 239 | /* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */ | ||
| 240 | /* FIXME: multicast disabled */ | ||
| 241 | net_dev->flags &= ~IFF_MULTICAST; | ||
| 242 | net_dev->features &= ~NETIF_F_SG; | ||
| 243 | net_dev->features &= ~NETIF_F_FRAGLIST; | ||
| 244 | /* All NETIF_F_*_CSUM disabled */ | ||
| 245 | net_dev->features |= NETIF_F_HIGHDMA; | ||
| 246 | net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */ | ||
| 247 | |||
| 248 | net_dev->netdev_ops = &i1480u_netdev_ops; | ||
| 249 | |||
| 250 | #ifdef i1480u_FLOW_CONTROL | ||
| 251 | /* Notification endpoint setup (submitted when we open the device) */ | ||
| 252 | i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
| 253 | if (i1480u->notif_urb == NULL) { | ||
| 254 | dev_err(&iface->dev, "Unable to allocate notification URB\n"); | ||
| 255 | result = -ENOMEM; | ||
| 256 | goto error_urb_alloc; | ||
| 257 | } | ||
| 258 | epd = &iface->cur_altsetting->endpoint[0].desc; | ||
| 259 | usb_fill_int_urb(i1480u->notif_urb, usb_dev, | ||
| 260 | usb_rcvintpipe(usb_dev, epd->bEndpointAddress), | ||
| 261 | i1480u->notif_buffer, sizeof(i1480u->notif_buffer), | ||
| 262 | i1480u_notif_cb, i1480u, epd->bInterval); | ||
| 263 | |||
| 264 | #endif | ||
| 265 | |||
| 266 | i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX; | ||
| 267 | i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; | ||
| 268 | i1480u->tx_inflight.restart_ts = jiffies; | ||
| 269 | usb_set_intfdata(iface, i1480u); | ||
| 270 | return result; | ||
| 271 | |||
| 272 | #ifdef i1480u_FLOW_CONTROL | ||
| 273 | error_urb_alloc: | ||
| 274 | #endif | ||
| 275 | wlp_remove(wlp); | ||
| 276 | error_wlp_setup: | ||
| 277 | uwb_rc_put(rc); | ||
| 278 | out: | ||
| 279 | usb_put_dev(i1480u->usb_dev); | ||
| 280 | return result; | ||
| 281 | } | ||
| 282 | |||
| 283 | static void i1480u_rm(struct i1480u *i1480u) | ||
| 284 | { | ||
| 285 | struct uwb_rc *rc = i1480u->wlp.rc; | ||
| 286 | usb_set_intfdata(i1480u->usb_iface, NULL); | ||
| 287 | #ifdef i1480u_FLOW_CONTROL | ||
| 288 | usb_kill_urb(i1480u->notif_urb); | ||
| 289 | usb_free_urb(i1480u->notif_urb); | ||
| 290 | #endif | ||
| 291 | wlp_remove(&i1480u->wlp); | ||
| 292 | uwb_rc_put(rc); | ||
| 293 | usb_put_dev(i1480u->usb_dev); | ||
| 294 | } | ||
| 295 | |||
| 296 | /** Just setup @net_dev's i1480u private data */ | ||
| 297 | static void i1480u_netdev_setup(struct net_device *net_dev) | ||
| 298 | { | ||
| 299 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 300 | /* Initialize @i1480u */ | ||
| 301 | memset(i1480u, 0, sizeof(*i1480u)); | ||
| 302 | i1480u_init(i1480u); | ||
| 303 | } | ||
| 304 | |||
| 305 | /** | ||
| 306 | * Probe a i1480u interface and register it | ||
| 307 | * | ||
| 308 | * @iface: USB interface to link to | ||
| 309 | * @id: USB class/subclass/protocol id | ||
| 310 | * @returns: 0 if ok, < 0 errno code on error. | ||
| 311 | * | ||
| 312 | * Does basic housekeeping stuff and then allocs a netdev with space | ||
| 313 | * for the i1480u data. Initializes, registers in i1480u, registers in | ||
| 314 | * netdev, ready to go. | ||
| 315 | */ | ||
| 316 | static int i1480u_probe(struct usb_interface *iface, | ||
| 317 | const struct usb_device_id *id) | ||
| 318 | { | ||
| 319 | int result; | ||
| 320 | struct net_device *net_dev; | ||
| 321 | struct device *dev = &iface->dev; | ||
| 322 | struct i1480u *i1480u; | ||
| 323 | |||
| 324 | /* Allocate instance [calls i1480u_netdev_setup() on it] */ | ||
| 325 | result = -ENOMEM; | ||
| 326 | net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup); | ||
| 327 | if (net_dev == NULL) { | ||
| 328 | dev_err(dev, "no memory for network device instance\n"); | ||
| 329 | goto error_alloc_netdev; | ||
| 330 | } | ||
| 331 | SET_NETDEV_DEV(net_dev, dev); | ||
| 332 | i1480u = netdev_priv(net_dev); | ||
| 333 | i1480u->net_dev = net_dev; | ||
| 334 | result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */ | ||
| 335 | if (result < 0) { | ||
| 336 | dev_err(dev, "cannot add i1480u device: %d\n", result); | ||
| 337 | goto error_i1480u_add; | ||
| 338 | } | ||
| 339 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ | ||
| 340 | if (result < 0) { | ||
| 341 | dev_err(dev, "cannot register network device: %d\n", result); | ||
| 342 | goto error_register_netdev; | ||
| 343 | } | ||
| 344 | i1480u_sysfs_setup(i1480u); | ||
| 345 | if (result < 0) | ||
| 346 | goto error_sysfs_init; | ||
| 347 | return 0; | ||
| 348 | |||
| 349 | error_sysfs_init: | ||
| 350 | unregister_netdev(net_dev); | ||
| 351 | error_register_netdev: | ||
| 352 | i1480u_rm(i1480u); | ||
| 353 | error_i1480u_add: | ||
| 354 | free_netdev(net_dev); | ||
| 355 | error_alloc_netdev: | ||
| 356 | return result; | ||
| 357 | } | ||
| 358 | |||
| 359 | |||
| 360 | /** | ||
| 361 | * Disconect a i1480u from the system. | ||
| 362 | * | ||
| 363 | * i1480u_stop() has been called before, so al the rx and tx contexts | ||
| 364 | * have been taken down already. Make sure the queue is stopped, | ||
| 365 | * unregister netdev and i1480u, free and kill. | ||
| 366 | */ | ||
| 367 | static void i1480u_disconnect(struct usb_interface *iface) | ||
| 368 | { | ||
| 369 | struct i1480u *i1480u; | ||
| 370 | struct net_device *net_dev; | ||
| 371 | |||
| 372 | i1480u = usb_get_intfdata(iface); | ||
| 373 | net_dev = i1480u->net_dev; | ||
| 374 | netif_stop_queue(net_dev); | ||
| 375 | #ifdef i1480u_FLOW_CONTROL | ||
| 376 | usb_kill_urb(i1480u->notif_urb); | ||
| 377 | #endif | ||
| 378 | i1480u_sysfs_release(i1480u); | ||
| 379 | unregister_netdev(net_dev); | ||
| 380 | i1480u_rm(i1480u); | ||
| 381 | free_netdev(net_dev); | ||
| 382 | } | ||
| 383 | |||
| 384 | static struct usb_device_id i1480u_id_table[] = { | ||
| 385 | { | ||
| 386 | .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ | ||
| 387 | | USB_DEVICE_ID_MATCH_DEV_INFO \ | ||
| 388 | | USB_DEVICE_ID_MATCH_INT_INFO, | ||
| 389 | .idVendor = 0x8086, | ||
| 390 | .idProduct = 0x0c3b, | ||
| 391 | .bDeviceClass = 0xef, | ||
| 392 | .bDeviceSubClass = 0x02, | ||
| 393 | .bDeviceProtocol = 0x02, | ||
| 394 | .bInterfaceClass = 0xff, | ||
| 395 | .bInterfaceSubClass = 0xff, | ||
| 396 | .bInterfaceProtocol = 0xff, | ||
| 397 | }, | ||
| 398 | {}, | ||
| 399 | }; | ||
| 400 | MODULE_DEVICE_TABLE(usb, i1480u_id_table); | ||
| 401 | |||
| 402 | static struct usb_driver i1480u_driver = { | ||
| 403 | .name = KBUILD_MODNAME, | ||
| 404 | .probe = i1480u_probe, | ||
| 405 | .disconnect = i1480u_disconnect, | ||
| 406 | .id_table = i1480u_id_table, | ||
| 407 | }; | ||
| 408 | |||
| 409 | static int __init i1480u_driver_init(void) | ||
| 410 | { | ||
| 411 | return usb_register(&i1480u_driver); | ||
| 412 | } | ||
| 413 | module_init(i1480u_driver_init); | ||
| 414 | |||
| 415 | |||
| 416 | static void __exit i1480u_driver_exit(void) | ||
| 417 | { | ||
| 418 | usb_deregister(&i1480u_driver); | ||
| 419 | } | ||
| 420 | module_exit(i1480u_driver_exit); | ||
| 421 | |||
| 422 | MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); | ||
| 423 | MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB"); | ||
| 424 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c deleted file mode 100644 index f98f6ce8b9e7..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ /dev/null | |||
| @@ -1,331 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Driver for the Linux Network stack. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: docs | ||
| 24 | * | ||
| 25 | * Implementation of the netdevice linkage (except tx and rx related stuff). | ||
| 26 | * | ||
| 27 | * ROADMAP: | ||
| 28 | * | ||
| 29 | * ENTRY POINTS (Net device): | ||
| 30 | * | ||
| 31 | * i1480u_open(): Called when we ifconfig up the interface; | ||
| 32 | * associates to a UWB host controller, reserves | ||
| 33 | * bandwidth (MAS), sets up RX USB URB and starts | ||
| 34 | * the queue. | ||
| 35 | * | ||
| 36 | * i1480u_stop(): Called when we ifconfig down a interface; | ||
| 37 | * reverses _open(). | ||
| 38 | * | ||
| 39 | * i1480u_set_config(): | ||
| 40 | */ | ||
| 41 | |||
| 42 | #include <linux/slab.h> | ||
| 43 | #include <linux/if_arp.h> | ||
| 44 | #include <linux/etherdevice.h> | ||
| 45 | |||
| 46 | #include "i1480u-wlp.h" | ||
| 47 | |||
| 48 | struct i1480u_cmd_set_ip_mas { | ||
| 49 | struct uwb_rccb rccb; | ||
| 50 | struct uwb_dev_addr addr; | ||
| 51 | u8 stream; | ||
| 52 | u8 owner; | ||
| 53 | u8 type; /* enum uwb_drp_type */ | ||
| 54 | u8 baMAS[32]; | ||
| 55 | } __attribute__((packed)); | ||
| 56 | |||
| 57 | |||
| 58 | static | ||
| 59 | int i1480u_set_ip_mas( | ||
| 60 | struct uwb_rc *rc, | ||
| 61 | const struct uwb_dev_addr *dstaddr, | ||
| 62 | u8 stream, u8 owner, u8 type, unsigned long *mas) | ||
| 63 | { | ||
| 64 | |||
| 65 | int result; | ||
| 66 | struct i1480u_cmd_set_ip_mas *cmd; | ||
| 67 | struct uwb_rc_evt_confirm reply; | ||
| 68 | |||
| 69 | result = -ENOMEM; | ||
| 70 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
| 71 | if (cmd == NULL) | ||
| 72 | goto error_kzalloc; | ||
| 73 | cmd->rccb.bCommandType = 0xfd; | ||
| 74 | cmd->rccb.wCommand = cpu_to_le16(0x000e); | ||
| 75 | cmd->addr = *dstaddr; | ||
| 76 | cmd->stream = stream; | ||
| 77 | cmd->owner = owner; | ||
| 78 | cmd->type = type; | ||
| 79 | if (mas == NULL) | ||
| 80 | memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS)); | ||
| 81 | else | ||
| 82 | memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS)); | ||
| 83 | reply.rceb.bEventType = 0xfd; | ||
| 84 | reply.rceb.wEvent = cpu_to_le16(0x000e); | ||
| 85 | result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd), | ||
| 86 | &reply.rceb, sizeof(reply)); | ||
| 87 | if (result < 0) | ||
| 88 | goto error_cmd; | ||
| 89 | if (reply.bResultCode != UWB_RC_RES_FAIL) { | ||
| 90 | dev_err(&rc->uwb_dev.dev, | ||
| 91 | "SET-IP-MAS: command execution failed: %d\n", | ||
| 92 | reply.bResultCode); | ||
| 93 | result = -EIO; | ||
| 94 | } | ||
| 95 | error_cmd: | ||
| 96 | kfree(cmd); | ||
| 97 | error_kzalloc: | ||
| 98 | return result; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Inform a WLP interface of a MAS reservation | ||
| 103 | * | ||
| 104 | * @rc is assumed refcnted. | ||
| 105 | */ | ||
| 106 | /* FIXME: detect if remote device is WLP capable? */ | ||
| 107 | static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc, | ||
| 108 | u8 stream, u8 owner, u8 type, unsigned long *mas) | ||
| 109 | { | ||
| 110 | int result = 0; | ||
| 111 | struct device *dev = &rc->uwb_dev.dev; | ||
| 112 | |||
| 113 | result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner, | ||
| 114 | type, mas); | ||
| 115 | if (result < 0) { | ||
| 116 | char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE]; | ||
| 117 | uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf), | ||
| 118 | &rc->uwb_dev.dev_addr); | ||
| 119 | uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf), | ||
| 120 | &uwb_dev->dev_addr); | ||
| 121 | dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n", | ||
| 122 | rcaddrbuf, devaddrbuf, result); | ||
| 123 | } | ||
| 124 | return result; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * Called by bandwidth allocator when change occurs in reservation. | ||
| 129 | * | ||
| 130 | * @rsv: The reservation that is being established, modified, or | ||
| 131 | * terminated. | ||
| 132 | * | ||
| 133 | * When a reservation is established, modified, or terminated the upper layer | ||
| 134 | * (WLP here) needs set/update the currently available Media Access Slots | ||
| 135 | * that can be use for IP traffic. | ||
| 136 | * | ||
| 137 | * Our action taken during failure depends on how the reservation is being | ||
| 138 | * changed: | ||
| 139 | * - if reservation is being established we do nothing if we cannot set the | ||
| 140 | * new MAS to be used | ||
| 141 | * - if reservation is being terminated we revert back to PCA whether the | ||
| 142 | * SET IP MAS command succeeds or not. | ||
| 143 | */ | ||
| 144 | void i1480u_bw_alloc_cb(struct uwb_rsv *rsv) | ||
| 145 | { | ||
| 146 | int result = 0; | ||
| 147 | struct i1480u *i1480u = rsv->pal_priv; | ||
| 148 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 149 | struct uwb_dev *target_dev = rsv->target.dev; | ||
| 150 | struct uwb_rc *rc = i1480u->wlp.rc; | ||
| 151 | u8 stream = rsv->stream; | ||
| 152 | int type = rsv->type; | ||
| 153 | int is_owner = rsv->owner == &rc->uwb_dev; | ||
| 154 | unsigned long *bmp = rsv->mas.bm; | ||
| 155 | |||
| 156 | dev_err(dev, "WLP callback called - sending set ip mas\n"); | ||
| 157 | /*user cannot change options while setting configuration*/ | ||
| 158 | mutex_lock(&i1480u->options.mutex); | ||
| 159 | switch (rsv->state) { | ||
| 160 | case UWB_RSV_STATE_T_ACCEPTED: | ||
| 161 | case UWB_RSV_STATE_O_ESTABLISHED: | ||
| 162 | result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, | ||
| 163 | type, bmp); | ||
| 164 | if (result < 0) { | ||
| 165 | dev_err(dev, "MAS reservation failed: %d\n", result); | ||
| 166 | goto out; | ||
| 167 | } | ||
| 168 | if (is_owner) { | ||
| 169 | wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, | ||
| 170 | WLP_DRP | stream); | ||
| 171 | wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0); | ||
| 172 | } | ||
| 173 | break; | ||
| 174 | case UWB_RSV_STATE_NONE: | ||
| 175 | /* revert back to PCA */ | ||
| 176 | result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, | ||
| 177 | type, bmp); | ||
| 178 | if (result < 0) | ||
| 179 | dev_err(dev, "MAS reservation failed: %d\n", result); | ||
| 180 | /* Revert to PCA even though SET IP MAS failed. */ | ||
| 181 | wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, | ||
| 182 | i1480u->options.pca_base_priority); | ||
| 183 | wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1); | ||
| 184 | break; | ||
| 185 | default: | ||
| 186 | dev_err(dev, "unexpected WLP reservation state: %s (%d).\n", | ||
| 187 | uwb_rsv_state_str(rsv->state), rsv->state); | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | out: | ||
| 191 | mutex_unlock(&i1480u->options.mutex); | ||
| 192 | return; | ||
| 193 | } | ||
| 194 | |||
| 195 | /** | ||
| 196 | * | ||
| 197 | * Called on 'ifconfig up' | ||
| 198 | */ | ||
| 199 | int i1480u_open(struct net_device *net_dev) | ||
| 200 | { | ||
| 201 | int result; | ||
| 202 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 203 | struct wlp *wlp = &i1480u->wlp; | ||
| 204 | struct uwb_rc *rc; | ||
| 205 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 206 | |||
| 207 | rc = wlp->rc; | ||
| 208 | result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ | ||
| 209 | if (result < 0) | ||
| 210 | goto error_rx_setup; | ||
| 211 | |||
| 212 | result = uwb_radio_start(&wlp->pal); | ||
| 213 | if (result < 0) | ||
| 214 | goto error_radio_start; | ||
| 215 | |||
| 216 | netif_wake_queue(net_dev); | ||
| 217 | #ifdef i1480u_FLOW_CONTROL | ||
| 218 | result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); | ||
| 219 | if (result < 0) { | ||
| 220 | dev_err(dev, "Can't submit notification URB: %d\n", result); | ||
| 221 | goto error_notif_urb_submit; | ||
| 222 | } | ||
| 223 | #endif | ||
| 224 | /* Interface is up with an address, now we can create WSS */ | ||
| 225 | result = wlp_wss_setup(net_dev, &wlp->wss); | ||
| 226 | if (result < 0) { | ||
| 227 | dev_err(dev, "Can't create WSS: %d. \n", result); | ||
| 228 | goto error_wss_setup; | ||
| 229 | } | ||
| 230 | return 0; | ||
| 231 | error_wss_setup: | ||
| 232 | #ifdef i1480u_FLOW_CONTROL | ||
| 233 | usb_kill_urb(i1480u->notif_urb); | ||
| 234 | error_notif_urb_submit: | ||
| 235 | #endif | ||
| 236 | uwb_radio_stop(&wlp->pal); | ||
| 237 | error_radio_start: | ||
| 238 | netif_stop_queue(net_dev); | ||
| 239 | i1480u_rx_release(i1480u); | ||
| 240 | error_rx_setup: | ||
| 241 | return result; | ||
| 242 | } | ||
| 243 | |||
| 244 | |||
| 245 | /** | ||
| 246 | * Called on 'ifconfig down' | ||
| 247 | */ | ||
| 248 | int i1480u_stop(struct net_device *net_dev) | ||
| 249 | { | ||
| 250 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 251 | struct wlp *wlp = &i1480u->wlp; | ||
| 252 | |||
| 253 | BUG_ON(wlp->rc == NULL); | ||
| 254 | wlp_wss_remove(&wlp->wss); | ||
| 255 | netif_carrier_off(net_dev); | ||
| 256 | #ifdef i1480u_FLOW_CONTROL | ||
| 257 | usb_kill_urb(i1480u->notif_urb); | ||
| 258 | #endif | ||
| 259 | netif_stop_queue(net_dev); | ||
| 260 | uwb_radio_stop(&wlp->pal); | ||
| 261 | i1480u_rx_release(i1480u); | ||
| 262 | i1480u_tx_release(i1480u); | ||
| 263 | return 0; | ||
| 264 | } | ||
| 265 | |||
| 266 | /** | ||
| 267 | * | ||
| 268 | * Change the interface config--we probably don't have to do anything. | ||
| 269 | */ | ||
| 270 | int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) | ||
| 271 | { | ||
| 272 | int result; | ||
| 273 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 274 | BUG_ON(i1480u->wlp.rc == NULL); | ||
| 275 | result = 0; | ||
| 276 | return result; | ||
| 277 | } | ||
| 278 | |||
| 279 | /** | ||
| 280 | * Change the MTU of the interface | ||
| 281 | */ | ||
| 282 | int i1480u_change_mtu(struct net_device *net_dev, int mtu) | ||
| 283 | { | ||
| 284 | static union { | ||
| 285 | struct wlp_tx_hdr tx; | ||
| 286 | struct wlp_rx_hdr rx; | ||
| 287 | } i1480u_all_hdrs; | ||
| 288 | |||
| 289 | if (mtu < ETH_HLEN) /* We encap eth frames */ | ||
| 290 | return -ERANGE; | ||
| 291 | if (mtu > 4000 - sizeof(i1480u_all_hdrs)) | ||
| 292 | return -ERANGE; | ||
| 293 | net_dev->mtu = mtu; | ||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | /** | ||
| 298 | * Stop the network queue | ||
| 299 | * | ||
| 300 | * Enable WLP substack to stop network queue. We also set the flow control | ||
| 301 | * threshold at this time to prevent the flow control from restarting the | ||
| 302 | * queue. | ||
| 303 | * | ||
| 304 | * we are loosing the current threshold value here ... FIXME? | ||
| 305 | */ | ||
| 306 | void i1480u_stop_queue(struct wlp *wlp) | ||
| 307 | { | ||
| 308 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
| 309 | struct net_device *net_dev = i1480u->net_dev; | ||
| 310 | i1480u->tx_inflight.threshold = 0; | ||
| 311 | netif_stop_queue(net_dev); | ||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * Start the network queue | ||
| 316 | * | ||
| 317 | * Enable WLP substack to start network queue. Also re-enable the flow | ||
| 318 | * control to manage the queue again. | ||
| 319 | * | ||
| 320 | * We re-enable the flow control by storing the default threshold in the | ||
| 321 | * flow control threshold. This means that if the user modified the | ||
| 322 | * threshold before the queue was stopped and restarted that information | ||
| 323 | * will be lost. FIXME? | ||
| 324 | */ | ||
| 325 | void i1480u_start_queue(struct wlp *wlp) | ||
| 326 | { | ||
| 327 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
| 328 | struct net_device *net_dev = i1480u->net_dev; | ||
| 329 | i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; | ||
| 330 | netif_start_queue(net_dev); | ||
| 331 | } | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c deleted file mode 100644 index d4e51e108aa4..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/rx.c +++ /dev/null | |||
| @@ -1,474 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Driver for the Linux Network stack. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * i1480u's RX handling is simple. i1480u will send the received | ||
| 24 | * network packets broken up in fragments; 1 to N fragments make a | ||
| 25 | * packet, we assemble them together and deliver the packet with netif_rx(). | ||
| 26 | * | ||
| 27 | * Beacuse each USB transfer is a *single* fragment (except when the | ||
| 28 | * transfer contains a first fragment), each URB called thus | ||
| 29 | * back contains one or two fragments. So we queue N URBs, each with its own | ||
| 30 | * fragment buffer. When a URB is done, we process it (adding to the | ||
| 31 | * current skb from the fragment buffer until complete). Once | ||
| 32 | * processed, we requeue the URB. There is always a bunch of URBs | ||
| 33 | * ready to take data, so the intergap should be minimal. | ||
| 34 | * | ||
| 35 | * An URB's transfer buffer is the data field of a socket buffer. This | ||
| 36 | * reduces copying as data can be passed directly to network layer. If a | ||
| 37 | * complete packet or 1st fragment is received the URB's transfer buffer is | ||
| 38 | * taken away from it and used to send data to the network layer. In this | ||
| 39 | * case a new transfer buffer is allocated to the URB before being requeued. | ||
| 40 | * If a "NEXT" or "LAST" fragment is received, the fragment contents is | ||
| 41 | * appended to the RX packet under construction and the transfer buffer | ||
| 42 | * is reused. To be able to use this buffer to assemble complete packets | ||
| 43 | * we set each buffer's size to that of the MAX ethernet packet that can | ||
| 44 | * be received. There is thus room for improvement in memory usage. | ||
| 45 | * | ||
| 46 | * When the max tx fragment size increases, we should be able to read | ||
| 47 | * data into the skbs directly with very simple code. | ||
| 48 | * | ||
| 49 | * ROADMAP: | ||
| 50 | * | ||
| 51 | * ENTRY POINTS: | ||
| 52 | * | ||
| 53 | * i1480u_rx_setup(): setup RX context [from i1480u_open()] | ||
| 54 | * | ||
| 55 | * i1480u_rx_release(): release RX context [from i1480u_stop()] | ||
| 56 | * | ||
| 57 | * i1480u_rx_cb(): called when the RX USB URB receives a | ||
| 58 | * packet. It removes the header and pushes it up | ||
| 59 | * the Linux netdev stack with netif_rx(). | ||
| 60 | * | ||
| 61 | * i1480u_rx_buffer() | ||
| 62 | * i1480u_drop() and i1480u_fix() | ||
| 63 | * i1480u_skb_deliver | ||
| 64 | * | ||
| 65 | */ | ||
| 66 | |||
| 67 | #include <linux/gfp.h> | ||
| 68 | #include <linux/netdevice.h> | ||
| 69 | #include <linux/etherdevice.h> | ||
| 70 | #include "i1480u-wlp.h" | ||
| 71 | |||
| 72 | /* | ||
| 73 | * Setup the RX context | ||
| 74 | * | ||
| 75 | * Each URB is provided with a transfer_buffer that is the data field | ||
| 76 | * of a new socket buffer. | ||
| 77 | */ | ||
| 78 | int i1480u_rx_setup(struct i1480u *i1480u) | ||
| 79 | { | ||
| 80 | int result, cnt; | ||
| 81 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 82 | struct net_device *net_dev = i1480u->net_dev; | ||
| 83 | struct usb_endpoint_descriptor *epd; | ||
| 84 | struct sk_buff *skb; | ||
| 85 | |||
| 86 | /* Alloc RX stuff */ | ||
| 87 | i1480u->rx_skb = NULL; /* not in process of receiving packet */ | ||
| 88 | result = -ENOMEM; | ||
| 89 | epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc; | ||
| 90 | for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { | ||
| 91 | struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt]; | ||
| 92 | rx_buf->i1480u = i1480u; | ||
| 93 | skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); | ||
| 94 | if (!skb) { | ||
| 95 | dev_err(dev, | ||
| 96 | "RX: cannot allocate RX buffer %d\n", cnt); | ||
| 97 | result = -ENOMEM; | ||
| 98 | goto error; | ||
| 99 | } | ||
| 100 | skb->dev = net_dev; | ||
| 101 | skb->ip_summed = CHECKSUM_NONE; | ||
| 102 | skb_reserve(skb, 2); | ||
| 103 | rx_buf->data = skb; | ||
| 104 | rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); | ||
| 105 | if (unlikely(rx_buf->urb == NULL)) { | ||
| 106 | dev_err(dev, "RX: cannot allocate URB %d\n", cnt); | ||
| 107 | result = -ENOMEM; | ||
| 108 | goto error; | ||
| 109 | } | ||
| 110 | usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev, | ||
| 111 | usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress), | ||
| 112 | rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2, | ||
| 113 | i1480u_rx_cb, rx_buf); | ||
| 114 | result = usb_submit_urb(rx_buf->urb, GFP_NOIO); | ||
| 115 | if (unlikely(result < 0)) { | ||
| 116 | dev_err(dev, "RX: cannot submit URB %d: %d\n", | ||
| 117 | cnt, result); | ||
| 118 | goto error; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | return 0; | ||
| 122 | |||
| 123 | error: | ||
| 124 | i1480u_rx_release(i1480u); | ||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | /* Release resources associated to the rx context */ | ||
| 130 | void i1480u_rx_release(struct i1480u *i1480u) | ||
| 131 | { | ||
| 132 | int cnt; | ||
| 133 | for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { | ||
| 134 | if (i1480u->rx_buf[cnt].data) | ||
| 135 | dev_kfree_skb(i1480u->rx_buf[cnt].data); | ||
| 136 | if (i1480u->rx_buf[cnt].urb) { | ||
| 137 | usb_kill_urb(i1480u->rx_buf[cnt].urb); | ||
| 138 | usb_free_urb(i1480u->rx_buf[cnt].urb); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | if (i1480u->rx_skb != NULL) | ||
| 142 | dev_kfree_skb(i1480u->rx_skb); | ||
| 143 | } | ||
| 144 | |||
| 145 | static | ||
| 146 | void i1480u_rx_unlink_urbs(struct i1480u *i1480u) | ||
| 147 | { | ||
| 148 | int cnt; | ||
| 149 | for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { | ||
| 150 | if (i1480u->rx_buf[cnt].urb) | ||
| 151 | usb_unlink_urb(i1480u->rx_buf[cnt].urb); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Fix an out-of-sequence packet */ | ||
| 156 | #define i1480u_fix(i1480u, msg...) \ | ||
| 157 | do { \ | ||
| 158 | if (printk_ratelimit()) \ | ||
| 159 | dev_err(&i1480u->usb_iface->dev, msg); \ | ||
| 160 | dev_kfree_skb_irq(i1480u->rx_skb); \ | ||
| 161 | i1480u->rx_skb = NULL; \ | ||
| 162 | i1480u->rx_untd_pkt_size = 0; \ | ||
| 163 | } while (0) | ||
| 164 | |||
| 165 | |||
| 166 | /* Drop an out-of-sequence packet */ | ||
| 167 | #define i1480u_drop(i1480u, msg...) \ | ||
| 168 | do { \ | ||
| 169 | if (printk_ratelimit()) \ | ||
| 170 | dev_err(&i1480u->usb_iface->dev, msg); \ | ||
| 171 | i1480u->net_dev->stats.rx_dropped++; \ | ||
| 172 | } while (0) | ||
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | |||
| 177 | /* Finalizes setting up the SKB and delivers it | ||
| 178 | * | ||
| 179 | * We first pass the incoming frame to WLP substack for verification. It | ||
| 180 | * may also be a WLP association frame in which case WLP will take over the | ||
| 181 | * processing. If WLP does not take it over it will still verify it, if the | ||
| 182 | * frame is invalid the skb will be freed by WLP and we will not continue | ||
| 183 | * parsing. | ||
| 184 | * */ | ||
| 185 | static | ||
| 186 | void i1480u_skb_deliver(struct i1480u *i1480u) | ||
| 187 | { | ||
| 188 | int should_parse; | ||
| 189 | struct net_device *net_dev = i1480u->net_dev; | ||
| 190 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 191 | |||
| 192 | should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb, | ||
| 193 | &i1480u->rx_srcaddr); | ||
| 194 | if (!should_parse) | ||
| 195 | goto out; | ||
| 196 | i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev); | ||
| 197 | net_dev->stats.rx_packets++; | ||
| 198 | net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size; | ||
| 199 | |||
| 200 | netif_rx(i1480u->rx_skb); /* deliver */ | ||
| 201 | out: | ||
| 202 | i1480u->rx_skb = NULL; | ||
| 203 | i1480u->rx_untd_pkt_size = 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | /* | ||
| 208 | * Process a buffer of data received from the USB RX endpoint | ||
| 209 | * | ||
| 210 | * First fragment arrives with next or last fragment. All other fragments | ||
| 211 | * arrive alone. | ||
| 212 | * | ||
| 213 | * /me hates long functions. | ||
| 214 | */ | ||
| 215 | static | ||
| 216 | void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf) | ||
| 217 | { | ||
| 218 | unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */ | ||
| 219 | size_t untd_hdr_size, untd_frg_size; | ||
| 220 | size_t i1480u_hdr_size; | ||
| 221 | struct wlp_rx_hdr *i1480u_hdr = NULL; | ||
| 222 | |||
| 223 | struct i1480u *i1480u = rx_buf->i1480u; | ||
| 224 | struct sk_buff *skb = rx_buf->data; | ||
| 225 | int size_left = rx_buf->urb->actual_length; | ||
| 226 | void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */ | ||
| 227 | struct untd_hdr *untd_hdr; | ||
| 228 | |||
| 229 | struct net_device *net_dev = i1480u->net_dev; | ||
| 230 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 231 | struct sk_buff *new_skb; | ||
| 232 | |||
| 233 | #if 0 | ||
| 234 | dev_fnstart(dev, | ||
| 235 | "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left); | ||
| 236 | dev_err(dev, "RX packet, %zu bytes\n", size_left); | ||
| 237 | dump_bytes(dev, ptr, size_left); | ||
| 238 | #endif | ||
| 239 | i1480u_hdr_size = sizeof(struct wlp_rx_hdr); | ||
| 240 | |||
| 241 | while (size_left > 0) { | ||
| 242 | if (pkt_completed) { | ||
| 243 | i1480u_drop(i1480u, "RX: fragment follows completed" | ||
| 244 | "packet in same buffer. Dropping\n"); | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | untd_hdr = ptr; | ||
| 248 | if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */ | ||
| 249 | i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n"); | ||
| 250 | goto out; | ||
| 251 | } | ||
| 252 | if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */ | ||
| 253 | i1480u_drop(i1480u, "RX: TX bit set! Dropping\n"); | ||
| 254 | goto out; | ||
| 255 | } | ||
| 256 | switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */ | ||
| 257 | case i1480u_PKT_FRAG_1ST: { | ||
| 258 | struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr; | ||
| 259 | dev_dbg(dev, "1st fragment\n"); | ||
| 260 | untd_hdr_size = sizeof(struct untd_hdr_1st); | ||
| 261 | if (i1480u->rx_skb != NULL) | ||
| 262 | i1480u_fix(i1480u, "RX: 1st fragment out of " | ||
| 263 | "sequence! Fixing\n"); | ||
| 264 | if (size_left < untd_hdr_size + i1480u_hdr_size) { | ||
| 265 | i1480u_drop(i1480u, "RX: short 1st fragment! " | ||
| 266 | "Dropping\n"); | ||
| 267 | goto out; | ||
| 268 | } | ||
| 269 | i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len) | ||
| 270 | - i1480u_hdr_size; | ||
| 271 | untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len); | ||
| 272 | if (size_left < untd_hdr_size + untd_frg_size) { | ||
| 273 | i1480u_drop(i1480u, | ||
| 274 | "RX: short payload! Dropping\n"); | ||
| 275 | goto out; | ||
| 276 | } | ||
| 277 | i1480u->rx_skb = skb; | ||
| 278 | i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size; | ||
| 279 | i1480u->rx_srcaddr = i1480u_hdr->srcaddr; | ||
| 280 | skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size); | ||
| 281 | skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); | ||
| 282 | stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); | ||
| 283 | stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); | ||
| 284 | rx_buf->data = NULL; /* need to create new buffer */ | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | case i1480u_PKT_FRAG_NXT: { | ||
| 288 | dev_dbg(dev, "nxt fragment\n"); | ||
| 289 | untd_hdr_size = sizeof(struct untd_hdr_rst); | ||
| 290 | if (i1480u->rx_skb == NULL) { | ||
| 291 | i1480u_drop(i1480u, "RX: next fragment out of " | ||
| 292 | "sequence! Dropping\n"); | ||
| 293 | goto out; | ||
| 294 | } | ||
| 295 | if (size_left < untd_hdr_size) { | ||
| 296 | i1480u_drop(i1480u, "RX: short NXT fragment! " | ||
| 297 | "Dropping\n"); | ||
| 298 | goto out; | ||
| 299 | } | ||
| 300 | untd_frg_size = le16_to_cpu(untd_hdr->len); | ||
| 301 | if (size_left < untd_hdr_size + untd_frg_size) { | ||
| 302 | i1480u_drop(i1480u, | ||
| 303 | "RX: short payload! Dropping\n"); | ||
| 304 | goto out; | ||
| 305 | } | ||
| 306 | memmove(skb_put(i1480u->rx_skb, untd_frg_size), | ||
| 307 | ptr + untd_hdr_size, untd_frg_size); | ||
| 308 | break; | ||
| 309 | } | ||
| 310 | case i1480u_PKT_FRAG_LST: { | ||
| 311 | dev_dbg(dev, "Lst fragment\n"); | ||
| 312 | untd_hdr_size = sizeof(struct untd_hdr_rst); | ||
| 313 | if (i1480u->rx_skb == NULL) { | ||
| 314 | i1480u_drop(i1480u, "RX: last fragment out of " | ||
| 315 | "sequence! Dropping\n"); | ||
| 316 | goto out; | ||
| 317 | } | ||
| 318 | if (size_left < untd_hdr_size) { | ||
| 319 | i1480u_drop(i1480u, "RX: short LST fragment! " | ||
| 320 | "Dropping\n"); | ||
| 321 | goto out; | ||
| 322 | } | ||
| 323 | untd_frg_size = le16_to_cpu(untd_hdr->len); | ||
| 324 | if (size_left < untd_frg_size + untd_hdr_size) { | ||
| 325 | i1480u_drop(i1480u, | ||
| 326 | "RX: short payload! Dropping\n"); | ||
| 327 | goto out; | ||
| 328 | } | ||
| 329 | memmove(skb_put(i1480u->rx_skb, untd_frg_size), | ||
| 330 | ptr + untd_hdr_size, untd_frg_size); | ||
| 331 | pkt_completed = 1; | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | case i1480u_PKT_FRAG_CMP: { | ||
| 335 | dev_dbg(dev, "cmp fragment\n"); | ||
| 336 | untd_hdr_size = sizeof(struct untd_hdr_cmp); | ||
| 337 | if (i1480u->rx_skb != NULL) | ||
| 338 | i1480u_fix(i1480u, "RX: fix out-of-sequence CMP" | ||
| 339 | " fragment!\n"); | ||
| 340 | if (size_left < untd_hdr_size + i1480u_hdr_size) { | ||
| 341 | i1480u_drop(i1480u, "RX: short CMP fragment! " | ||
| 342 | "Dropping\n"); | ||
| 343 | goto out; | ||
| 344 | } | ||
| 345 | i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len); | ||
| 346 | untd_frg_size = i1480u->rx_untd_pkt_size; | ||
| 347 | if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) { | ||
| 348 | i1480u_drop(i1480u, | ||
| 349 | "RX: short payload! Dropping\n"); | ||
| 350 | goto out; | ||
| 351 | } | ||
| 352 | i1480u->rx_skb = skb; | ||
| 353 | i1480u_hdr = (void *) untd_hdr + untd_hdr_size; | ||
| 354 | i1480u->rx_srcaddr = i1480u_hdr->srcaddr; | ||
| 355 | stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); | ||
| 356 | stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); | ||
| 357 | skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size); | ||
| 358 | skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); | ||
| 359 | rx_buf->data = NULL; /* for hand off skb to network stack */ | ||
| 360 | pkt_completed = 1; | ||
| 361 | i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */ | ||
| 362 | break; | ||
| 363 | } | ||
| 364 | default: | ||
| 365 | i1480u_drop(i1480u, "RX: unknown packet type %u! " | ||
| 366 | "Dropping\n", untd_hdr_type(untd_hdr)); | ||
| 367 | goto out; | ||
| 368 | } | ||
| 369 | size_left -= untd_hdr_size + untd_frg_size; | ||
| 370 | if (size_left > 0) | ||
| 371 | ptr += untd_hdr_size + untd_frg_size; | ||
| 372 | } | ||
| 373 | if (pkt_completed) | ||
| 374 | i1480u_skb_deliver(i1480u); | ||
| 375 | out: | ||
| 376 | /* recreate needed RX buffers*/ | ||
| 377 | if (rx_buf->data == NULL) { | ||
| 378 | /* buffer is being used to receive packet, create new */ | ||
| 379 | new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); | ||
| 380 | if (!new_skb) { | ||
| 381 | if (printk_ratelimit()) | ||
| 382 | dev_err(dev, | ||
| 383 | "RX: cannot allocate RX buffer\n"); | ||
| 384 | } else { | ||
| 385 | new_skb->dev = net_dev; | ||
| 386 | new_skb->ip_summed = CHECKSUM_NONE; | ||
| 387 | skb_reserve(new_skb, 2); | ||
| 388 | rx_buf->data = new_skb; | ||
| 389 | } | ||
| 390 | } | ||
| 391 | return; | ||
| 392 | } | ||
| 393 | |||
| 394 | |||
| 395 | /* | ||
| 396 | * Called when an RX URB has finished receiving or has found some kind | ||
| 397 | * of error condition. | ||
| 398 | * | ||
| 399 | * LIMITATIONS: | ||
| 400 | * | ||
| 401 | * - We read USB-transfers, each transfer contains a SINGLE fragment | ||
| 402 | * (can contain a complete packet, or a 1st, next, or last fragment | ||
| 403 | * of a packet). | ||
| 404 | * Looks like a transfer can contain more than one fragment (07/18/06) | ||
| 405 | * | ||
| 406 | * - Each transfer buffer is the size of the maximum packet size (minus | ||
| 407 | * headroom), i1480u_MAX_PKT_SIZE - 2 | ||
| 408 | * | ||
| 409 | * - We always read the full USB-transfer, no partials. | ||
| 410 | * | ||
| 411 | * - Each transfer is read directly into a skb. This skb will be used to | ||
| 412 | * send data to the upper layers if it is the first fragment or a complete | ||
| 413 | * packet. In the other cases the data will be copied from the skb to | ||
| 414 | * another skb that is being prepared for the upper layers from a prev | ||
| 415 | * first fragment. | ||
| 416 | * | ||
| 417 | * It is simply too much of a pain. Gosh, there should be a unified | ||
| 418 | * SG infrastructure for *everything* [so that I could declare a SG | ||
| 419 | * buffer, pass it to USB for receiving, append some space to it if | ||
| 420 | * I wish, receive more until I have the whole chunk, adapt | ||
| 421 | * pointers on each fragment to remove hardware headers and then | ||
| 422 | * attach that to an skbuff and netif_rx()]. | ||
| 423 | */ | ||
| 424 | void i1480u_rx_cb(struct urb *urb) | ||
| 425 | { | ||
| 426 | int result; | ||
| 427 | int do_parse_buffer = 1; | ||
| 428 | struct i1480u_rx_buf *rx_buf = urb->context; | ||
| 429 | struct i1480u *i1480u = rx_buf->i1480u; | ||
| 430 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 431 | unsigned long flags; | ||
| 432 | u8 rx_buf_idx = rx_buf - i1480u->rx_buf; | ||
| 433 | |||
| 434 | switch (urb->status) { | ||
| 435 | case 0: | ||
| 436 | break; | ||
| 437 | case -ECONNRESET: /* Not an error, but a controlled situation; */ | ||
| 438 | case -ENOENT: /* (we killed the URB)...so, no broadcast */ | ||
| 439 | case -ESHUTDOWN: /* going away! */ | ||
| 440 | dev_err(dev, "RX URB[%u]: goind down %d\n", | ||
| 441 | rx_buf_idx, urb->status); | ||
| 442 | goto error; | ||
| 443 | default: | ||
| 444 | dev_err(dev, "RX URB[%u]: unknown status %d\n", | ||
| 445 | rx_buf_idx, urb->status); | ||
| 446 | if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS, | ||
| 447 | EDC_ERROR_TIMEFRAME)) { | ||
| 448 | dev_err(dev, "RX: max acceptable errors exceeded," | ||
| 449 | " resetting device.\n"); | ||
| 450 | i1480u_rx_unlink_urbs(i1480u); | ||
| 451 | wlp_reset_all(&i1480u->wlp); | ||
| 452 | goto error; | ||
| 453 | } | ||
| 454 | do_parse_buffer = 0; | ||
| 455 | break; | ||
| 456 | } | ||
| 457 | spin_lock_irqsave(&i1480u->lock, flags); | ||
| 458 | /* chew the data fragments, extract network packets */ | ||
| 459 | if (do_parse_buffer) { | ||
| 460 | i1480u_rx_buffer(rx_buf); | ||
| 461 | if (rx_buf->data) { | ||
| 462 | rx_buf->urb->transfer_buffer = rx_buf->data->data; | ||
| 463 | result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC); | ||
| 464 | if (result < 0) { | ||
| 465 | dev_err(dev, "RX URB[%u]: cannot submit %d\n", | ||
| 466 | rx_buf_idx, result); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } | ||
| 470 | spin_unlock_irqrestore(&i1480u->lock, flags); | ||
| 471 | error: | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | |||
diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c deleted file mode 100644 index 4ffaf546cc6c..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/sysfs.c +++ /dev/null | |||
| @@ -1,407 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Sysfs interfaces | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: docs | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/netdevice.h> | ||
| 27 | #include <linux/etherdevice.h> | ||
| 28 | #include <linux/device.h> | ||
| 29 | |||
| 30 | #include "i1480u-wlp.h" | ||
| 31 | |||
| 32 | |||
| 33 | /** | ||
| 34 | * | ||
| 35 | * @dev: Class device from the net_device; assumed refcnted. | ||
| 36 | * | ||
| 37 | * Yes, I don't lock--we assume it is refcounted and I am getting a | ||
| 38 | * single byte value that is kind of atomic to read. | ||
| 39 | */ | ||
| 40 | ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf) | ||
| 41 | { | ||
| 42 | return sprintf(buf, "%u\n", | ||
| 43 | wlp_tx_hdr_phy_rate(&options->def_tx_hdr)); | ||
| 44 | } | ||
| 45 | EXPORT_SYMBOL_GPL(uwb_phy_rate_show); | ||
| 46 | |||
| 47 | |||
| 48 | ssize_t uwb_phy_rate_store(struct wlp_options *options, | ||
| 49 | const char *buf, size_t size) | ||
| 50 | { | ||
| 51 | ssize_t result; | ||
| 52 | unsigned rate; | ||
| 53 | |||
| 54 | result = sscanf(buf, "%u\n", &rate); | ||
| 55 | if (result != 1) { | ||
| 56 | result = -EINVAL; | ||
| 57 | goto out; | ||
| 58 | } | ||
| 59 | result = -EINVAL; | ||
| 60 | if (rate >= UWB_PHY_RATE_INVALID) | ||
| 61 | goto out; | ||
| 62 | wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate); | ||
| 63 | result = 0; | ||
| 64 | out: | ||
| 65 | return result < 0 ? result : size; | ||
| 66 | } | ||
| 67 | EXPORT_SYMBOL_GPL(uwb_phy_rate_store); | ||
| 68 | |||
| 69 | |||
| 70 | ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf) | ||
| 71 | { | ||
| 72 | return sprintf(buf, "%u\n", | ||
| 73 | wlp_tx_hdr_rts_cts(&options->def_tx_hdr)); | ||
| 74 | } | ||
| 75 | EXPORT_SYMBOL_GPL(uwb_rts_cts_show); | ||
| 76 | |||
| 77 | |||
| 78 | ssize_t uwb_rts_cts_store(struct wlp_options *options, | ||
| 79 | const char *buf, size_t size) | ||
| 80 | { | ||
| 81 | ssize_t result; | ||
| 82 | unsigned value; | ||
| 83 | |||
| 84 | result = sscanf(buf, "%u\n", &value); | ||
| 85 | if (result != 1) { | ||
| 86 | result = -EINVAL; | ||
| 87 | goto out; | ||
| 88 | } | ||
| 89 | result = -EINVAL; | ||
| 90 | wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value); | ||
| 91 | result = 0; | ||
| 92 | out: | ||
| 93 | return result < 0 ? result : size; | ||
| 94 | } | ||
| 95 | EXPORT_SYMBOL_GPL(uwb_rts_cts_store); | ||
| 96 | |||
| 97 | |||
| 98 | ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf) | ||
| 99 | { | ||
| 100 | return sprintf(buf, "%u\n", | ||
| 101 | wlp_tx_hdr_ack_policy(&options->def_tx_hdr)); | ||
| 102 | } | ||
| 103 | EXPORT_SYMBOL_GPL(uwb_ack_policy_show); | ||
| 104 | |||
| 105 | |||
| 106 | ssize_t uwb_ack_policy_store(struct wlp_options *options, | ||
| 107 | const char *buf, size_t size) | ||
| 108 | { | ||
| 109 | ssize_t result; | ||
| 110 | unsigned value; | ||
| 111 | |||
| 112 | result = sscanf(buf, "%u\n", &value); | ||
| 113 | if (result != 1 || value > UWB_ACK_B_REQ) { | ||
| 114 | result = -EINVAL; | ||
| 115 | goto out; | ||
| 116 | } | ||
| 117 | wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value); | ||
| 118 | result = 0; | ||
| 119 | out: | ||
| 120 | return result < 0 ? result : size; | ||
| 121 | } | ||
| 122 | EXPORT_SYMBOL_GPL(uwb_ack_policy_store); | ||
| 123 | |||
| 124 | |||
| 125 | /** | ||
| 126 | * Show the PCA base priority. | ||
| 127 | * | ||
| 128 | * We can access without locking, as the value is (for now) orthogonal | ||
| 129 | * to other values. | ||
| 130 | */ | ||
| 131 | ssize_t uwb_pca_base_priority_show(const struct wlp_options *options, | ||
| 132 | char *buf) | ||
| 133 | { | ||
| 134 | return sprintf(buf, "%u\n", | ||
| 135 | options->pca_base_priority); | ||
| 136 | } | ||
| 137 | EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show); | ||
| 138 | |||
| 139 | |||
| 140 | /** | ||
| 141 | * Set the PCA base priority. | ||
| 142 | * | ||
| 143 | * We can access without locking, as the value is (for now) orthogonal | ||
| 144 | * to other values. | ||
| 145 | */ | ||
| 146 | ssize_t uwb_pca_base_priority_store(struct wlp_options *options, | ||
| 147 | const char *buf, size_t size) | ||
| 148 | { | ||
| 149 | ssize_t result = -EINVAL; | ||
| 150 | u8 pca_base_priority; | ||
| 151 | |||
| 152 | result = sscanf(buf, "%hhu\n", &pca_base_priority); | ||
| 153 | if (result != 1) { | ||
| 154 | result = -EINVAL; | ||
| 155 | goto out; | ||
| 156 | } | ||
| 157 | result = -EINVAL; | ||
| 158 | if (pca_base_priority >= 8) | ||
| 159 | goto out; | ||
| 160 | options->pca_base_priority = pca_base_priority; | ||
| 161 | /* Update TX header if we are currently using PCA. */ | ||
| 162 | if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0) | ||
| 163 | wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority); | ||
| 164 | result = 0; | ||
| 165 | out: | ||
| 166 | return result < 0 ? result : size; | ||
| 167 | } | ||
| 168 | EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store); | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Show current inflight values | ||
| 172 | * | ||
| 173 | * Will print the current MAX and THRESHOLD values for the basic flow | ||
| 174 | * control. In addition it will report how many times the TX queue needed | ||
| 175 | * to be restarted since the last time this query was made. | ||
| 176 | */ | ||
| 177 | static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight, | ||
| 178 | char *buf) | ||
| 179 | { | ||
| 180 | ssize_t result; | ||
| 181 | unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ; | ||
| 182 | unsigned long restart_count = atomic_read(&inflight->restart_count); | ||
| 183 | |||
| 184 | result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n" | ||
| 185 | "#read: threshold max inflight_count restarts " | ||
| 186 | "seconds restarts/sec\n" | ||
| 187 | "#write: threshold max\n", | ||
| 188 | inflight->threshold, inflight->max, | ||
| 189 | atomic_read(&inflight->count), | ||
| 190 | restart_count, sec_elapsed, | ||
| 191 | sec_elapsed == 0 ? 0 : restart_count/sec_elapsed); | ||
| 192 | inflight->restart_ts = jiffies; | ||
| 193 | atomic_set(&inflight->restart_count, 0); | ||
| 194 | return result; | ||
| 195 | } | ||
| 196 | |||
| 197 | static | ||
| 198 | ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight, | ||
| 199 | const char *buf, size_t size) | ||
| 200 | { | ||
| 201 | unsigned long in_threshold, in_max; | ||
| 202 | ssize_t result; | ||
| 203 | result = sscanf(buf, "%lu %lu", &in_threshold, &in_max); | ||
| 204 | if (result != 2) | ||
| 205 | return -EINVAL; | ||
| 206 | if (in_max <= in_threshold) | ||
| 207 | return -EINVAL; | ||
| 208 | inflight->max = in_max; | ||
| 209 | inflight->threshold = in_threshold; | ||
| 210 | return size; | ||
| 211 | } | ||
| 212 | /* | ||
| 213 | * Glue (or function adaptors) for accesing info on sysfs | ||
| 214 | * | ||
| 215 | * [we need this indirection because the PCI driver does almost the | ||
| 216 | * same] | ||
| 217 | * | ||
| 218 | * Linux 2.6.21 changed how 'struct netdevice' does attributes (from | ||
| 219 | * having a 'struct class_dev' to having a 'struct device'). That is | ||
| 220 | * quite of a pain. | ||
| 221 | * | ||
| 222 | * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE() | ||
| 223 | * create adaptors for extracting the 'struct i1480u' from a 'struct | ||
| 224 | * dev' and calling a function for doing a sysfs operation (as we have | ||
| 225 | * them factorized already). i1480u_ATTR creates the attribute file | ||
| 226 | * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a | ||
| 227 | * class_device_attr_NAME or device_attr_NAME (for group registration). | ||
| 228 | */ | ||
| 229 | |||
| 230 | #define i1480u_SHOW(name, fn, param) \ | ||
| 231 | static ssize_t i1480u_show_##name(struct device *dev, \ | ||
| 232 | struct device_attribute *attr,\ | ||
| 233 | char *buf) \ | ||
| 234 | { \ | ||
| 235 | struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ | ||
| 236 | return fn(&i1480u->param, buf); \ | ||
| 237 | } | ||
| 238 | |||
| 239 | #define i1480u_STORE(name, fn, param) \ | ||
| 240 | static ssize_t i1480u_store_##name(struct device *dev, \ | ||
| 241 | struct device_attribute *attr,\ | ||
| 242 | const char *buf, size_t size)\ | ||
| 243 | { \ | ||
| 244 | struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ | ||
| 245 | return fn(&i1480u->param, buf, size); \ | ||
| 246 | } | ||
| 247 | |||
| 248 | #define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \ | ||
| 249 | i1480u_show_##name,\ | ||
| 250 | i1480u_store_##name) | ||
| 251 | |||
| 252 | #define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \ | ||
| 253 | S_IRUGO, \ | ||
| 254 | i1480u_show_##name, NULL) | ||
| 255 | |||
| 256 | #define i1480u_ATTR_NAME(a) (dev_attr_##a) | ||
| 257 | |||
| 258 | |||
| 259 | /* | ||
| 260 | * Sysfs adaptors | ||
| 261 | */ | ||
| 262 | i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options); | ||
| 263 | i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options); | ||
| 264 | i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR); | ||
| 265 | |||
| 266 | i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options); | ||
| 267 | i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options); | ||
| 268 | i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR); | ||
| 269 | |||
| 270 | i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options); | ||
| 271 | i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options); | ||
| 272 | i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR); | ||
| 273 | |||
| 274 | i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options); | ||
| 275 | i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options); | ||
| 276 | i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR); | ||
| 277 | |||
| 278 | i1480u_SHOW(wlp_eda, wlp_eda_show, wlp); | ||
| 279 | i1480u_STORE(wlp_eda, wlp_eda_store, wlp); | ||
| 280 | i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR); | ||
| 281 | |||
| 282 | i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp); | ||
| 283 | i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp); | ||
| 284 | i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR); | ||
| 285 | |||
| 286 | i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp); | ||
| 287 | i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp); | ||
| 288 | i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR); | ||
| 289 | |||
| 290 | i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp); | ||
| 291 | i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp); | ||
| 292 | i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR); | ||
| 293 | |||
| 294 | i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp); | ||
| 295 | i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp); | ||
| 296 | i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR); | ||
| 297 | |||
| 298 | i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp); | ||
| 299 | i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp); | ||
| 300 | i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR); | ||
| 301 | |||
| 302 | i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp); | ||
| 303 | i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp); | ||
| 304 | i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR); | ||
| 305 | |||
| 306 | i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp); | ||
| 307 | i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp); | ||
| 308 | i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR); | ||
| 309 | |||
| 310 | i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp); | ||
| 311 | i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp); | ||
| 312 | i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR); | ||
| 313 | |||
| 314 | i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp); | ||
| 315 | i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp); | ||
| 316 | i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR); | ||
| 317 | |||
| 318 | i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp); | ||
| 319 | i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp); | ||
| 320 | i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR); | ||
| 321 | |||
| 322 | i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp); | ||
| 323 | i1480u_ATTR_SHOW(wlp_neighborhood); | ||
| 324 | |||
| 325 | i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss); | ||
| 326 | i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss); | ||
| 327 | i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR); | ||
| 328 | |||
| 329 | /* | ||
| 330 | * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over | ||
| 331 | * the last 256 received WLP frames (ECMA-368 13.3). | ||
| 332 | * | ||
| 333 | * [the -7dB that have to be substracted from the LQI to make the LQE | ||
| 334 | * are already taken into account]. | ||
| 335 | */ | ||
| 336 | i1480u_SHOW(wlp_lqe, stats_show, lqe_stats); | ||
| 337 | i1480u_STORE(wlp_lqe, stats_store, lqe_stats); | ||
| 338 | i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR); | ||
| 339 | |||
| 340 | /* | ||
| 341 | * Show the Receive Signal Strength Indicator averaged over all the | ||
| 342 | * received WLP frames (ECMA-368 13.3). Still is not clear what | ||
| 343 | * this value is, but is kind of a percentage of the signal strength | ||
| 344 | * at the antenna. | ||
| 345 | */ | ||
| 346 | i1480u_SHOW(wlp_rssi, stats_show, rssi_stats); | ||
| 347 | i1480u_STORE(wlp_rssi, stats_store, rssi_stats); | ||
| 348 | i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR); | ||
| 349 | |||
| 350 | /** | ||
| 351 | * We maintain a basic flow control counter. "count" how many TX URBs are | ||
| 352 | * outstanding. Only allow "max" | ||
| 353 | * TX URBs to be outstanding. If this value is reached the queue will be | ||
| 354 | * stopped. The queue will be restarted when there are | ||
| 355 | * "threshold" URBs outstanding. | ||
| 356 | */ | ||
| 357 | i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight); | ||
| 358 | i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight); | ||
| 359 | i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR); | ||
| 360 | |||
| 361 | static struct attribute *i1480u_attrs[] = { | ||
| 362 | &i1480u_ATTR_NAME(uwb_phy_rate).attr, | ||
| 363 | &i1480u_ATTR_NAME(uwb_rts_cts).attr, | ||
| 364 | &i1480u_ATTR_NAME(uwb_ack_policy).attr, | ||
| 365 | &i1480u_ATTR_NAME(uwb_pca_base_priority).attr, | ||
| 366 | &i1480u_ATTR_NAME(wlp_lqe).attr, | ||
| 367 | &i1480u_ATTR_NAME(wlp_rssi).attr, | ||
| 368 | &i1480u_ATTR_NAME(wlp_eda).attr, | ||
| 369 | &i1480u_ATTR_NAME(wlp_uuid).attr, | ||
| 370 | &i1480u_ATTR_NAME(wlp_dev_name).attr, | ||
| 371 | &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr, | ||
| 372 | &i1480u_ATTR_NAME(wlp_dev_model_name).attr, | ||
| 373 | &i1480u_ATTR_NAME(wlp_dev_model_nr).attr, | ||
| 374 | &i1480u_ATTR_NAME(wlp_dev_serial).attr, | ||
| 375 | &i1480u_ATTR_NAME(wlp_dev_prim_category).attr, | ||
| 376 | &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr, | ||
| 377 | &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr, | ||
| 378 | &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr, | ||
| 379 | &i1480u_ATTR_NAME(wlp_neighborhood).attr, | ||
| 380 | &i1480u_ATTR_NAME(wss_activate).attr, | ||
| 381 | &i1480u_ATTR_NAME(wlp_tx_inflight).attr, | ||
| 382 | NULL, | ||
| 383 | }; | ||
| 384 | |||
| 385 | static struct attribute_group i1480u_attr_group = { | ||
| 386 | .name = NULL, /* we want them in the same directory */ | ||
| 387 | .attrs = i1480u_attrs, | ||
| 388 | }; | ||
| 389 | |||
| 390 | int i1480u_sysfs_setup(struct i1480u *i1480u) | ||
| 391 | { | ||
| 392 | int result; | ||
| 393 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 394 | result = sysfs_create_group(&i1480u->net_dev->dev.kobj, | ||
| 395 | &i1480u_attr_group); | ||
| 396 | if (result < 0) | ||
| 397 | dev_err(dev, "cannot initialize sysfs attributes: %d\n", | ||
| 398 | result); | ||
| 399 | return result; | ||
| 400 | } | ||
| 401 | |||
| 402 | |||
| 403 | void i1480u_sysfs_release(struct i1480u *i1480u) | ||
| 404 | { | ||
| 405 | sysfs_remove_group(&i1480u->net_dev->dev.kobj, | ||
| 406 | &i1480u_attr_group); | ||
| 407 | } | ||
diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c deleted file mode 100644 index 3c117a364564..000000000000 --- a/drivers/uwb/i1480/i1480u-wlp/tx.c +++ /dev/null | |||
| @@ -1,584 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Deal with TX (massaging data to transmit, handling it) | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * Transmission engine. Get an skb, create from that a WLP transmit | ||
| 24 | * context, add a WLP TX header (which we keep prefilled in the | ||
| 25 | * device's instance), fill out the target-specific fields and | ||
| 26 | * fire it. | ||
| 27 | * | ||
| 28 | * ROADMAP: | ||
| 29 | * | ||
| 30 | * Entry points: | ||
| 31 | * | ||
| 32 | * i1480u_tx_release(): called by i1480u_disconnect() to release | ||
| 33 | * pending tx contexts. | ||
| 34 | * | ||
| 35 | * i1480u_tx_cb(): callback for TX contexts (USB URBs) | ||
| 36 | * i1480u_tx_destroy(): | ||
| 37 | * | ||
| 38 | * i1480u_tx_timeout(): called for timeout handling from the | ||
| 39 | * network stack. | ||
| 40 | * | ||
| 41 | * i1480u_hard_start_xmit(): called for transmitting an skb from | ||
| 42 | * the network stack. Will interact with WLP | ||
| 43 | * substack to verify and prepare frame. | ||
| 44 | * i1480u_xmit_frame(): actual transmission on hardware | ||
| 45 | * | ||
| 46 | * i1480u_tx_create() Creates TX context | ||
| 47 | * i1480u_tx_create_1() For packets in 1 fragment | ||
| 48 | * i1480u_tx_create_n() For packets in >1 fragments | ||
| 49 | * | ||
| 50 | * TODO: | ||
| 51 | * | ||
| 52 | * - FIXME: rewrite using usb_sg_*(), add asynch support to | ||
| 53 | * usb_sg_*(). It might not make too much sense as most of | ||
| 54 | * the times the MTU will be smaller than one page... | ||
| 55 | */ | ||
| 56 | |||
| 57 | #include <linux/slab.h> | ||
| 58 | #include "i1480u-wlp.h" | ||
| 59 | |||
| 60 | enum { | ||
| 61 | /* This is only for Next and Last TX packets */ | ||
| 62 | i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE | ||
| 63 | - sizeof(struct untd_hdr_rst), | ||
| 64 | }; | ||
| 65 | |||
| 66 | /* Free resources allocated to a i1480u tx context. */ | ||
| 67 | static | ||
| 68 | void i1480u_tx_free(struct i1480u_tx *wtx) | ||
| 69 | { | ||
| 70 | kfree(wtx->buf); | ||
| 71 | if (wtx->skb) | ||
| 72 | dev_kfree_skb_irq(wtx->skb); | ||
| 73 | usb_free_urb(wtx->urb); | ||
| 74 | kfree(wtx); | ||
| 75 | } | ||
| 76 | |||
| 77 | static | ||
| 78 | void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx) | ||
| 79 | { | ||
| 80 | unsigned long flags; | ||
| 81 | spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */ | ||
| 82 | list_del(&wtx->list_node); | ||
| 83 | i1480u_tx_free(wtx); | ||
| 84 | spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); | ||
| 85 | } | ||
| 86 | |||
| 87 | static | ||
| 88 | void i1480u_tx_unlink_urbs(struct i1480u *i1480u) | ||
| 89 | { | ||
| 90 | unsigned long flags; | ||
| 91 | struct i1480u_tx *wtx, *next; | ||
| 92 | |||
| 93 | spin_lock_irqsave(&i1480u->tx_list_lock, flags); | ||
| 94 | list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { | ||
| 95 | usb_unlink_urb(wtx->urb); | ||
| 96 | } | ||
| 97 | spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); | ||
| 98 | } | ||
| 99 | |||
| 100 | |||
| 101 | /* | ||
| 102 | * Callback for a completed tx USB URB. | ||
| 103 | * | ||
| 104 | * TODO: | ||
| 105 | * | ||
| 106 | * - FIXME: recover errors more gracefully | ||
| 107 | * - FIXME: handle NAKs (I dont think they come here) for flow ctl | ||
| 108 | */ | ||
| 109 | static | ||
| 110 | void i1480u_tx_cb(struct urb *urb) | ||
| 111 | { | ||
| 112 | struct i1480u_tx *wtx = urb->context; | ||
| 113 | struct i1480u *i1480u = wtx->i1480u; | ||
| 114 | struct net_device *net_dev = i1480u->net_dev; | ||
| 115 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 116 | unsigned long flags; | ||
| 117 | |||
| 118 | switch (urb->status) { | ||
| 119 | case 0: | ||
| 120 | spin_lock_irqsave(&i1480u->lock, flags); | ||
| 121 | net_dev->stats.tx_packets++; | ||
| 122 | net_dev->stats.tx_bytes += urb->actual_length; | ||
| 123 | spin_unlock_irqrestore(&i1480u->lock, flags); | ||
| 124 | break; | ||
| 125 | case -ECONNRESET: /* Not an error, but a controlled situation; */ | ||
| 126 | case -ENOENT: /* (we killed the URB)...so, no broadcast */ | ||
| 127 | dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status); | ||
| 128 | netif_stop_queue(net_dev); | ||
| 129 | break; | ||
| 130 | case -ESHUTDOWN: /* going away! */ | ||
| 131 | dev_dbg(dev, "notif endp: down %d\n", urb->status); | ||
| 132 | netif_stop_queue(net_dev); | ||
| 133 | break; | ||
| 134 | default: | ||
| 135 | dev_err(dev, "TX: unknown URB status %d\n", urb->status); | ||
| 136 | if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS, | ||
| 137 | EDC_ERROR_TIMEFRAME)) { | ||
| 138 | dev_err(dev, "TX: max acceptable errors exceeded." | ||
| 139 | "Reset device.\n"); | ||
| 140 | netif_stop_queue(net_dev); | ||
| 141 | i1480u_tx_unlink_urbs(i1480u); | ||
| 142 | wlp_reset_all(&i1480u->wlp); | ||
| 143 | } | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | i1480u_tx_destroy(i1480u, wtx); | ||
| 147 | if (atomic_dec_return(&i1480u->tx_inflight.count) | ||
| 148 | <= i1480u->tx_inflight.threshold | ||
| 149 | && netif_queue_stopped(net_dev) | ||
| 150 | && i1480u->tx_inflight.threshold != 0) { | ||
| 151 | netif_start_queue(net_dev); | ||
| 152 | atomic_inc(&i1480u->tx_inflight.restart_count); | ||
| 153 | } | ||
| 154 | return; | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | /* | ||
| 159 | * Given a buffer that doesn't fit in a single fragment, create an | ||
| 160 | * scatter/gather structure for delivery to the USB pipe. | ||
| 161 | * | ||
| 162 | * Implements functionality of i1480u_tx_create(). | ||
| 163 | * | ||
| 164 | * @wtx: tx descriptor | ||
| 165 | * @skb: skb to send | ||
| 166 | * @gfp_mask: gfp allocation mask | ||
| 167 | * @returns: Pointer to @wtx if ok, NULL on error. | ||
| 168 | * | ||
| 169 | * Sorry, TOO LONG a function, but breaking it up is kind of hard | ||
| 170 | * | ||
| 171 | * This will break the buffer in chunks smaller than | ||
| 172 | * i1480u_MAX_FRG_SIZE (including the header) and add proper headers | ||
| 173 | * to each: | ||
| 174 | * | ||
| 175 | * 1st header \ | ||
| 176 | * i1480 tx header | fragment 1 | ||
| 177 | * fragment data / | ||
| 178 | * nxt header \ fragment 2 | ||
| 179 | * fragment data / | ||
| 180 | * .. | ||
| 181 | * .. | ||
| 182 | * last header \ fragment 3 | ||
| 183 | * last fragment data / | ||
| 184 | * | ||
| 185 | * This does not fill the i1480 TX header, it is left up to the | ||
| 186 | * caller to do that; you can get it from @wtx->wlp_tx_hdr. | ||
| 187 | * | ||
| 188 | * This function consumes the skb unless there is an error. | ||
| 189 | */ | ||
| 190 | static | ||
| 191 | int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb, | ||
| 192 | gfp_t gfp_mask) | ||
| 193 | { | ||
| 194 | int result; | ||
| 195 | void *pl; | ||
| 196 | size_t pl_size; | ||
| 197 | |||
| 198 | void *pl_itr, *buf_itr; | ||
| 199 | size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0; | ||
| 200 | struct untd_hdr_1st *untd_hdr_1st; | ||
| 201 | struct wlp_tx_hdr *wlp_tx_hdr; | ||
| 202 | struct untd_hdr_rst *untd_hdr_rst; | ||
| 203 | |||
| 204 | wtx->skb = NULL; | ||
| 205 | pl = skb->data; | ||
| 206 | pl_itr = pl; | ||
| 207 | pl_size = skb->len; | ||
| 208 | pl_size_left = pl_size; /* payload size */ | ||
| 209 | /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus | ||
| 210 | * the headers */ | ||
| 211 | pl_size_1st = i1480u_MAX_FRG_SIZE | ||
| 212 | - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr); | ||
| 213 | BUG_ON(pl_size_1st > pl_size); | ||
| 214 | pl_size_left -= pl_size_1st; | ||
| 215 | /* The rest have an smaller header (no i1480 TX header). We | ||
| 216 | * need to break up the payload in blocks smaller than | ||
| 217 | * i1480u_MAX_PL_SIZE (payload excluding header). */ | ||
| 218 | frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE; | ||
| 219 | /* Allocate space for the new buffer. In this new buffer we'll | ||
| 220 | * place the headers followed by the data fragment, headers, | ||
| 221 | * data fragments, etc.. | ||
| 222 | */ | ||
| 223 | result = -ENOMEM; | ||
| 224 | wtx->buf_size = sizeof(*untd_hdr_1st) | ||
| 225 | + sizeof(*wlp_tx_hdr) | ||
| 226 | + frgs * sizeof(*untd_hdr_rst) | ||
| 227 | + pl_size; | ||
| 228 | wtx->buf = kmalloc(wtx->buf_size, gfp_mask); | ||
| 229 | if (wtx->buf == NULL) | ||
| 230 | goto error_buf_alloc; | ||
| 231 | |||
| 232 | buf_itr = wtx->buf; /* We got the space, let's fill it up */ | ||
| 233 | /* Fill 1st fragment */ | ||
| 234 | untd_hdr_1st = buf_itr; | ||
| 235 | buf_itr += sizeof(*untd_hdr_1st); | ||
| 236 | untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST); | ||
| 237 | untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0); | ||
| 238 | untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr)); | ||
| 239 | untd_hdr_1st->fragment_len = | ||
| 240 | cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr)); | ||
| 241 | memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding)); | ||
| 242 | /* Set up i1480 header info */ | ||
| 243 | wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr; | ||
| 244 | buf_itr += sizeof(*wlp_tx_hdr); | ||
| 245 | /* Copy the first fragment */ | ||
| 246 | memcpy(buf_itr, pl_itr, pl_size_1st); | ||
| 247 | pl_itr += pl_size_1st; | ||
| 248 | buf_itr += pl_size_1st; | ||
| 249 | |||
| 250 | /* Now do each remaining fragment */ | ||
| 251 | result = -EINVAL; | ||
| 252 | while (pl_size_left > 0) { | ||
| 253 | if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf | ||
| 254 | > wtx->buf_size) { | ||
| 255 | printk(KERN_ERR "BUG: no space for header\n"); | ||
| 256 | goto error_bug; | ||
| 257 | } | ||
| 258 | untd_hdr_rst = buf_itr; | ||
| 259 | buf_itr += sizeof(*untd_hdr_rst); | ||
| 260 | if (pl_size_left > i1480u_MAX_PL_SIZE) { | ||
| 261 | frg_pl_size = i1480u_MAX_PL_SIZE; | ||
| 262 | untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT); | ||
| 263 | } else { | ||
| 264 | frg_pl_size = pl_size_left; | ||
| 265 | untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST); | ||
| 266 | } | ||
| 267 | untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0); | ||
| 268 | untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size); | ||
| 269 | untd_hdr_rst->padding = 0; | ||
| 270 | if (buf_itr + frg_pl_size - wtx->buf | ||
| 271 | > wtx->buf_size) { | ||
| 272 | printk(KERN_ERR "BUG: no space for payload\n"); | ||
| 273 | goto error_bug; | ||
| 274 | } | ||
| 275 | memcpy(buf_itr, pl_itr, frg_pl_size); | ||
| 276 | buf_itr += frg_pl_size; | ||
| 277 | pl_itr += frg_pl_size; | ||
| 278 | pl_size_left -= frg_pl_size; | ||
| 279 | } | ||
| 280 | dev_kfree_skb_irq(skb); | ||
| 281 | return 0; | ||
| 282 | |||
| 283 | error_bug: | ||
| 284 | printk(KERN_ERR | ||
| 285 | "BUG: skb %u bytes\n" | ||
| 286 | "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n" | ||
| 287 | "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n", | ||
| 288 | skb->len, | ||
| 289 | frg_pl_size, i1480u_MAX_FRG_SIZE, | ||
| 290 | buf_itr - wtx->buf, wtx->buf_size, pl_size_left); | ||
| 291 | |||
| 292 | kfree(wtx->buf); | ||
| 293 | error_buf_alloc: | ||
| 294 | return result; | ||
| 295 | } | ||
| 296 | |||
| 297 | |||
| 298 | /* | ||
| 299 | * Given a buffer that fits in a single fragment, fill out a @wtx | ||
| 300 | * struct for transmitting it down the USB pipe. | ||
| 301 | * | ||
| 302 | * Uses the fact that we have space reserved in front of the skbuff | ||
| 303 | * for hardware headers :] | ||
| 304 | * | ||
| 305 | * This does not fill the i1480 TX header, it is left up to the | ||
| 306 | * caller to do that; you can get it from @wtx->wlp_tx_hdr. | ||
| 307 | * | ||
| 308 | * @pl: pointer to payload data | ||
| 309 | * @pl_size: size of the payuload | ||
| 310 | * | ||
| 311 | * This function does not consume the @skb. | ||
| 312 | */ | ||
| 313 | static | ||
| 314 | int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb, | ||
| 315 | gfp_t gfp_mask) | ||
| 316 | { | ||
| 317 | struct untd_hdr_cmp *untd_hdr_cmp; | ||
| 318 | struct wlp_tx_hdr *wlp_tx_hdr; | ||
| 319 | |||
| 320 | wtx->buf = NULL; | ||
| 321 | wtx->skb = skb; | ||
| 322 | BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr)); | ||
| 323 | wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr)); | ||
| 324 | wtx->wlp_tx_hdr = wlp_tx_hdr; | ||
| 325 | BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp)); | ||
| 326 | untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp)); | ||
| 327 | |||
| 328 | untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP); | ||
| 329 | untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0); | ||
| 330 | untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp)); | ||
| 331 | untd_hdr_cmp->padding = 0; | ||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 335 | |||
| 336 | /* | ||
| 337 | * Given a skb to transmit, massage it to become palatable for the TX pipe | ||
| 338 | * | ||
| 339 | * This will break the buffer in chunks smaller than | ||
| 340 | * i1480u_MAX_FRG_SIZE and add proper headers to each. | ||
| 341 | * | ||
| 342 | * 1st header \ | ||
| 343 | * i1480 tx header | fragment 1 | ||
| 344 | * fragment data / | ||
| 345 | * nxt header \ fragment 2 | ||
| 346 | * fragment data / | ||
| 347 | * .. | ||
| 348 | * .. | ||
| 349 | * last header \ fragment 3 | ||
| 350 | * last fragment data / | ||
| 351 | * | ||
| 352 | * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE. | ||
| 353 | * | ||
| 354 | * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the | ||
| 355 | * following is composed: | ||
| 356 | * | ||
| 357 | * complete header \ | ||
| 358 | * i1480 tx header | single fragment | ||
| 359 | * packet data / | ||
| 360 | * | ||
| 361 | * We were going to use s/g support, but because the interface is | ||
| 362 | * synch and at the end there is plenty of overhead to do it, it | ||
| 363 | * didn't seem that worth for data that is going to be smaller than | ||
| 364 | * one page. | ||
| 365 | */ | ||
| 366 | static | ||
| 367 | struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u, | ||
| 368 | struct sk_buff *skb, gfp_t gfp_mask) | ||
| 369 | { | ||
| 370 | int result; | ||
| 371 | struct usb_endpoint_descriptor *epd; | ||
| 372 | int usb_pipe; | ||
| 373 | unsigned long flags; | ||
| 374 | |||
| 375 | struct i1480u_tx *wtx; | ||
| 376 | const size_t pl_max_size = | ||
| 377 | i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp) | ||
| 378 | - sizeof(struct wlp_tx_hdr); | ||
| 379 | |||
| 380 | wtx = kmalloc(sizeof(*wtx), gfp_mask); | ||
| 381 | if (wtx == NULL) | ||
| 382 | goto error_wtx_alloc; | ||
| 383 | wtx->urb = usb_alloc_urb(0, gfp_mask); | ||
| 384 | if (wtx->urb == NULL) | ||
| 385 | goto error_urb_alloc; | ||
| 386 | epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc; | ||
| 387 | usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress); | ||
| 388 | /* Fits in a single complete packet or need to split? */ | ||
| 389 | if (skb->len > pl_max_size) { | ||
| 390 | result = i1480u_tx_create_n(wtx, skb, gfp_mask); | ||
| 391 | if (result < 0) | ||
| 392 | goto error_create; | ||
| 393 | usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, | ||
| 394 | wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx); | ||
| 395 | } else { | ||
| 396 | result = i1480u_tx_create_1(wtx, skb, gfp_mask); | ||
| 397 | if (result < 0) | ||
| 398 | goto error_create; | ||
| 399 | usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, | ||
| 400 | skb->data, skb->len, i1480u_tx_cb, wtx); | ||
| 401 | } | ||
| 402 | spin_lock_irqsave(&i1480u->tx_list_lock, flags); | ||
| 403 | list_add(&wtx->list_node, &i1480u->tx_list); | ||
| 404 | spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); | ||
| 405 | return wtx; | ||
| 406 | |||
| 407 | error_create: | ||
| 408 | kfree(wtx->urb); | ||
| 409 | error_urb_alloc: | ||
| 410 | kfree(wtx); | ||
| 411 | error_wtx_alloc: | ||
| 412 | return NULL; | ||
| 413 | } | ||
| 414 | |||
| 415 | /* | ||
| 416 | * Actual fragmentation and transmission of frame | ||
| 417 | * | ||
| 418 | * @wlp: WLP substack data structure | ||
| 419 | * @skb: To be transmitted | ||
| 420 | * @dst: Device address of destination | ||
| 421 | * @returns: 0 on success, <0 on failure | ||
| 422 | * | ||
| 423 | * This function can also be called directly (not just from | ||
| 424 | * hard_start_xmit), so we also check here if the interface is up before | ||
| 425 | * taking sending anything. | ||
| 426 | */ | ||
| 427 | int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 428 | struct uwb_dev_addr *dst) | ||
| 429 | { | ||
| 430 | int result = -ENXIO; | ||
| 431 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
| 432 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 433 | struct net_device *net_dev = i1480u->net_dev; | ||
| 434 | struct i1480u_tx *wtx; | ||
| 435 | struct wlp_tx_hdr *wlp_tx_hdr; | ||
| 436 | static unsigned char dev_bcast[2] = { 0xff, 0xff }; | ||
| 437 | |||
| 438 | BUG_ON(i1480u->wlp.rc == NULL); | ||
| 439 | if ((net_dev->flags & IFF_UP) == 0) | ||
| 440 | goto out; | ||
| 441 | result = -EBUSY; | ||
| 442 | if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) { | ||
| 443 | netif_stop_queue(net_dev); | ||
| 444 | goto error_max_inflight; | ||
| 445 | } | ||
| 446 | result = -ENOMEM; | ||
| 447 | wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC); | ||
| 448 | if (unlikely(wtx == NULL)) { | ||
| 449 | if (printk_ratelimit()) | ||
| 450 | dev_err(dev, "TX: no memory for WLP TX URB," | ||
| 451 | "dropping packet (in flight %d)\n", | ||
| 452 | atomic_read(&i1480u->tx_inflight.count)); | ||
| 453 | netif_stop_queue(net_dev); | ||
| 454 | goto error_wtx_alloc; | ||
| 455 | } | ||
| 456 | wtx->i1480u = i1480u; | ||
| 457 | /* Fill out the i1480 header; @i1480u->def_tx_hdr read without | ||
| 458 | * locking. We do so because they are kind of orthogonal to | ||
| 459 | * each other (and thus not changed in an atomic batch). | ||
| 460 | * The ETH header is right after the WLP TX header. */ | ||
| 461 | wlp_tx_hdr = wtx->wlp_tx_hdr; | ||
| 462 | *wlp_tx_hdr = i1480u->options.def_tx_hdr; | ||
| 463 | wlp_tx_hdr->dstaddr = *dst; | ||
| 464 | if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast)) | ||
| 465 | && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) { | ||
| 466 | /*Broadcast message directed to DRP host. Send as best effort | ||
| 467 | * on PCA. */ | ||
| 468 | wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority); | ||
| 469 | } | ||
| 470 | |||
| 471 | result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */ | ||
| 472 | if (result < 0) { | ||
| 473 | dev_err(dev, "TX: cannot submit URB: %d\n", result); | ||
| 474 | /* We leave the freeing of skb to calling function */ | ||
| 475 | wtx->skb = NULL; | ||
| 476 | goto error_tx_urb_submit; | ||
| 477 | } | ||
| 478 | atomic_inc(&i1480u->tx_inflight.count); | ||
| 479 | net_dev->trans_start = jiffies; | ||
| 480 | return result; | ||
| 481 | |||
| 482 | error_tx_urb_submit: | ||
| 483 | i1480u_tx_destroy(i1480u, wtx); | ||
| 484 | error_wtx_alloc: | ||
| 485 | error_max_inflight: | ||
| 486 | out: | ||
| 487 | return result; | ||
| 488 | } | ||
| 489 | |||
| 490 | |||
| 491 | /* | ||
| 492 | * Transmit an skb Called when an skbuf has to be transmitted | ||
| 493 | * | ||
| 494 | * The skb is first passed to WLP substack to ensure this is a valid | ||
| 495 | * frame. If valid the device address of destination will be filled and | ||
| 496 | * the WLP header prepended to the skb. If this step fails we fake sending | ||
| 497 | * the frame, if we return an error the network stack will just keep trying. | ||
| 498 | * | ||
| 499 | * Broadcast frames inside a WSS needs to be treated special as multicast is | ||
| 500 | * not supported. A broadcast frame is sent as unicast to each member of the | ||
| 501 | * WSS - this is done by the WLP substack when it finds a broadcast frame. | ||
| 502 | * So, we test if the WLP substack took over the skb and only transmit it | ||
| 503 | * if it has not (been taken over). | ||
| 504 | * | ||
| 505 | * @net_dev->xmit_lock is held | ||
| 506 | */ | ||
| 507 | netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *skb, | ||
| 508 | struct net_device *net_dev) | ||
| 509 | { | ||
| 510 | int result; | ||
| 511 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 512 | struct device *dev = &i1480u->usb_iface->dev; | ||
| 513 | struct uwb_dev_addr dst; | ||
| 514 | |||
| 515 | if ((net_dev->flags & IFF_UP) == 0) | ||
| 516 | goto error; | ||
| 517 | result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst); | ||
| 518 | if (result < 0) { | ||
| 519 | dev_err(dev, "WLP verification of TX frame failed (%d). " | ||
| 520 | "Dropping packet.\n", result); | ||
| 521 | goto error; | ||
| 522 | } else if (result == 1) { | ||
| 523 | /* trans_start time will be set when WLP actually transmits | ||
| 524 | * the frame */ | ||
| 525 | goto out; | ||
| 526 | } | ||
| 527 | result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst); | ||
| 528 | if (result < 0) { | ||
| 529 | dev_err(dev, "Frame TX failed (%d).\n", result); | ||
| 530 | goto error; | ||
| 531 | } | ||
| 532 | return NETDEV_TX_OK; | ||
| 533 | error: | ||
| 534 | dev_kfree_skb_any(skb); | ||
| 535 | net_dev->stats.tx_dropped++; | ||
| 536 | out: | ||
| 537 | return NETDEV_TX_OK; | ||
| 538 | } | ||
| 539 | |||
| 540 | |||
| 541 | /* | ||
| 542 | * Called when a pkt transmission doesn't complete in a reasonable period | ||
| 543 | * Device reset may sleep - do it outside of interrupt context (delayed) | ||
| 544 | */ | ||
| 545 | void i1480u_tx_timeout(struct net_device *net_dev) | ||
| 546 | { | ||
| 547 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
| 548 | |||
| 549 | wlp_reset_all(&i1480u->wlp); | ||
| 550 | } | ||
| 551 | |||
| 552 | |||
| 553 | void i1480u_tx_release(struct i1480u *i1480u) | ||
| 554 | { | ||
| 555 | unsigned long flags; | ||
| 556 | struct i1480u_tx *wtx, *next; | ||
| 557 | int count = 0, empty; | ||
| 558 | |||
| 559 | spin_lock_irqsave(&i1480u->tx_list_lock, flags); | ||
| 560 | list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { | ||
| 561 | count++; | ||
| 562 | usb_unlink_urb(wtx->urb); | ||
| 563 | } | ||
| 564 | spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); | ||
| 565 | count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */ | ||
| 566 | /* | ||
| 567 | * We don't like this sollution too much (dirty as it is), but | ||
| 568 | * it is cheaper than putting a refcount on each i1480u_tx and | ||
| 569 | * i1480uting for all of them to go away... | ||
| 570 | * | ||
| 571 | * Called when no more packets can be added to tx_list | ||
| 572 | * so can i1480ut for it to be empty. | ||
| 573 | */ | ||
| 574 | while (1) { | ||
| 575 | spin_lock_irqsave(&i1480u->tx_list_lock, flags); | ||
| 576 | empty = list_empty(&i1480u->tx_list); | ||
| 577 | spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); | ||
| 578 | if (empty) | ||
| 579 | break; | ||
| 580 | count--; | ||
| 581 | BUG_ON(count == 0); | ||
| 582 | msleep(20); | ||
| 583 | } | ||
| 584 | } | ||
diff --git a/drivers/uwb/wlp/Makefile b/drivers/uwb/wlp/Makefile deleted file mode 100644 index c72c11db5b1b..000000000000 --- a/drivers/uwb/wlp/Makefile +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | obj-$(CONFIG_UWB_WLP) := wlp.o | ||
| 2 | |||
| 3 | wlp-objs := \ | ||
| 4 | driver.o \ | ||
| 5 | eda.o \ | ||
| 6 | messages.o \ | ||
| 7 | sysfs.o \ | ||
| 8 | txrx.o \ | ||
| 9 | wlp-lc.o \ | ||
| 10 | wss-lc.o | ||
diff --git a/drivers/uwb/wlp/driver.c b/drivers/uwb/wlp/driver.c deleted file mode 100644 index cb8d699b6a67..000000000000 --- a/drivers/uwb/wlp/driver.c +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007 Intel Corporation | ||
| 5 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License version | ||
| 9 | * 2 as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 19 | * 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * Life cycle of WLP substack | ||
| 23 | * | ||
| 24 | * FIXME: Docs | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | |||
| 29 | static int __init wlp_subsys_init(void) | ||
| 30 | { | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | module_init(wlp_subsys_init); | ||
| 34 | |||
| 35 | static void __exit wlp_subsys_exit(void) | ||
| 36 | { | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | module_exit(wlp_subsys_exit); | ||
| 40 | |||
| 41 | MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>"); | ||
| 42 | MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)"); | ||
| 43 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c deleted file mode 100644 index 086fc0cf9401..000000000000 --- a/drivers/uwb/wlp/eda.c +++ /dev/null | |||
| @@ -1,415 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WUSB Wire Adapter: WLP interface | ||
| 3 | * Ethernet to device address cache | ||
| 4 | * | ||
| 5 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * We need to be able to map ethernet addresses to device addresses | ||
| 24 | * and back because there is not explicit relationship between the eth | ||
| 25 | * addresses used in the ETH frames and the device addresses (no, it | ||
| 26 | * would not have been simpler to force as ETH address the MBOA MAC | ||
| 27 | * address...no, not at all :). | ||
| 28 | * | ||
| 29 | * A device has one MBOA MAC address and one device address. It is possible | ||
| 30 | * for a device to have more than one virtual MAC address (although a | ||
| 31 | * virtual address can be the same as the MBOA MAC address). The device | ||
| 32 | * address is guaranteed to be unique among the devices in the extended | ||
| 33 | * beacon group (see ECMA 17.1.1). We thus use the device address as index | ||
| 34 | * to this cache. We do allow searching based on virtual address as this | ||
| 35 | * is how Ethernet frames will be addressed. | ||
| 36 | * | ||
| 37 | * We need to support virtual EUI-48. Although, right now the virtual | ||
| 38 | * EUI-48 will always be the same as the MAC SAP address. The EDA cache | ||
| 39 | * entry thus contains a MAC SAP address as well as the virtual address | ||
| 40 | * (used to map the network stack address to a neighbor). When we move | ||
| 41 | * to support more than one virtual MAC on a host then this organization | ||
| 42 | * will have to change. Perhaps a neighbor has a list of WSSs, each with a | ||
| 43 | * tag and virtual EUI-48. | ||
| 44 | * | ||
| 45 | * On data transmission | ||
| 46 | * it is used to determine if the neighbor is connected and what WSS it | ||
| 47 | * belongs to. With this we know what tag to add to the WLP frame. Storing | ||
| 48 | * the WSS in the EDA cache may be overkill because we only support one | ||
| 49 | * WSS. Hopefully we will support more than one WSS at some point. | ||
| 50 | * On data reception it is used to determine the WSS based on | ||
| 51 | * the tag and address of the transmitting neighbor. | ||
| 52 | */ | ||
| 53 | |||
| 54 | #include <linux/netdevice.h> | ||
| 55 | #include <linux/etherdevice.h> | ||
| 56 | #include <linux/slab.h> | ||
| 57 | #include <linux/wlp.h> | ||
| 58 | #include "wlp-internal.h" | ||
| 59 | |||
| 60 | |||
| 61 | /* FIXME: cache is not purged, only on device close */ | ||
| 62 | |||
| 63 | /* FIXME: does not scale, change to dynamic array */ | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Initialize the EDA cache | ||
| 67 | * | ||
| 68 | * @returns 0 if ok, < 0 errno code on error | ||
| 69 | * | ||
| 70 | * Call when the interface is being brought up | ||
| 71 | * | ||
| 72 | * NOTE: Keep it as a separate function as the implementation will | ||
| 73 | * change and be more complex. | ||
| 74 | */ | ||
| 75 | void wlp_eda_init(struct wlp_eda *eda) | ||
| 76 | { | ||
| 77 | INIT_LIST_HEAD(&eda->cache); | ||
| 78 | spin_lock_init(&eda->lock); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* | ||
| 82 | * Release the EDA cache | ||
| 83 | * | ||
| 84 | * @returns 0 if ok, < 0 errno code on error | ||
| 85 | * | ||
| 86 | * Called when the interface is brought down | ||
| 87 | */ | ||
| 88 | void wlp_eda_release(struct wlp_eda *eda) | ||
| 89 | { | ||
| 90 | unsigned long flags; | ||
| 91 | struct wlp_eda_node *itr, *next; | ||
| 92 | |||
| 93 | spin_lock_irqsave(&eda->lock, flags); | ||
| 94 | list_for_each_entry_safe(itr, next, &eda->cache, list_node) { | ||
| 95 | list_del(&itr->list_node); | ||
| 96 | kfree(itr); | ||
| 97 | } | ||
| 98 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Add an address mapping | ||
| 103 | * | ||
| 104 | * @returns 0 if ok, < 0 errno code on error | ||
| 105 | * | ||
| 106 | * An address mapping is initially created when the neighbor device is seen | ||
| 107 | * for the first time (it is "onair"). At this time the neighbor is not | ||
| 108 | * connected or associated with a WSS so we only populate the Ethernet and | ||
| 109 | * Device address fields. | ||
| 110 | * | ||
| 111 | */ | ||
| 112 | int wlp_eda_create_node(struct wlp_eda *eda, | ||
| 113 | const unsigned char eth_addr[ETH_ALEN], | ||
| 114 | const struct uwb_dev_addr *dev_addr) | ||
| 115 | { | ||
| 116 | int result = 0; | ||
| 117 | struct wlp_eda_node *itr; | ||
| 118 | unsigned long flags; | ||
| 119 | |||
| 120 | BUG_ON(dev_addr == NULL || eth_addr == NULL); | ||
| 121 | spin_lock_irqsave(&eda->lock, flags); | ||
| 122 | list_for_each_entry(itr, &eda->cache, list_node) { | ||
| 123 | if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { | ||
| 124 | printk(KERN_ERR "EDA cache already contains entry " | ||
| 125 | "for neighbor %02x:%02x\n", | ||
| 126 | dev_addr->data[1], dev_addr->data[0]); | ||
| 127 | result = -EEXIST; | ||
| 128 | goto out_unlock; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | itr = kzalloc(sizeof(*itr), GFP_ATOMIC); | ||
| 132 | if (itr != NULL) { | ||
| 133 | memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr)); | ||
| 134 | itr->dev_addr = *dev_addr; | ||
| 135 | list_add(&itr->list_node, &eda->cache); | ||
| 136 | } else | ||
| 137 | result = -ENOMEM; | ||
| 138 | out_unlock: | ||
| 139 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 140 | return result; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * Remove entry from EDA cache | ||
| 145 | * | ||
| 146 | * This is done when the device goes off air. | ||
| 147 | */ | ||
| 148 | void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr) | ||
| 149 | { | ||
| 150 | struct wlp_eda_node *itr, *next; | ||
| 151 | unsigned long flags; | ||
| 152 | |||
| 153 | spin_lock_irqsave(&eda->lock, flags); | ||
| 154 | list_for_each_entry_safe(itr, next, &eda->cache, list_node) { | ||
| 155 | if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { | ||
| 156 | list_del(&itr->list_node); | ||
| 157 | kfree(itr); | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* | ||
| 165 | * Update an address mapping | ||
| 166 | * | ||
| 167 | * @returns 0 if ok, < 0 errno code on error | ||
| 168 | */ | ||
| 169 | int wlp_eda_update_node(struct wlp_eda *eda, | ||
| 170 | const struct uwb_dev_addr *dev_addr, | ||
| 171 | struct wlp_wss *wss, | ||
| 172 | const unsigned char virt_addr[ETH_ALEN], | ||
| 173 | const u8 tag, const enum wlp_wss_connect state) | ||
| 174 | { | ||
| 175 | int result = -ENOENT; | ||
| 176 | struct wlp_eda_node *itr; | ||
| 177 | unsigned long flags; | ||
| 178 | |||
| 179 | spin_lock_irqsave(&eda->lock, flags); | ||
| 180 | list_for_each_entry(itr, &eda->cache, list_node) { | ||
| 181 | if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { | ||
| 182 | /* Found it, update it */ | ||
| 183 | itr->wss = wss; | ||
| 184 | memcpy(itr->virt_addr, virt_addr, | ||
| 185 | sizeof(itr->virt_addr)); | ||
| 186 | itr->tag = tag; | ||
| 187 | itr->state = state; | ||
| 188 | result = 0; | ||
| 189 | goto out_unlock; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | /* Not found */ | ||
| 193 | out_unlock: | ||
| 194 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 195 | return result; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 199 | * Update only state field of an address mapping | ||
| 200 | * | ||
| 201 | * @returns 0 if ok, < 0 errno code on error | ||
| 202 | */ | ||
| 203 | int wlp_eda_update_node_state(struct wlp_eda *eda, | ||
| 204 | const struct uwb_dev_addr *dev_addr, | ||
| 205 | const enum wlp_wss_connect state) | ||
| 206 | { | ||
| 207 | int result = -ENOENT; | ||
| 208 | struct wlp_eda_node *itr; | ||
| 209 | unsigned long flags; | ||
| 210 | |||
| 211 | spin_lock_irqsave(&eda->lock, flags); | ||
| 212 | list_for_each_entry(itr, &eda->cache, list_node) { | ||
| 213 | if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { | ||
| 214 | /* Found it, update it */ | ||
| 215 | itr->state = state; | ||
| 216 | result = 0; | ||
| 217 | goto out_unlock; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | /* Not found */ | ||
| 221 | out_unlock: | ||
| 222 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 223 | return result; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* | ||
| 227 | * Return contents of EDA cache entry | ||
| 228 | * | ||
| 229 | * @dev_addr: index to EDA cache | ||
| 230 | * @eda_entry: pointer to where contents of EDA cache will be copied | ||
| 231 | */ | ||
| 232 | int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr, | ||
| 233 | struct wlp_eda_node *eda_entry) | ||
| 234 | { | ||
| 235 | int result = -ENOENT; | ||
| 236 | struct wlp_eda_node *itr; | ||
| 237 | unsigned long flags; | ||
| 238 | |||
| 239 | spin_lock_irqsave(&eda->lock, flags); | ||
| 240 | list_for_each_entry(itr, &eda->cache, list_node) { | ||
| 241 | if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { | ||
| 242 | *eda_entry = *itr; | ||
| 243 | result = 0; | ||
| 244 | goto out_unlock; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | /* Not found */ | ||
| 248 | out_unlock: | ||
| 249 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 250 | return result; | ||
| 251 | } | ||
| 252 | |||
| 253 | /* | ||
| 254 | * Execute function for every element in the cache | ||
| 255 | * | ||
| 256 | * @function: function to execute on element of cache (must be atomic) | ||
| 257 | * @priv: private data of function | ||
| 258 | * @returns: result of first function that failed, or last function | ||
| 259 | * executed if no function failed. | ||
| 260 | * | ||
| 261 | * Stop executing when function returns error for any element in cache. | ||
| 262 | * | ||
| 263 | * IMPORTANT: We are using a spinlock here: the function executed on each | ||
| 264 | * element has to be atomic. | ||
| 265 | */ | ||
| 266 | int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function, | ||
| 267 | void *priv) | ||
| 268 | { | ||
| 269 | int result = 0; | ||
| 270 | struct wlp *wlp = container_of(eda, struct wlp, eda); | ||
| 271 | struct wlp_eda_node *entry; | ||
| 272 | unsigned long flags; | ||
| 273 | |||
| 274 | spin_lock_irqsave(&eda->lock, flags); | ||
| 275 | list_for_each_entry(entry, &eda->cache, list_node) { | ||
| 276 | result = (*function)(wlp, entry, priv); | ||
| 277 | if (result < 0) | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | |||
| 284 | /* | ||
| 285 | * Execute function for single element in the cache (return dev addr) | ||
| 286 | * | ||
| 287 | * @virt_addr: index into EDA cache used to determine which element to | ||
| 288 | * execute the function on | ||
| 289 | * @dev_addr: device address of element in cache will be returned using | ||
| 290 | * @dev_addr | ||
| 291 | * @function: function to execute on element of cache (must be atomic) | ||
| 292 | * @priv: private data of function | ||
| 293 | * @returns: result of function | ||
| 294 | * | ||
| 295 | * IMPORTANT: We are using a spinlock here: the function executed on the | ||
| 296 | * element has to be atomic. | ||
| 297 | */ | ||
| 298 | int wlp_eda_for_virtual(struct wlp_eda *eda, | ||
| 299 | const unsigned char virt_addr[ETH_ALEN], | ||
| 300 | struct uwb_dev_addr *dev_addr, | ||
| 301 | wlp_eda_for_each_f function, | ||
| 302 | void *priv) | ||
| 303 | { | ||
| 304 | int result = 0; | ||
| 305 | struct wlp *wlp = container_of(eda, struct wlp, eda); | ||
| 306 | struct wlp_eda_node *itr; | ||
| 307 | unsigned long flags; | ||
| 308 | int found = 0; | ||
| 309 | |||
| 310 | spin_lock_irqsave(&eda->lock, flags); | ||
| 311 | list_for_each_entry(itr, &eda->cache, list_node) { | ||
| 312 | if (!memcmp(itr->virt_addr, virt_addr, | ||
| 313 | sizeof(itr->virt_addr))) { | ||
| 314 | result = (*function)(wlp, itr, priv); | ||
| 315 | *dev_addr = itr->dev_addr; | ||
| 316 | found = 1; | ||
| 317 | break; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | if (!found) | ||
| 321 | result = -ENODEV; | ||
| 322 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 323 | return result; | ||
| 324 | } | ||
| 325 | |||
| 326 | static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED", | ||
| 327 | "WLP_WSS_CONNECTED", | ||
| 328 | "WLP_WSS_CONNECT_FAILED", | ||
| 329 | }; | ||
| 330 | |||
| 331 | static const char *wlp_wss_connect_state_str(unsigned id) | ||
| 332 | { | ||
| 333 | if (id >= ARRAY_SIZE(__wlp_wss_connect_state)) | ||
| 334 | return "unknown WSS connection state"; | ||
| 335 | return __wlp_wss_connect_state[id]; | ||
| 336 | } | ||
| 337 | |||
| 338 | /* | ||
| 339 | * View EDA cache from user space | ||
| 340 | * | ||
| 341 | * A debugging feature to give user visibility into the EDA cache. Also | ||
| 342 | * used to display members of WSS to user (called from wlp_wss_members_show()) | ||
| 343 | */ | ||
| 344 | ssize_t wlp_eda_show(struct wlp *wlp, char *buf) | ||
| 345 | { | ||
| 346 | ssize_t result = 0; | ||
| 347 | struct wlp_eda_node *entry; | ||
| 348 | unsigned long flags; | ||
| 349 | struct wlp_eda *eda = &wlp->eda; | ||
| 350 | spin_lock_irqsave(&eda->lock, flags); | ||
| 351 | result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr " | ||
| 352 | "tag state virt_addr\n"); | ||
| 353 | list_for_each_entry(entry, &eda->cache, list_node) { | ||
| 354 | result += scnprintf(buf + result, PAGE_SIZE - result, | ||
| 355 | "%pM %02x:%02x %p 0x%02x %s %pM\n", | ||
| 356 | entry->eth_addr, | ||
| 357 | entry->dev_addr.data[1], | ||
| 358 | entry->dev_addr.data[0], entry->wss, | ||
| 359 | entry->tag, | ||
| 360 | wlp_wss_connect_state_str(entry->state), | ||
| 361 | entry->virt_addr); | ||
| 362 | if (result >= PAGE_SIZE) | ||
| 363 | break; | ||
| 364 | } | ||
| 365 | spin_unlock_irqrestore(&eda->lock, flags); | ||
| 366 | return result; | ||
| 367 | } | ||
| 368 | EXPORT_SYMBOL_GPL(wlp_eda_show); | ||
| 369 | |||
| 370 | /* | ||
| 371 | * Add new EDA cache entry based on user input in sysfs | ||
| 372 | * | ||
| 373 | * Should only be used for debugging. | ||
| 374 | * | ||
| 375 | * The WSS is assumed to be the only WSS supported. This needs to be | ||
| 376 | * redesigned when we support more than one WSS. | ||
| 377 | */ | ||
| 378 | ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size) | ||
| 379 | { | ||
| 380 | ssize_t result; | ||
| 381 | struct wlp_eda *eda = &wlp->eda; | ||
| 382 | u8 eth_addr[6]; | ||
| 383 | struct uwb_dev_addr dev_addr; | ||
| 384 | u8 tag; | ||
| 385 | unsigned state; | ||
| 386 | |||
| 387 | result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx " | ||
| 388 | "%02hhx:%02hhx %02hhx %u\n", | ||
| 389 | ð_addr[0], ð_addr[1], | ||
| 390 | ð_addr[2], ð_addr[3], | ||
| 391 | ð_addr[4], ð_addr[5], | ||
| 392 | &dev_addr.data[1], &dev_addr.data[0], &tag, &state); | ||
| 393 | switch (result) { | ||
| 394 | case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */ | ||
| 395 | /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/ | ||
| 396 | result = -ENOSYS; | ||
| 397 | break; | ||
| 398 | case 10: | ||
| 399 | state = state >= 1 ? 1 : 0; | ||
| 400 | result = wlp_eda_create_node(eda, eth_addr, &dev_addr); | ||
| 401 | if (result < 0 && result != -EEXIST) | ||
| 402 | goto error; | ||
| 403 | /* Set virtual addr to be same as MAC */ | ||
| 404 | result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss, | ||
| 405 | eth_addr, tag, state); | ||
| 406 | if (result < 0) | ||
| 407 | goto error; | ||
| 408 | break; | ||
| 409 | default: /* bad format */ | ||
| 410 | result = -EINVAL; | ||
| 411 | } | ||
| 412 | error: | ||
| 413 | return result < 0 ? result : size; | ||
| 414 | } | ||
| 415 | EXPORT_SYMBOL_GPL(wlp_eda_store); | ||
diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c deleted file mode 100644 index 3a8e033dce21..000000000000 --- a/drivers/uwb/wlp/messages.c +++ /dev/null | |||
| @@ -1,1798 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * Message construction and parsing | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Intel Corporation | ||
| 6 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: docs | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/wlp.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | |||
| 29 | #include "wlp-internal.h" | ||
| 30 | |||
| 31 | static | ||
| 32 | const char *__wlp_assoc_frame[] = { | ||
| 33 | [WLP_ASSOC_D1] = "WLP_ASSOC_D1", | ||
| 34 | [WLP_ASSOC_D2] = "WLP_ASSOC_D2", | ||
| 35 | [WLP_ASSOC_M1] = "WLP_ASSOC_M1", | ||
| 36 | [WLP_ASSOC_M2] = "WLP_ASSOC_M2", | ||
| 37 | [WLP_ASSOC_M3] = "WLP_ASSOC_M3", | ||
| 38 | [WLP_ASSOC_M4] = "WLP_ASSOC_M4", | ||
| 39 | [WLP_ASSOC_M5] = "WLP_ASSOC_M5", | ||
| 40 | [WLP_ASSOC_M6] = "WLP_ASSOC_M6", | ||
| 41 | [WLP_ASSOC_M7] = "WLP_ASSOC_M7", | ||
| 42 | [WLP_ASSOC_M8] = "WLP_ASSOC_M8", | ||
| 43 | [WLP_ASSOC_F0] = "WLP_ASSOC_F0", | ||
| 44 | [WLP_ASSOC_E1] = "WLP_ASSOC_E1", | ||
| 45 | [WLP_ASSOC_E2] = "WLP_ASSOC_E2", | ||
| 46 | [WLP_ASSOC_C1] = "WLP_ASSOC_C1", | ||
| 47 | [WLP_ASSOC_C2] = "WLP_ASSOC_C2", | ||
| 48 | [WLP_ASSOC_C3] = "WLP_ASSOC_C3", | ||
| 49 | [WLP_ASSOC_C4] = "WLP_ASSOC_C4", | ||
| 50 | }; | ||
| 51 | |||
| 52 | static const char *wlp_assoc_frame_str(unsigned id) | ||
| 53 | { | ||
| 54 | if (id >= ARRAY_SIZE(__wlp_assoc_frame)) | ||
| 55 | return "unknown association frame"; | ||
| 56 | return __wlp_assoc_frame[id]; | ||
| 57 | } | ||
| 58 | |||
| 59 | static const char *__wlp_assc_error[] = { | ||
| 60 | "none", | ||
| 61 | "Authenticator Failure", | ||
| 62 | "Rogue activity suspected", | ||
| 63 | "Device busy", | ||
| 64 | "Setup Locked", | ||
| 65 | "Registrar not ready", | ||
| 66 | "Invalid WSS selection", | ||
| 67 | "Message timeout", | ||
| 68 | "Enrollment session timeout", | ||
| 69 | "Device password invalid", | ||
| 70 | "Unsupported version", | ||
| 71 | "Internal error", | ||
| 72 | "Undefined error", | ||
| 73 | "Numeric comparison failure", | ||
| 74 | "Waiting for user input", | ||
| 75 | }; | ||
| 76 | |||
| 77 | static const char *wlp_assc_error_str(unsigned id) | ||
| 78 | { | ||
| 79 | if (id >= ARRAY_SIZE(__wlp_assc_error)) | ||
| 80 | return "unknown WLP association error"; | ||
| 81 | return __wlp_assc_error[id]; | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type, | ||
| 85 | size_t len) | ||
| 86 | { | ||
| 87 | hdr->type = cpu_to_le16(type); | ||
| 88 | hdr->length = cpu_to_le16(len); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* | ||
| 92 | * Populate fields of a constant sized attribute | ||
| 93 | * | ||
| 94 | * @returns: total size of attribute including size of new value | ||
| 95 | * | ||
| 96 | * We have two instances of this function (wlp_pset and wlp_set): one takes | ||
| 97 | * the value as a parameter, the other takes a pointer to the value as | ||
| 98 | * parameter. They thus only differ in how the value is assigned to the | ||
| 99 | * attribute. | ||
| 100 | * | ||
| 101 | * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of | ||
| 102 | * sizeof(type) to be able to use this same code for the structures that | ||
| 103 | * contain 8bit enum values and be able to deal with pointer types. | ||
| 104 | */ | ||
| 105 | #define wlp_set(type, type_code, name) \ | ||
| 106 | static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ | ||
| 107 | { \ | ||
| 108 | wlp_set_attr_hdr(&attr->hdr, type_code, \ | ||
| 109 | sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ | ||
| 110 | attr->name = value; \ | ||
| 111 | return sizeof(*attr); \ | ||
| 112 | } | ||
| 113 | |||
| 114 | #define wlp_pset(type, type_code, name) \ | ||
| 115 | static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ | ||
| 116 | { \ | ||
| 117 | wlp_set_attr_hdr(&attr->hdr, type_code, \ | ||
| 118 | sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ | ||
| 119 | attr->name = *value; \ | ||
| 120 | return sizeof(*attr); \ | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Populate fields of a variable attribute | ||
| 125 | * | ||
| 126 | * @returns: total size of attribute including size of new value | ||
| 127 | * | ||
| 128 | * Provided with a pointer to the memory area reserved for the | ||
| 129 | * attribute structure, the field is populated with the value. The | ||
| 130 | * reserved memory has to contain enough space for the value. | ||
| 131 | */ | ||
| 132 | #define wlp_vset(type, type_code, name) \ | ||
| 133 | static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \ | ||
| 134 | size_t len) \ | ||
| 135 | { \ | ||
| 136 | wlp_set_attr_hdr(&attr->hdr, type_code, len); \ | ||
| 137 | memcpy(attr->name, value, len); \ | ||
| 138 | return sizeof(*attr) + len; \ | ||
| 139 | } | ||
| 140 | |||
| 141 | wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name) | ||
| 142 | wlp_vset(char *, WLP_ATTR_MANUF, manufacturer) | ||
| 143 | wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type) | ||
| 144 | wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name) | ||
| 145 | wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr) | ||
| 146 | wlp_vset(char *, WLP_ATTR_SERIAL, serial) | ||
| 147 | wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name) | ||
| 148 | wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e) | ||
| 149 | wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r) | ||
| 150 | wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid) | ||
| 151 | wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) | ||
| 152 | /*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/ | ||
| 153 | wlp_set(u8, WLP_ATTR_WLP_VER, version) | ||
| 154 | wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) | ||
| 155 | wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) | ||
| 156 | wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl) | ||
| 157 | wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) | ||
| 158 | wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast) | ||
| 159 | wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce) | ||
| 160 | wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce) | ||
| 161 | wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag) | ||
| 162 | wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt) | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Fill in the WSS information attributes | ||
| 166 | * | ||
| 167 | * We currently only support one WSS, and this is assumed in this function | ||
| 168 | * that can populate only one WSS information attribute. | ||
| 169 | */ | ||
| 170 | static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr, | ||
| 171 | struct wlp_wss *wss) | ||
| 172 | { | ||
| 173 | size_t datalen; | ||
| 174 | void *ptr = attr->wss_info; | ||
| 175 | size_t used = sizeof(*attr); | ||
| 176 | |||
| 177 | datalen = sizeof(struct wlp_wss_info) + strlen(wss->name); | ||
| 178 | wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen); | ||
| 179 | used = wlp_set_wssid(ptr, &wss->wssid); | ||
| 180 | used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name)); | ||
| 181 | used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll); | ||
| 182 | used += wlp_set_wss_sec_status(ptr + used, wss->secure_status); | ||
| 183 | used += wlp_set_wss_bcast(ptr + used, &wss->bcast); | ||
| 184 | return sizeof(*attr) + used; | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Verify attribute header | ||
| 189 | * | ||
| 190 | * @hdr: Pointer to attribute header that will be verified. | ||
| 191 | * @type: Expected attribute type. | ||
| 192 | * @len: Expected length of attribute value (excluding header). | ||
| 193 | * | ||
| 194 | * Most attribute values have a known length even when they do have a | ||
| 195 | * length field. This knowledge can be used via this function to verify | ||
| 196 | * that the length field matches the expected value. | ||
| 197 | */ | ||
| 198 | static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr, | ||
| 199 | enum wlp_attr_type type, unsigned len) | ||
| 200 | { | ||
| 201 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 202 | |||
| 203 | if (le16_to_cpu(hdr->type) != type) { | ||
| 204 | dev_err(dev, "WLP: unexpected header type. Expected " | ||
| 205 | "%u, got %u.\n", type, le16_to_cpu(hdr->type)); | ||
| 206 | return -EINVAL; | ||
| 207 | } | ||
| 208 | if (le16_to_cpu(hdr->length) != len) { | ||
| 209 | dev_err(dev, "WLP: unexpected length in header. Expected " | ||
| 210 | "%u, got %u.\n", len, le16_to_cpu(hdr->length)); | ||
| 211 | return -EINVAL; | ||
| 212 | } | ||
| 213 | return 0; | ||
| 214 | } | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Check if header of WSS information attribute valid | ||
| 218 | * | ||
| 219 | * @returns: length of WSS attributes (value of length attribute field) if | ||
| 220 | * valid WSS information attribute found | ||
| 221 | * -ENODATA if no WSS information attribute found | ||
| 222 | * -EIO other error occured | ||
| 223 | * | ||
| 224 | * The WSS information attribute is optional. The function will be provided | ||
| 225 | * with a pointer to data that could _potentially_ be a WSS information | ||
| 226 | * attribute. If a valid WSS information attribute is found it will return | ||
| 227 | * 0, if no WSS information attribute is found it will return -ENODATA, and | ||
| 228 | * another error will be returned if it is a WSS information attribute, but | ||
| 229 | * some parsing failure occured. | ||
| 230 | */ | ||
| 231 | static int wlp_check_wss_info_attr_hdr(struct wlp *wlp, | ||
| 232 | struct wlp_attr_hdr *hdr, size_t buflen) | ||
| 233 | { | ||
| 234 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 235 | size_t len; | ||
| 236 | int result = 0; | ||
| 237 | |||
| 238 | if (buflen < sizeof(*hdr)) { | ||
| 239 | dev_err(dev, "WLP: Not enough space in buffer to parse" | ||
| 240 | " WSS information attribute header.\n"); | ||
| 241 | result = -EIO; | ||
| 242 | goto out; | ||
| 243 | } | ||
| 244 | if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) { | ||
| 245 | /* WSS information is optional */ | ||
| 246 | result = -ENODATA; | ||
| 247 | goto out; | ||
| 248 | } | ||
| 249 | len = le16_to_cpu(hdr->length); | ||
| 250 | if (buflen < sizeof(*hdr) + len) { | ||
| 251 | dev_err(dev, "WLP: Not enough space in buffer to parse " | ||
| 252 | "variable data. Got %d, expected %d.\n", | ||
| 253 | (int)buflen, (int)(sizeof(*hdr) + len)); | ||
| 254 | result = -EIO; | ||
| 255 | goto out; | ||
| 256 | } | ||
| 257 | result = len; | ||
| 258 | out: | ||
| 259 | return result; | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | static ssize_t wlp_get_attribute(struct wlp *wlp, u16 type_code, | ||
| 264 | struct wlp_attr_hdr *attr_hdr, void *value, ssize_t value_len, | ||
| 265 | ssize_t buflen) | ||
| 266 | { | ||
| 267 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 268 | ssize_t attr_len = sizeof(*attr_hdr) + value_len; | ||
| 269 | if (buflen < 0) | ||
| 270 | return -EINVAL; | ||
| 271 | if (buflen < attr_len) { | ||
| 272 | dev_err(dev, "WLP: Not enough space in buffer to parse" | ||
| 273 | " attribute field. Need %d, received %zu\n", | ||
| 274 | (int)attr_len, buflen); | ||
| 275 | return -EIO; | ||
| 276 | } | ||
| 277 | if (wlp_check_attr_hdr(wlp, attr_hdr, type_code, value_len) < 0) { | ||
| 278 | dev_err(dev, "WLP: Header verification failed. \n"); | ||
| 279 | return -EINVAL; | ||
| 280 | } | ||
| 281 | memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), value_len); | ||
| 282 | return attr_len; | ||
| 283 | } | ||
| 284 | |||
| 285 | static ssize_t wlp_vget_attribute(struct wlp *wlp, u16 type_code, | ||
| 286 | struct wlp_attr_hdr *attr_hdr, void *value, ssize_t max_value_len, | ||
| 287 | ssize_t buflen) | ||
| 288 | { | ||
| 289 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 290 | size_t len; | ||
| 291 | if (buflen < 0) | ||
| 292 | return -EINVAL; | ||
| 293 | if (buflen < sizeof(*attr_hdr)) { | ||
| 294 | dev_err(dev, "WLP: Not enough space in buffer to parse" | ||
| 295 | " header.\n"); | ||
| 296 | return -EIO; | ||
| 297 | } | ||
| 298 | if (le16_to_cpu(attr_hdr->type) != type_code) { | ||
| 299 | dev_err(dev, "WLP: Unexpected attribute type. Got %u, " | ||
| 300 | "expected %u.\n", le16_to_cpu(attr_hdr->type), | ||
| 301 | type_code); | ||
| 302 | return -EINVAL; | ||
| 303 | } | ||
| 304 | len = le16_to_cpu(attr_hdr->length); | ||
| 305 | if (len > max_value_len) { | ||
| 306 | dev_err(dev, "WLP: Attribute larger than maximum " | ||
| 307 | "allowed. Received %zu, max is %d.\n", len, | ||
| 308 | (int)max_value_len); | ||
| 309 | return -EFBIG; | ||
| 310 | } | ||
| 311 | if (buflen < sizeof(*attr_hdr) + len) { | ||
| 312 | dev_err(dev, "WLP: Not enough space in buffer to parse " | ||
| 313 | "variable data.\n"); | ||
| 314 | return -EIO; | ||
| 315 | } | ||
| 316 | memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), len); | ||
| 317 | return sizeof(*attr_hdr) + len; | ||
| 318 | } | ||
| 319 | |||
| 320 | /** | ||
| 321 | * Get value of attribute from fixed size attribute field. | ||
| 322 | * | ||
| 323 | * @attr: Pointer to attribute field. | ||
| 324 | * @value: Pointer to variable in which attribute value will be placed. | ||
| 325 | * @buflen: Size of buffer in which attribute field (including header) | ||
| 326 | * can be found. | ||
| 327 | * @returns: Amount of given buffer consumed by parsing for this attribute. | ||
| 328 | * | ||
| 329 | * The size and type of the value is known by the type of the attribute. | ||
| 330 | */ | ||
| 331 | #define wlp_get(type, type_code, name) \ | ||
| 332 | ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \ | ||
| 333 | type *value, ssize_t buflen) \ | ||
| 334 | { \ | ||
| 335 | return wlp_get_attribute(wlp, (type_code), &attr->hdr, \ | ||
| 336 | value, sizeof(*value), buflen); \ | ||
| 337 | } | ||
| 338 | |||
| 339 | #define wlp_get_sparse(type, type_code, name) \ | ||
| 340 | static wlp_get(type, type_code, name) | ||
| 341 | |||
| 342 | /** | ||
| 343 | * Get value of attribute from variable sized attribute field. | ||
| 344 | * | ||
| 345 | * @max: The maximum size of this attribute. This value is dictated by | ||
| 346 | * the maximum value from the WLP specification. | ||
| 347 | * | ||
| 348 | * @attr: Pointer to attribute field. | ||
| 349 | * @value: Pointer to variable that will contain the value. The memory | ||
| 350 | * must already have been allocated for this value. | ||
| 351 | * @buflen: Size of buffer in which attribute field (including header) | ||
| 352 | * can be found. | ||
| 353 | * @returns: Amount of given bufferconsumed by parsing for this attribute. | ||
| 354 | */ | ||
| 355 | #define wlp_vget(type_val, type_code, name, max) \ | ||
| 356 | static ssize_t wlp_get_##name(struct wlp *wlp, \ | ||
| 357 | struct wlp_attr_##name *attr, \ | ||
| 358 | type_val *value, ssize_t buflen) \ | ||
| 359 | { \ | ||
| 360 | return wlp_vget_attribute(wlp, (type_code), &attr->hdr, \ | ||
| 361 | value, (max), buflen); \ | ||
| 362 | } | ||
| 363 | |||
| 364 | wlp_get(u8, WLP_ATTR_WLP_VER, version) | ||
| 365 | wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) | ||
| 366 | wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) | ||
| 367 | wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) | ||
| 368 | wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e) | ||
| 369 | wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r) | ||
| 370 | wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid) | ||
| 371 | wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl) | ||
| 372 | wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) | ||
| 373 | wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast) | ||
| 374 | wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag) | ||
| 375 | wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt) | ||
| 376 | wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce) | ||
| 377 | wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce) | ||
| 378 | |||
| 379 | /* The buffers for the device info attributes can be found in the | ||
| 380 | * wlp_device_info struct. These buffers contain one byte more than the | ||
| 381 | * max allowed by the spec - this is done to be able to add the | ||
| 382 | * terminating \0 for user display. This terminating byte is not required | ||
| 383 | * in the actual attribute field (because it has a length field) so the | ||
| 384 | * maximum allowed for this value is one less than its size in the | ||
| 385 | * structure. | ||
| 386 | */ | ||
| 387 | wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name, | ||
| 388 | FIELD_SIZEOF(struct wlp_wss, name) - 1) | ||
| 389 | wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name, | ||
| 390 | FIELD_SIZEOF(struct wlp_device_info, name) - 1) | ||
| 391 | wlp_vget(char, WLP_ATTR_MANUF, manufacturer, | ||
| 392 | FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1) | ||
| 393 | wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name, | ||
| 394 | FIELD_SIZEOF(struct wlp_device_info, model_name) - 1) | ||
| 395 | wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr, | ||
| 396 | FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1) | ||
| 397 | wlp_vget(char, WLP_ATTR_SERIAL, serial, | ||
| 398 | FIELD_SIZEOF(struct wlp_device_info, serial) - 1) | ||
| 399 | |||
| 400 | /** | ||
| 401 | * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info | ||
| 402 | * | ||
| 403 | * @attr: pointer to WSS name attribute in WSS information attribute field | ||
| 404 | * @info: structure that will be populated with data from WSS information | ||
| 405 | * field (WSS name, Accept enroll, secure status, broadcast address) | ||
| 406 | * @buflen: size of buffer | ||
| 407 | * | ||
| 408 | * Although the WSSID attribute forms part of the WSS info attribute it is | ||
| 409 | * retrieved separately and stored in a different location. | ||
| 410 | */ | ||
| 411 | static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp, | ||
| 412 | struct wlp_attr_hdr *attr, | ||
| 413 | struct wlp_wss_tmp_info *info, | ||
| 414 | ssize_t buflen) | ||
| 415 | { | ||
| 416 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 417 | void *ptr = attr; | ||
| 418 | size_t used = 0; | ||
| 419 | ssize_t result = -EINVAL; | ||
| 420 | |||
| 421 | result = wlp_get_wss_name(wlp, ptr, info->name, buflen); | ||
| 422 | if (result < 0) { | ||
| 423 | dev_err(dev, "WLP: unable to obtain WSS name from " | ||
| 424 | "WSS info in D2 message.\n"); | ||
| 425 | goto error_parse; | ||
| 426 | } | ||
| 427 | used += result; | ||
| 428 | |||
| 429 | result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll, | ||
| 430 | buflen - used); | ||
| 431 | if (result < 0) { | ||
| 432 | dev_err(dev, "WLP: unable to obtain accepting " | ||
| 433 | "enrollment from WSS info in D2 message.\n"); | ||
| 434 | goto error_parse; | ||
| 435 | } | ||
| 436 | if (info->accept_enroll != 0 && info->accept_enroll != 1) { | ||
| 437 | dev_err(dev, "WLP: invalid value for accepting " | ||
| 438 | "enrollment in D2 message.\n"); | ||
| 439 | result = -EINVAL; | ||
| 440 | goto error_parse; | ||
| 441 | } | ||
| 442 | used += result; | ||
| 443 | |||
| 444 | result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status, | ||
| 445 | buflen - used); | ||
| 446 | if (result < 0) { | ||
| 447 | dev_err(dev, "WLP: unable to obtain secure " | ||
| 448 | "status from WSS info in D2 message.\n"); | ||
| 449 | goto error_parse; | ||
| 450 | } | ||
| 451 | if (info->sec_status != 0 && info->sec_status != 1) { | ||
| 452 | dev_err(dev, "WLP: invalid value for secure " | ||
| 453 | "status in D2 message.\n"); | ||
| 454 | result = -EINVAL; | ||
| 455 | goto error_parse; | ||
| 456 | } | ||
| 457 | used += result; | ||
| 458 | |||
| 459 | result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast, | ||
| 460 | buflen - used); | ||
| 461 | if (result < 0) { | ||
| 462 | dev_err(dev, "WLP: unable to obtain broadcast " | ||
| 463 | "address from WSS info in D2 message.\n"); | ||
| 464 | goto error_parse; | ||
| 465 | } | ||
| 466 | used += result; | ||
| 467 | result = used; | ||
| 468 | error_parse: | ||
| 469 | return result; | ||
| 470 | } | ||
| 471 | |||
| 472 | /** | ||
| 473 | * Create a new WSSID entry for the neighbor, allocate temporary storage | ||
| 474 | * | ||
| 475 | * Each neighbor can have many WSS active. We maintain a list of WSSIDs | ||
| 476 | * advertised by neighbor. During discovery we also cache information about | ||
| 477 | * these WSS in temporary storage. | ||
| 478 | * | ||
| 479 | * The temporary storage will be removed after it has been used (eg. | ||
| 480 | * displayed to user), the wssid element will be removed from the list when | ||
| 481 | * the neighbor is rediscovered or when it disappears. | ||
| 482 | */ | ||
| 483 | static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp, | ||
| 484 | struct wlp_neighbor_e *neighbor) | ||
| 485 | { | ||
| 486 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 487 | struct wlp_wssid_e *wssid_e; | ||
| 488 | |||
| 489 | wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL); | ||
| 490 | if (wssid_e == NULL) { | ||
| 491 | dev_err(dev, "WLP: unable to allocate memory " | ||
| 492 | "for WSS information.\n"); | ||
| 493 | goto error_alloc; | ||
| 494 | } | ||
| 495 | wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL); | ||
| 496 | if (wssid_e->info == NULL) { | ||
| 497 | dev_err(dev, "WLP: unable to allocate memory " | ||
| 498 | "for temporary WSS information.\n"); | ||
| 499 | kfree(wssid_e); | ||
| 500 | wssid_e = NULL; | ||
| 501 | goto error_alloc; | ||
| 502 | } | ||
| 503 | list_add(&wssid_e->node, &neighbor->wssid); | ||
| 504 | error_alloc: | ||
| 505 | return wssid_e; | ||
| 506 | } | ||
| 507 | |||
| 508 | /** | ||
| 509 | * Parse WSS information attribute | ||
| 510 | * | ||
| 511 | * @attr: pointer to WSS information attribute header | ||
| 512 | * @buflen: size of buffer in which WSS information attribute appears | ||
| 513 | * @wssid: will place wssid from WSS info attribute in this location | ||
| 514 | * @wss_info: will place other information from WSS information attribute | ||
| 515 | * in this location | ||
| 516 | * | ||
| 517 | * memory for @wssid and @wss_info must be allocated when calling this | ||
| 518 | */ | ||
| 519 | static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr, | ||
| 520 | size_t buflen, struct wlp_uuid *wssid, | ||
| 521 | struct wlp_wss_tmp_info *wss_info) | ||
| 522 | { | ||
| 523 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 524 | ssize_t result; | ||
| 525 | size_t len; | ||
| 526 | size_t used = 0; | ||
| 527 | void *ptr; | ||
| 528 | |||
| 529 | result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr, | ||
| 530 | buflen); | ||
| 531 | if (result < 0) | ||
| 532 | goto out; | ||
| 533 | len = result; | ||
| 534 | used = sizeof(*attr); | ||
| 535 | ptr = attr; | ||
| 536 | |||
| 537 | result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used); | ||
| 538 | if (result < 0) { | ||
| 539 | dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n"); | ||
| 540 | goto out; | ||
| 541 | } | ||
| 542 | used += result; | ||
| 543 | result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info, | ||
| 544 | buflen - used); | ||
| 545 | if (result < 0) { | ||
| 546 | dev_err(dev, "WLP: unable to obtain WSS information " | ||
| 547 | "from WSS information attributes. \n"); | ||
| 548 | goto out; | ||
| 549 | } | ||
| 550 | used += result; | ||
| 551 | if (len + sizeof(*attr) != used) { | ||
| 552 | dev_err(dev, "WLP: Amount of data parsed does not " | ||
| 553 | "match length field. Parsed %zu, length " | ||
| 554 | "field %zu. \n", used, len); | ||
| 555 | result = -EINVAL; | ||
| 556 | goto out; | ||
| 557 | } | ||
| 558 | result = used; | ||
| 559 | out: | ||
| 560 | return result; | ||
| 561 | } | ||
| 562 | |||
| 563 | /** | ||
| 564 | * Retrieve WSS info from association frame | ||
| 565 | * | ||
| 566 | * @attr: pointer to WSS information attribute | ||
| 567 | * @neighbor: ptr to neighbor being discovered, NULL if enrollment in | ||
| 568 | * progress | ||
| 569 | * @wss: ptr to WSS being enrolled in, NULL if discovery in progress | ||
| 570 | * @buflen: size of buffer in which WSS information appears | ||
| 571 | * | ||
| 572 | * The WSS information attribute appears in the D2 association message. | ||
| 573 | * This message is used in two ways: to discover all neighbors or to enroll | ||
| 574 | * into a WSS activated by a neighbor. During discovery we only want to | ||
| 575 | * store the WSS info in a cache, to be deleted right after it has been | ||
| 576 | * used (eg. displayed to the user). During enrollment we store the WSS | ||
| 577 | * information for the lifetime of enrollment. | ||
| 578 | * | ||
| 579 | * During discovery we are interested in all WSS information, during | ||
| 580 | * enrollment we are only interested in the WSS being enrolled in. Even so, | ||
| 581 | * when in enrollment we keep parsing the message after finding the WSS of | ||
| 582 | * interest, this simplifies the calling routine in that it can be sure | ||
| 583 | * that all WSS information attributes have been parsed out of the message. | ||
| 584 | * | ||
| 585 | * Association frame is process with nbmutex held. The list access is safe. | ||
| 586 | */ | ||
| 587 | static ssize_t wlp_get_all_wss_info(struct wlp *wlp, | ||
| 588 | struct wlp_attr_wss_info *attr, | ||
| 589 | struct wlp_neighbor_e *neighbor, | ||
| 590 | struct wlp_wss *wss, ssize_t buflen) | ||
| 591 | { | ||
| 592 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 593 | size_t used = 0; | ||
| 594 | ssize_t result = -EINVAL; | ||
| 595 | struct wlp_attr_wss_info *cur; | ||
| 596 | struct wlp_uuid wssid; | ||
| 597 | struct wlp_wss_tmp_info wss_info; | ||
| 598 | unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */ | ||
| 599 | struct wlp_wssid_e *wssid_e; | ||
| 600 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 601 | |||
| 602 | if (buflen < 0) | ||
| 603 | goto out; | ||
| 604 | |||
| 605 | if (neighbor != NULL && wss == NULL) | ||
| 606 | enroll = 0; /* discovery */ | ||
| 607 | else if (wss != NULL && neighbor == NULL) | ||
| 608 | enroll = 1; /* enrollment */ | ||
| 609 | else | ||
| 610 | goto out; | ||
| 611 | |||
| 612 | cur = attr; | ||
| 613 | while (buflen - used > 0) { | ||
| 614 | memset(&wss_info, 0, sizeof(wss_info)); | ||
| 615 | cur = (void *)cur + used; | ||
| 616 | result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid, | ||
| 617 | &wss_info); | ||
| 618 | if (result == -ENODATA) { | ||
| 619 | result = used; | ||
| 620 | goto out; | ||
| 621 | } else if (result < 0) { | ||
| 622 | dev_err(dev, "WLP: Unable to parse WSS information " | ||
| 623 | "from WSS information attribute. \n"); | ||
| 624 | result = -EINVAL; | ||
| 625 | goto error_parse; | ||
| 626 | } | ||
| 627 | if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) { | ||
| 628 | if (wss_info.accept_enroll != 1) { | ||
| 629 | dev_err(dev, "WLP: Requested WSS does " | ||
| 630 | "not accept enrollment.\n"); | ||
| 631 | result = -EINVAL; | ||
| 632 | goto out; | ||
| 633 | } | ||
| 634 | memcpy(wss->name, wss_info.name, sizeof(wss->name)); | ||
| 635 | wss->bcast = wss_info.bcast; | ||
| 636 | wss->secure_status = wss_info.sec_status; | ||
| 637 | wss->accept_enroll = wss_info.accept_enroll; | ||
| 638 | wss->state = WLP_WSS_STATE_PART_ENROLLED; | ||
| 639 | wlp_wss_uuid_print(buf, sizeof(buf), &wssid); | ||
| 640 | dev_dbg(dev, "WLP: Found WSS %s. Enrolling.\n", buf); | ||
| 641 | } else { | ||
| 642 | wssid_e = wlp_create_wssid_e(wlp, neighbor); | ||
| 643 | if (wssid_e == NULL) { | ||
| 644 | dev_err(dev, "WLP: Cannot create new WSSID " | ||
| 645 | "entry for neighbor %02x:%02x.\n", | ||
| 646 | neighbor->uwb_dev->dev_addr.data[1], | ||
| 647 | neighbor->uwb_dev->dev_addr.data[0]); | ||
| 648 | result = -ENOMEM; | ||
| 649 | goto out; | ||
| 650 | } | ||
| 651 | wssid_e->wssid = wssid; | ||
| 652 | *wssid_e->info = wss_info; | ||
| 653 | } | ||
| 654 | used += result; | ||
| 655 | } | ||
| 656 | result = used; | ||
| 657 | error_parse: | ||
| 658 | if (result < 0 && !enroll) /* this was a discovery */ | ||
| 659 | wlp_remove_neighbor_tmp_info(neighbor); | ||
| 660 | out: | ||
| 661 | return result; | ||
| 662 | |||
| 663 | } | ||
| 664 | |||
| 665 | /** | ||
| 666 | * Parse WSS information attributes into cache for discovery | ||
| 667 | * | ||
| 668 | * @attr: the first WSS information attribute in message | ||
| 669 | * @neighbor: the neighbor whose cache will be populated | ||
| 670 | * @buflen: size of the input buffer | ||
| 671 | */ | ||
| 672 | static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp, | ||
| 673 | struct wlp_attr_wss_info *attr, | ||
| 674 | struct wlp_neighbor_e *neighbor, | ||
| 675 | ssize_t buflen) | ||
| 676 | { | ||
| 677 | return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen); | ||
| 678 | } | ||
| 679 | |||
| 680 | /** | ||
| 681 | * Parse WSS information attributes into WSS struct for enrollment | ||
| 682 | * | ||
| 683 | * @attr: the first WSS information attribute in message | ||
| 684 | * @wss: the WSS that will be enrolled | ||
| 685 | * @buflen: size of the input buffer | ||
| 686 | */ | ||
| 687 | static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp, | ||
| 688 | struct wlp_attr_wss_info *attr, | ||
| 689 | struct wlp_wss *wss, ssize_t buflen) | ||
| 690 | { | ||
| 691 | return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen); | ||
| 692 | } | ||
| 693 | |||
| 694 | /** | ||
| 695 | * Construct a D1 association frame | ||
| 696 | * | ||
| 697 | * We use the radio control functions to determine the values of the device | ||
| 698 | * properties. These are of variable length and the total space needed is | ||
| 699 | * tallied first before we start constructing the message. The radio | ||
| 700 | * control functions return strings that are terminated with \0. This | ||
| 701 | * character should not be included in the message (there is a length field | ||
| 702 | * accompanying it in the attribute). | ||
| 703 | */ | ||
| 704 | static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss, | ||
| 705 | struct sk_buff **skb) | ||
| 706 | { | ||
| 707 | |||
| 708 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 709 | int result = 0; | ||
| 710 | struct wlp_device_info *info; | ||
| 711 | size_t used = 0; | ||
| 712 | struct wlp_frame_assoc *_d1; | ||
| 713 | struct sk_buff *_skb; | ||
| 714 | void *d1_itr; | ||
| 715 | |||
| 716 | if (wlp->dev_info == NULL) { | ||
| 717 | result = __wlp_setup_device_info(wlp); | ||
| 718 | if (result < 0) { | ||
| 719 | dev_err(dev, "WLP: Unable to setup device " | ||
| 720 | "information for D1 message.\n"); | ||
| 721 | goto error; | ||
| 722 | } | ||
| 723 | } | ||
| 724 | info = wlp->dev_info; | ||
| 725 | _skb = dev_alloc_skb(sizeof(*_d1) | ||
| 726 | + sizeof(struct wlp_attr_uuid_e) | ||
| 727 | + sizeof(struct wlp_attr_wss_sel_mthd) | ||
| 728 | + sizeof(struct wlp_attr_dev_name) | ||
| 729 | + strlen(info->name) | ||
| 730 | + sizeof(struct wlp_attr_manufacturer) | ||
| 731 | + strlen(info->manufacturer) | ||
| 732 | + sizeof(struct wlp_attr_model_name) | ||
| 733 | + strlen(info->model_name) | ||
| 734 | + sizeof(struct wlp_attr_model_nr) | ||
| 735 | + strlen(info->model_nr) | ||
| 736 | + sizeof(struct wlp_attr_serial) | ||
| 737 | + strlen(info->serial) | ||
| 738 | + sizeof(struct wlp_attr_prim_dev_type) | ||
| 739 | + sizeof(struct wlp_attr_wlp_assc_err)); | ||
| 740 | if (_skb == NULL) { | ||
| 741 | dev_err(dev, "WLP: Cannot allocate memory for association " | ||
| 742 | "message.\n"); | ||
| 743 | result = -ENOMEM; | ||
| 744 | goto error; | ||
| 745 | } | ||
| 746 | _d1 = (void *) _skb->data; | ||
| 747 | _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 748 | _d1->hdr.type = WLP_FRAME_ASSOCIATION; | ||
| 749 | _d1->type = WLP_ASSOC_D1; | ||
| 750 | |||
| 751 | wlp_set_version(&_d1->version, WLP_VERSION); | ||
| 752 | wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1); | ||
| 753 | d1_itr = _d1->attr; | ||
| 754 | used = wlp_set_uuid_e(d1_itr, &wlp->uuid); | ||
| 755 | used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT); | ||
| 756 | used += wlp_set_dev_name(d1_itr + used, info->name, | ||
| 757 | strlen(info->name)); | ||
| 758 | used += wlp_set_manufacturer(d1_itr + used, info->manufacturer, | ||
| 759 | strlen(info->manufacturer)); | ||
| 760 | used += wlp_set_model_name(d1_itr + used, info->model_name, | ||
| 761 | strlen(info->model_name)); | ||
| 762 | used += wlp_set_model_nr(d1_itr + used, info->model_nr, | ||
| 763 | strlen(info->model_nr)); | ||
| 764 | used += wlp_set_serial(d1_itr + used, info->serial, | ||
| 765 | strlen(info->serial)); | ||
| 766 | used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type); | ||
| 767 | used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE); | ||
| 768 | skb_put(_skb, sizeof(*_d1) + used); | ||
| 769 | *skb = _skb; | ||
| 770 | error: | ||
| 771 | return result; | ||
| 772 | } | ||
| 773 | |||
| 774 | /** | ||
| 775 | * Construct a D2 association frame | ||
| 776 | * | ||
| 777 | * We use the radio control functions to determine the values of the device | ||
| 778 | * properties. These are of variable length and the total space needed is | ||
| 779 | * tallied first before we start constructing the message. The radio | ||
| 780 | * control functions return strings that are terminated with \0. This | ||
| 781 | * character should not be included in the message (there is a length field | ||
| 782 | * accompanying it in the attribute). | ||
| 783 | */ | ||
| 784 | static | ||
| 785 | int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss, | ||
| 786 | struct sk_buff **skb, struct wlp_uuid *uuid_e) | ||
| 787 | { | ||
| 788 | |||
| 789 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 790 | int result = 0; | ||
| 791 | struct wlp_device_info *info; | ||
| 792 | size_t used = 0; | ||
| 793 | struct wlp_frame_assoc *_d2; | ||
| 794 | struct sk_buff *_skb; | ||
| 795 | void *d2_itr; | ||
| 796 | size_t mem_needed; | ||
| 797 | |||
| 798 | if (wlp->dev_info == NULL) { | ||
| 799 | result = __wlp_setup_device_info(wlp); | ||
| 800 | if (result < 0) { | ||
| 801 | dev_err(dev, "WLP: Unable to setup device " | ||
| 802 | "information for D2 message.\n"); | ||
| 803 | goto error; | ||
| 804 | } | ||
| 805 | } | ||
| 806 | info = wlp->dev_info; | ||
| 807 | mem_needed = sizeof(*_d2) | ||
| 808 | + sizeof(struct wlp_attr_uuid_e) | ||
| 809 | + sizeof(struct wlp_attr_uuid_r) | ||
| 810 | + sizeof(struct wlp_attr_dev_name) | ||
| 811 | + strlen(info->name) | ||
| 812 | + sizeof(struct wlp_attr_manufacturer) | ||
| 813 | + strlen(info->manufacturer) | ||
| 814 | + sizeof(struct wlp_attr_model_name) | ||
| 815 | + strlen(info->model_name) | ||
| 816 | + sizeof(struct wlp_attr_model_nr) | ||
| 817 | + strlen(info->model_nr) | ||
| 818 | + sizeof(struct wlp_attr_serial) | ||
| 819 | + strlen(info->serial) | ||
| 820 | + sizeof(struct wlp_attr_prim_dev_type) | ||
| 821 | + sizeof(struct wlp_attr_wlp_assc_err); | ||
| 822 | if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) | ||
| 823 | mem_needed += sizeof(struct wlp_attr_wss_info) | ||
| 824 | + sizeof(struct wlp_wss_info) | ||
| 825 | + strlen(wlp->wss.name); | ||
| 826 | _skb = dev_alloc_skb(mem_needed); | ||
| 827 | if (_skb == NULL) { | ||
| 828 | dev_err(dev, "WLP: Cannot allocate memory for association " | ||
| 829 | "message.\n"); | ||
| 830 | result = -ENOMEM; | ||
| 831 | goto error; | ||
| 832 | } | ||
| 833 | _d2 = (void *) _skb->data; | ||
| 834 | _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 835 | _d2->hdr.type = WLP_FRAME_ASSOCIATION; | ||
| 836 | _d2->type = WLP_ASSOC_D2; | ||
| 837 | |||
| 838 | wlp_set_version(&_d2->version, WLP_VERSION); | ||
| 839 | wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2); | ||
| 840 | d2_itr = _d2->attr; | ||
| 841 | used = wlp_set_uuid_e(d2_itr, uuid_e); | ||
| 842 | used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid); | ||
| 843 | if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) | ||
| 844 | used += wlp_set_wss_info(d2_itr + used, &wlp->wss); | ||
| 845 | used += wlp_set_dev_name(d2_itr + used, info->name, | ||
| 846 | strlen(info->name)); | ||
| 847 | used += wlp_set_manufacturer(d2_itr + used, info->manufacturer, | ||
| 848 | strlen(info->manufacturer)); | ||
| 849 | used += wlp_set_model_name(d2_itr + used, info->model_name, | ||
| 850 | strlen(info->model_name)); | ||
| 851 | used += wlp_set_model_nr(d2_itr + used, info->model_nr, | ||
| 852 | strlen(info->model_nr)); | ||
| 853 | used += wlp_set_serial(d2_itr + used, info->serial, | ||
| 854 | strlen(info->serial)); | ||
| 855 | used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type); | ||
| 856 | used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE); | ||
| 857 | skb_put(_skb, sizeof(*_d2) + used); | ||
| 858 | *skb = _skb; | ||
| 859 | error: | ||
| 860 | return result; | ||
| 861 | } | ||
| 862 | |||
| 863 | /** | ||
| 864 | * Allocate memory for and populate fields of F0 association frame | ||
| 865 | * | ||
| 866 | * Currently (while focusing on unsecure enrollment) we ignore the | ||
| 867 | * nonce's that could be placed in the message. Only the error field is | ||
| 868 | * populated by the value provided by the caller. | ||
| 869 | */ | ||
| 870 | static | ||
| 871 | int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb, | ||
| 872 | enum wlp_assc_error error) | ||
| 873 | { | ||
| 874 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 875 | int result = -ENOMEM; | ||
| 876 | struct { | ||
| 877 | struct wlp_frame_assoc f0_hdr; | ||
| 878 | struct wlp_attr_enonce enonce; | ||
| 879 | struct wlp_attr_rnonce rnonce; | ||
| 880 | struct wlp_attr_wlp_assc_err assc_err; | ||
| 881 | } *f0; | ||
| 882 | struct sk_buff *_skb; | ||
| 883 | struct wlp_nonce tmp; | ||
| 884 | |||
| 885 | _skb = dev_alloc_skb(sizeof(*f0)); | ||
| 886 | if (_skb == NULL) { | ||
| 887 | dev_err(dev, "WLP: Unable to allocate memory for F0 " | ||
| 888 | "association frame. \n"); | ||
| 889 | goto error_alloc; | ||
| 890 | } | ||
| 891 | f0 = (void *) _skb->data; | ||
| 892 | f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 893 | f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION; | ||
| 894 | f0->f0_hdr.type = WLP_ASSOC_F0; | ||
| 895 | wlp_set_version(&f0->f0_hdr.version, WLP_VERSION); | ||
| 896 | wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0); | ||
| 897 | memset(&tmp, 0, sizeof(tmp)); | ||
| 898 | wlp_set_enonce(&f0->enonce, &tmp); | ||
| 899 | wlp_set_rnonce(&f0->rnonce, &tmp); | ||
| 900 | wlp_set_wlp_assc_err(&f0->assc_err, error); | ||
| 901 | skb_put(_skb, sizeof(*f0)); | ||
| 902 | *skb = _skb; | ||
| 903 | result = 0; | ||
| 904 | error_alloc: | ||
| 905 | return result; | ||
| 906 | } | ||
| 907 | |||
| 908 | /** | ||
| 909 | * Parse F0 frame | ||
| 910 | * | ||
| 911 | * We just retrieve the values and print it as an error to the user. | ||
| 912 | * Calling function already knows an error occured (F0 indicates error), so | ||
| 913 | * we just parse the content as debug for higher layers. | ||
| 914 | */ | ||
| 915 | int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb) | ||
| 916 | { | ||
| 917 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 918 | struct wlp_frame_assoc *f0 = (void *) skb->data; | ||
| 919 | void *ptr = skb->data; | ||
| 920 | size_t len = skb->len; | ||
| 921 | size_t used; | ||
| 922 | ssize_t result; | ||
| 923 | struct wlp_nonce enonce, rnonce; | ||
| 924 | enum wlp_assc_error assc_err; | ||
| 925 | char enonce_buf[WLP_WSS_NONCE_STRSIZE]; | ||
| 926 | char rnonce_buf[WLP_WSS_NONCE_STRSIZE]; | ||
| 927 | |||
| 928 | used = sizeof(*f0); | ||
| 929 | result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used); | ||
| 930 | if (result < 0) { | ||
| 931 | dev_err(dev, "WLP: unable to obtain Enrollee nonce " | ||
| 932 | "attribute from F0 message.\n"); | ||
| 933 | goto error_parse; | ||
| 934 | } | ||
| 935 | used += result; | ||
| 936 | result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used); | ||
| 937 | if (result < 0) { | ||
| 938 | dev_err(dev, "WLP: unable to obtain Registrar nonce " | ||
| 939 | "attribute from F0 message.\n"); | ||
| 940 | goto error_parse; | ||
| 941 | } | ||
| 942 | used += result; | ||
| 943 | result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); | ||
| 944 | if (result < 0) { | ||
| 945 | dev_err(dev, "WLP: unable to obtain WLP Association error " | ||
| 946 | "attribute from F0 message.\n"); | ||
| 947 | goto error_parse; | ||
| 948 | } | ||
| 949 | wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce); | ||
| 950 | wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce); | ||
| 951 | dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee " | ||
| 952 | "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n", | ||
| 953 | enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err)); | ||
| 954 | result = 0; | ||
| 955 | error_parse: | ||
| 956 | return result; | ||
| 957 | } | ||
| 958 | |||
| 959 | /** | ||
| 960 | * Retrieve variable device information from association message | ||
| 961 | * | ||
| 962 | * The device information parsed is not required in any message. This | ||
| 963 | * routine will thus not fail if an attribute is not present. | ||
| 964 | * The attributes are expected in a certain order, even if all are not | ||
| 965 | * present. The "attribute type" value is used to ensure the attributes | ||
| 966 | * are parsed in the correct order. | ||
| 967 | * | ||
| 968 | * If an error is encountered during parsing the function will return an | ||
| 969 | * error code, when this happens the given device_info structure may be | ||
| 970 | * partially filled. | ||
| 971 | */ | ||
| 972 | static | ||
| 973 | int wlp_get_variable_info(struct wlp *wlp, void *data, | ||
| 974 | struct wlp_device_info *dev_info, ssize_t len) | ||
| 975 | { | ||
| 976 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 977 | size_t used = 0; | ||
| 978 | struct wlp_attr_hdr *hdr; | ||
| 979 | ssize_t result = 0; | ||
| 980 | unsigned last = 0; | ||
| 981 | |||
| 982 | while (len - used > 0) { | ||
| 983 | if (len - used < sizeof(*hdr)) { | ||
| 984 | dev_err(dev, "WLP: Partial data in frame, cannot " | ||
| 985 | "parse. \n"); | ||
| 986 | goto error_parse; | ||
| 987 | } | ||
| 988 | hdr = data + used; | ||
| 989 | switch (le16_to_cpu(hdr->type)) { | ||
| 990 | case WLP_ATTR_MANUF: | ||
| 991 | if (last >= WLP_ATTR_MANUF) { | ||
| 992 | dev_err(dev, "WLP: Incorrect order of " | ||
| 993 | "attribute values in D1 msg.\n"); | ||
| 994 | goto error_parse; | ||
| 995 | } | ||
| 996 | result = wlp_get_manufacturer(wlp, data + used, | ||
| 997 | dev_info->manufacturer, | ||
| 998 | len - used); | ||
| 999 | if (result < 0) { | ||
| 1000 | dev_err(dev, "WLP: Unable to obtain " | ||
| 1001 | "Manufacturer attribute from D1 " | ||
| 1002 | "message.\n"); | ||
| 1003 | goto error_parse; | ||
| 1004 | } | ||
| 1005 | last = WLP_ATTR_MANUF; | ||
| 1006 | used += result; | ||
| 1007 | break; | ||
| 1008 | case WLP_ATTR_MODEL_NAME: | ||
| 1009 | if (last >= WLP_ATTR_MODEL_NAME) { | ||
| 1010 | dev_err(dev, "WLP: Incorrect order of " | ||
| 1011 | "attribute values in D1 msg.\n"); | ||
| 1012 | goto error_parse; | ||
| 1013 | } | ||
| 1014 | result = wlp_get_model_name(wlp, data + used, | ||
| 1015 | dev_info->model_name, | ||
| 1016 | len - used); | ||
| 1017 | if (result < 0) { | ||
| 1018 | dev_err(dev, "WLP: Unable to obtain Model " | ||
| 1019 | "name attribute from D1 message.\n"); | ||
| 1020 | goto error_parse; | ||
| 1021 | } | ||
| 1022 | last = WLP_ATTR_MODEL_NAME; | ||
| 1023 | used += result; | ||
| 1024 | break; | ||
| 1025 | case WLP_ATTR_MODEL_NR: | ||
| 1026 | if (last >= WLP_ATTR_MODEL_NR) { | ||
| 1027 | dev_err(dev, "WLP: Incorrect order of " | ||
| 1028 | "attribute values in D1 msg.\n"); | ||
| 1029 | goto error_parse; | ||
| 1030 | } | ||
| 1031 | result = wlp_get_model_nr(wlp, data + used, | ||
| 1032 | dev_info->model_nr, | ||
| 1033 | len - used); | ||
| 1034 | if (result < 0) { | ||
| 1035 | dev_err(dev, "WLP: Unable to obtain Model " | ||
| 1036 | "number attribute from D1 message.\n"); | ||
| 1037 | goto error_parse; | ||
| 1038 | } | ||
| 1039 | last = WLP_ATTR_MODEL_NR; | ||
| 1040 | used += result; | ||
| 1041 | break; | ||
| 1042 | case WLP_ATTR_SERIAL: | ||
| 1043 | if (last >= WLP_ATTR_SERIAL) { | ||
| 1044 | dev_err(dev, "WLP: Incorrect order of " | ||
| 1045 | "attribute values in D1 msg.\n"); | ||
| 1046 | goto error_parse; | ||
| 1047 | } | ||
| 1048 | result = wlp_get_serial(wlp, data + used, | ||
| 1049 | dev_info->serial, len - used); | ||
| 1050 | if (result < 0) { | ||
| 1051 | dev_err(dev, "WLP: Unable to obtain Serial " | ||
| 1052 | "number attribute from D1 message.\n"); | ||
| 1053 | goto error_parse; | ||
| 1054 | } | ||
| 1055 | last = WLP_ATTR_SERIAL; | ||
| 1056 | used += result; | ||
| 1057 | break; | ||
| 1058 | case WLP_ATTR_PRI_DEV_TYPE: | ||
| 1059 | if (last >= WLP_ATTR_PRI_DEV_TYPE) { | ||
| 1060 | dev_err(dev, "WLP: Incorrect order of " | ||
| 1061 | "attribute values in D1 msg.\n"); | ||
| 1062 | goto error_parse; | ||
| 1063 | } | ||
| 1064 | result = wlp_get_prim_dev_type(wlp, data + used, | ||
| 1065 | &dev_info->prim_dev_type, | ||
| 1066 | len - used); | ||
| 1067 | if (result < 0) { | ||
| 1068 | dev_err(dev, "WLP: Unable to obtain Primary " | ||
| 1069 | "device type attribute from D1 " | ||
| 1070 | "message.\n"); | ||
| 1071 | goto error_parse; | ||
| 1072 | } | ||
| 1073 | dev_info->prim_dev_type.category = | ||
| 1074 | le16_to_cpu(dev_info->prim_dev_type.category); | ||
| 1075 | dev_info->prim_dev_type.subID = | ||
| 1076 | le16_to_cpu(dev_info->prim_dev_type.subID); | ||
| 1077 | last = WLP_ATTR_PRI_DEV_TYPE; | ||
| 1078 | used += result; | ||
| 1079 | break; | ||
| 1080 | default: | ||
| 1081 | /* This is not variable device information. */ | ||
| 1082 | goto out; | ||
| 1083 | break; | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | out: | ||
| 1087 | return used; | ||
| 1088 | error_parse: | ||
| 1089 | return -EINVAL; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | /** | ||
| 1093 | * Parse incoming D1 frame, populate attribute values | ||
| 1094 | * | ||
| 1095 | * Caller provides pointers to memory already allocated for attributes | ||
| 1096 | * expected in the D1 frame. These variables will be populated. | ||
| 1097 | */ | ||
| 1098 | static | ||
| 1099 | int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 1100 | struct wlp_uuid *uuid_e, | ||
| 1101 | enum wlp_wss_sel_mthd *sel_mthd, | ||
| 1102 | struct wlp_device_info *dev_info, | ||
| 1103 | enum wlp_assc_error *assc_err) | ||
| 1104 | { | ||
| 1105 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1106 | struct wlp_frame_assoc *d1 = (void *) skb->data; | ||
| 1107 | void *ptr = skb->data; | ||
| 1108 | size_t len = skb->len; | ||
| 1109 | size_t used; | ||
| 1110 | ssize_t result; | ||
| 1111 | |||
| 1112 | used = sizeof(*d1); | ||
| 1113 | result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used); | ||
| 1114 | if (result < 0) { | ||
| 1115 | dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 " | ||
| 1116 | "message.\n"); | ||
| 1117 | goto error_parse; | ||
| 1118 | } | ||
| 1119 | used += result; | ||
| 1120 | result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used); | ||
| 1121 | if (result < 0) { | ||
| 1122 | dev_err(dev, "WLP: unable to obtain WSS selection method " | ||
| 1123 | "from D1 message.\n"); | ||
| 1124 | goto error_parse; | ||
| 1125 | } | ||
| 1126 | used += result; | ||
| 1127 | result = wlp_get_dev_name(wlp, ptr + used, dev_info->name, | ||
| 1128 | len - used); | ||
| 1129 | if (result < 0) { | ||
| 1130 | dev_err(dev, "WLP: unable to obtain Device Name from D1 " | ||
| 1131 | "message.\n"); | ||
| 1132 | goto error_parse; | ||
| 1133 | } | ||
| 1134 | used += result; | ||
| 1135 | result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used); | ||
| 1136 | if (result < 0) { | ||
| 1137 | dev_err(dev, "WLP: unable to obtain Device Information from " | ||
| 1138 | "D1 message.\n"); | ||
| 1139 | goto error_parse; | ||
| 1140 | } | ||
| 1141 | used += result; | ||
| 1142 | result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used); | ||
| 1143 | if (result < 0) { | ||
| 1144 | dev_err(dev, "WLP: unable to obtain WLP Association Error " | ||
| 1145 | "Information from D1 message.\n"); | ||
| 1146 | goto error_parse; | ||
| 1147 | } | ||
| 1148 | result = 0; | ||
| 1149 | error_parse: | ||
| 1150 | return result; | ||
| 1151 | } | ||
| 1152 | /** | ||
| 1153 | * Handle incoming D1 frame | ||
| 1154 | * | ||
| 1155 | * The frame has already been verified to contain an Association header with | ||
| 1156 | * the correct version number. Parse the incoming frame, construct and send | ||
| 1157 | * a D2 frame in response. | ||
| 1158 | * | ||
| 1159 | * It is not clear what to do with most fields in the incoming D1 frame. We | ||
| 1160 | * retrieve and discard the information here for now. | ||
| 1161 | */ | ||
| 1162 | void wlp_handle_d1_frame(struct work_struct *ws) | ||
| 1163 | { | ||
| 1164 | struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, | ||
| 1165 | struct wlp_assoc_frame_ctx, | ||
| 1166 | ws); | ||
| 1167 | struct wlp *wlp = frame_ctx->wlp; | ||
| 1168 | struct wlp_wss *wss = &wlp->wss; | ||
| 1169 | struct sk_buff *skb = frame_ctx->skb; | ||
| 1170 | struct uwb_dev_addr *src = &frame_ctx->src; | ||
| 1171 | int result; | ||
| 1172 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1173 | struct wlp_uuid uuid_e; | ||
| 1174 | enum wlp_wss_sel_mthd sel_mthd = 0; | ||
| 1175 | struct wlp_device_info dev_info; | ||
| 1176 | enum wlp_assc_error assc_err; | ||
| 1177 | struct sk_buff *resp = NULL; | ||
| 1178 | |||
| 1179 | /* Parse D1 frame */ | ||
| 1180 | mutex_lock(&wss->mutex); | ||
| 1181 | mutex_lock(&wlp->mutex); /* to access wlp->uuid */ | ||
| 1182 | memset(&dev_info, 0, sizeof(dev_info)); | ||
| 1183 | result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info, | ||
| 1184 | &assc_err); | ||
| 1185 | if (result < 0) { | ||
| 1186 | dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n"); | ||
| 1187 | kfree_skb(skb); | ||
| 1188 | goto out; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | kfree_skb(skb); | ||
| 1192 | if (!wlp_uuid_is_set(&wlp->uuid)) { | ||
| 1193 | dev_err(dev, "WLP: UUID is not set. Set via sysfs to " | ||
| 1194 | "proceed. Respong to D1 message with error F0.\n"); | ||
| 1195 | result = wlp_build_assoc_f0(wlp, &resp, | ||
| 1196 | WLP_ASSOC_ERROR_NOT_READY); | ||
| 1197 | if (result < 0) { | ||
| 1198 | dev_err(dev, "WLP: Unable to construct F0 message.\n"); | ||
| 1199 | goto out; | ||
| 1200 | } | ||
| 1201 | } else { | ||
| 1202 | /* Construct D2 frame */ | ||
| 1203 | result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e); | ||
| 1204 | if (result < 0) { | ||
| 1205 | dev_err(dev, "WLP: Unable to construct D2 message.\n"); | ||
| 1206 | goto out; | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | /* Send D2 frame */ | ||
| 1210 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 1211 | result = wlp->xmit_frame(wlp, resp, src); | ||
| 1212 | if (result < 0) { | ||
| 1213 | dev_err(dev, "WLP: Unable to transmit D2 association " | ||
| 1214 | "message: %d\n", result); | ||
| 1215 | if (result == -ENXIO) | ||
| 1216 | dev_err(dev, "WLP: Is network interface up? \n"); | ||
| 1217 | /* We could try again ... */ | ||
| 1218 | dev_kfree_skb_any(resp); /* we need to free if tx fails */ | ||
| 1219 | } | ||
| 1220 | out: | ||
| 1221 | kfree(frame_ctx); | ||
| 1222 | mutex_unlock(&wlp->mutex); | ||
| 1223 | mutex_unlock(&wss->mutex); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /** | ||
| 1227 | * Parse incoming D2 frame, create and populate temporary cache | ||
| 1228 | * | ||
| 1229 | * @skb: socket buffer in which D2 frame can be found | ||
| 1230 | * @neighbor: the neighbor that sent the D2 frame | ||
| 1231 | * | ||
| 1232 | * Will allocate memory for temporary storage of information learned during | ||
| 1233 | * discovery. | ||
| 1234 | */ | ||
| 1235 | int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb, | ||
| 1236 | struct wlp_neighbor_e *neighbor) | ||
| 1237 | { | ||
| 1238 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1239 | struct wlp_frame_assoc *d2 = (void *) skb->data; | ||
| 1240 | void *ptr = skb->data; | ||
| 1241 | size_t len = skb->len; | ||
| 1242 | size_t used; | ||
| 1243 | ssize_t result; | ||
| 1244 | struct wlp_uuid uuid_e; | ||
| 1245 | struct wlp_device_info *nb_info; | ||
| 1246 | enum wlp_assc_error assc_err; | ||
| 1247 | |||
| 1248 | used = sizeof(*d2); | ||
| 1249 | result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); | ||
| 1250 | if (result < 0) { | ||
| 1251 | dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " | ||
| 1252 | "message.\n"); | ||
| 1253 | goto error_parse; | ||
| 1254 | } | ||
| 1255 | if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { | ||
| 1256 | dev_err(dev, "WLP: UUID-E in incoming D2 does not match " | ||
| 1257 | "local UUID sent in D1. \n"); | ||
| 1258 | goto error_parse; | ||
| 1259 | } | ||
| 1260 | used += result; | ||
| 1261 | result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used); | ||
| 1262 | if (result < 0) { | ||
| 1263 | dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " | ||
| 1264 | "message.\n"); | ||
| 1265 | goto error_parse; | ||
| 1266 | } | ||
| 1267 | used += result; | ||
| 1268 | result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor, | ||
| 1269 | len - used); | ||
| 1270 | if (result < 0) { | ||
| 1271 | dev_err(dev, "WLP: unable to obtain WSS information " | ||
| 1272 | "from D2 message.\n"); | ||
| 1273 | goto error_parse; | ||
| 1274 | } | ||
| 1275 | used += result; | ||
| 1276 | neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); | ||
| 1277 | if (neighbor->info == NULL) { | ||
| 1278 | dev_err(dev, "WLP: cannot allocate memory to store device " | ||
| 1279 | "info.\n"); | ||
| 1280 | result = -ENOMEM; | ||
| 1281 | goto error_parse; | ||
| 1282 | } | ||
| 1283 | nb_info = neighbor->info; | ||
| 1284 | result = wlp_get_dev_name(wlp, ptr + used, nb_info->name, | ||
| 1285 | len - used); | ||
| 1286 | if (result < 0) { | ||
| 1287 | dev_err(dev, "WLP: unable to obtain Device Name from D2 " | ||
| 1288 | "message.\n"); | ||
| 1289 | goto error_parse; | ||
| 1290 | } | ||
| 1291 | used += result; | ||
| 1292 | result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used); | ||
| 1293 | if (result < 0) { | ||
| 1294 | dev_err(dev, "WLP: unable to obtain Device Information from " | ||
| 1295 | "D2 message.\n"); | ||
| 1296 | goto error_parse; | ||
| 1297 | } | ||
| 1298 | used += result; | ||
| 1299 | result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); | ||
| 1300 | if (result < 0) { | ||
| 1301 | dev_err(dev, "WLP: unable to obtain WLP Association Error " | ||
| 1302 | "Information from D2 message.\n"); | ||
| 1303 | goto error_parse; | ||
| 1304 | } | ||
| 1305 | if (assc_err != WLP_ASSOC_ERROR_NONE) { | ||
| 1306 | dev_err(dev, "WLP: neighbor device returned association " | ||
| 1307 | "error %d\n", assc_err); | ||
| 1308 | result = -EINVAL; | ||
| 1309 | goto error_parse; | ||
| 1310 | } | ||
| 1311 | result = 0; | ||
| 1312 | error_parse: | ||
| 1313 | if (result < 0) | ||
| 1314 | wlp_remove_neighbor_tmp_info(neighbor); | ||
| 1315 | return result; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /** | ||
| 1319 | * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in | ||
| 1320 | * | ||
| 1321 | * @wss: our WSS that will be enrolled | ||
| 1322 | * @skb: socket buffer in which D2 frame can be found | ||
| 1323 | * @neighbor: the neighbor that sent the D2 frame | ||
| 1324 | * @wssid: the wssid of the WSS in which we want to enroll | ||
| 1325 | * | ||
| 1326 | * Forms part of enrollment sequence. We are trying to enroll in WSS with | ||
| 1327 | * @wssid by using @neighbor as registrar. A D1 message was sent to | ||
| 1328 | * @neighbor and now we need to parse the D2 response. The neighbor's | ||
| 1329 | * response is searched for the requested WSS and if found (and it accepts | ||
| 1330 | * enrollment), we store the information. | ||
| 1331 | */ | ||
| 1332 | int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb, | ||
| 1333 | struct wlp_neighbor_e *neighbor, | ||
| 1334 | struct wlp_uuid *wssid) | ||
| 1335 | { | ||
| 1336 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 1337 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1338 | void *ptr = skb->data; | ||
| 1339 | size_t len = skb->len; | ||
| 1340 | size_t used; | ||
| 1341 | ssize_t result; | ||
| 1342 | struct wlp_uuid uuid_e; | ||
| 1343 | struct wlp_uuid uuid_r; | ||
| 1344 | struct wlp_device_info nb_info; | ||
| 1345 | enum wlp_assc_error assc_err; | ||
| 1346 | char uuid_bufA[WLP_WSS_UUID_STRSIZE]; | ||
| 1347 | char uuid_bufB[WLP_WSS_UUID_STRSIZE]; | ||
| 1348 | |||
| 1349 | used = sizeof(struct wlp_frame_assoc); | ||
| 1350 | result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); | ||
| 1351 | if (result < 0) { | ||
| 1352 | dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " | ||
| 1353 | "message.\n"); | ||
| 1354 | goto error_parse; | ||
| 1355 | } | ||
| 1356 | if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { | ||
| 1357 | dev_err(dev, "WLP: UUID-E in incoming D2 does not match " | ||
| 1358 | "local UUID sent in D1. \n"); | ||
| 1359 | goto error_parse; | ||
| 1360 | } | ||
| 1361 | used += result; | ||
| 1362 | result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used); | ||
| 1363 | if (result < 0) { | ||
| 1364 | dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " | ||
| 1365 | "message.\n"); | ||
| 1366 | goto error_parse; | ||
| 1367 | } | ||
| 1368 | if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) { | ||
| 1369 | wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA), | ||
| 1370 | &neighbor->uuid); | ||
| 1371 | wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r); | ||
| 1372 | dev_err(dev, "WLP: UUID of neighbor does not match UUID " | ||
| 1373 | "learned during discovery. Originally discovered: %s, " | ||
| 1374 | "now from D2 message: %s\n", uuid_bufA, uuid_bufB); | ||
| 1375 | result = -EINVAL; | ||
| 1376 | goto error_parse; | ||
| 1377 | } | ||
| 1378 | used += result; | ||
| 1379 | wss->wssid = *wssid; | ||
| 1380 | result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used); | ||
| 1381 | if (result < 0) { | ||
| 1382 | dev_err(dev, "WLP: unable to obtain WSS information " | ||
| 1383 | "from D2 message.\n"); | ||
| 1384 | goto error_parse; | ||
| 1385 | } | ||
| 1386 | if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { | ||
| 1387 | dev_err(dev, "WLP: D2 message did not contain information " | ||
| 1388 | "for successful enrollment. \n"); | ||
| 1389 | result = -EINVAL; | ||
| 1390 | goto error_parse; | ||
| 1391 | } | ||
| 1392 | used += result; | ||
| 1393 | /* Place device information on stack to continue parsing of message */ | ||
| 1394 | result = wlp_get_dev_name(wlp, ptr + used, nb_info.name, | ||
| 1395 | len - used); | ||
| 1396 | if (result < 0) { | ||
| 1397 | dev_err(dev, "WLP: unable to obtain Device Name from D2 " | ||
| 1398 | "message.\n"); | ||
| 1399 | goto error_parse; | ||
| 1400 | } | ||
| 1401 | used += result; | ||
| 1402 | result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used); | ||
| 1403 | if (result < 0) { | ||
| 1404 | dev_err(dev, "WLP: unable to obtain Device Information from " | ||
| 1405 | "D2 message.\n"); | ||
| 1406 | goto error_parse; | ||
| 1407 | } | ||
| 1408 | used += result; | ||
| 1409 | result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); | ||
| 1410 | if (result < 0) { | ||
| 1411 | dev_err(dev, "WLP: unable to obtain WLP Association Error " | ||
| 1412 | "Information from D2 message.\n"); | ||
| 1413 | goto error_parse; | ||
| 1414 | } | ||
| 1415 | if (assc_err != WLP_ASSOC_ERROR_NONE) { | ||
| 1416 | dev_err(dev, "WLP: neighbor device returned association " | ||
| 1417 | "error %d\n", assc_err); | ||
| 1418 | if (wss->state == WLP_WSS_STATE_PART_ENROLLED) { | ||
| 1419 | dev_err(dev, "WLP: Enrolled in WSS (should not " | ||
| 1420 | "happen according to spec). Undoing. \n"); | ||
| 1421 | wlp_wss_reset(wss); | ||
| 1422 | } | ||
| 1423 | result = -EINVAL; | ||
| 1424 | goto error_parse; | ||
| 1425 | } | ||
| 1426 | result = 0; | ||
| 1427 | error_parse: | ||
| 1428 | return result; | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | /** | ||
| 1432 | * Parse C3/C4 frame into provided variables | ||
| 1433 | * | ||
| 1434 | * @wssid: will point to copy of wssid retrieved from C3/C4 frame | ||
| 1435 | * @tag: will point to copy of tag retrieved from C3/C4 frame | ||
| 1436 | * @virt_addr: will point to copy of virtual address retrieved from C3/C4 | ||
| 1437 | * frame. | ||
| 1438 | * | ||
| 1439 | * Calling function has to allocate memory for these values. | ||
| 1440 | * | ||
| 1441 | * skb contains a valid C3/C4 frame, return the individual fields of this | ||
| 1442 | * frame in the provided variables. | ||
| 1443 | */ | ||
| 1444 | int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 1445 | struct wlp_uuid *wssid, u8 *tag, | ||
| 1446 | struct uwb_mac_addr *virt_addr) | ||
| 1447 | { | ||
| 1448 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1449 | int result; | ||
| 1450 | void *ptr = skb->data; | ||
| 1451 | size_t len = skb->len; | ||
| 1452 | size_t used; | ||
| 1453 | struct wlp_frame_assoc *assoc = ptr; | ||
| 1454 | |||
| 1455 | used = sizeof(*assoc); | ||
| 1456 | result = wlp_get_wssid(wlp, ptr + used, wssid, len - used); | ||
| 1457 | if (result < 0) { | ||
| 1458 | dev_err(dev, "WLP: unable to obtain WSSID attribute from " | ||
| 1459 | "%s message.\n", wlp_assoc_frame_str(assoc->type)); | ||
| 1460 | goto error_parse; | ||
| 1461 | } | ||
| 1462 | used += result; | ||
| 1463 | result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used); | ||
| 1464 | if (result < 0) { | ||
| 1465 | dev_err(dev, "WLP: unable to obtain WSS tag attribute from " | ||
| 1466 | "%s message.\n", wlp_assoc_frame_str(assoc->type)); | ||
| 1467 | goto error_parse; | ||
| 1468 | } | ||
| 1469 | used += result; | ||
| 1470 | result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used); | ||
| 1471 | if (result < 0) { | ||
| 1472 | dev_err(dev, "WLP: unable to obtain WSS virtual address " | ||
| 1473 | "attribute from %s message.\n", | ||
| 1474 | wlp_assoc_frame_str(assoc->type)); | ||
| 1475 | goto error_parse; | ||
| 1476 | } | ||
| 1477 | error_parse: | ||
| 1478 | return result; | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | /** | ||
| 1482 | * Allocate memory for and populate fields of C1 or C2 association frame | ||
| 1483 | * | ||
| 1484 | * The C1 and C2 association frames appear identical - except for the type. | ||
| 1485 | */ | ||
| 1486 | static | ||
| 1487 | int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1488 | struct sk_buff **skb, enum wlp_assoc_type type) | ||
| 1489 | { | ||
| 1490 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1491 | int result = -ENOMEM; | ||
| 1492 | struct { | ||
| 1493 | struct wlp_frame_assoc c_hdr; | ||
| 1494 | struct wlp_attr_wssid wssid; | ||
| 1495 | } *c; | ||
| 1496 | struct sk_buff *_skb; | ||
| 1497 | |||
| 1498 | _skb = dev_alloc_skb(sizeof(*c)); | ||
| 1499 | if (_skb == NULL) { | ||
| 1500 | dev_err(dev, "WLP: Unable to allocate memory for C1/C2 " | ||
| 1501 | "association frame. \n"); | ||
| 1502 | goto error_alloc; | ||
| 1503 | } | ||
| 1504 | c = (void *) _skb->data; | ||
| 1505 | c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 1506 | c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; | ||
| 1507 | c->c_hdr.type = type; | ||
| 1508 | wlp_set_version(&c->c_hdr.version, WLP_VERSION); | ||
| 1509 | wlp_set_msg_type(&c->c_hdr.msg_type, type); | ||
| 1510 | wlp_set_wssid(&c->wssid, &wss->wssid); | ||
| 1511 | skb_put(_skb, sizeof(*c)); | ||
| 1512 | *skb = _skb; | ||
| 1513 | result = 0; | ||
| 1514 | error_alloc: | ||
| 1515 | return result; | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | |||
| 1519 | static | ||
| 1520 | int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1521 | struct sk_buff **skb) | ||
| 1522 | { | ||
| 1523 | return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1); | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | static | ||
| 1527 | int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1528 | struct sk_buff **skb) | ||
| 1529 | { | ||
| 1530 | return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2); | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | |||
| 1534 | /** | ||
| 1535 | * Allocate memory for and populate fields of C3 or C4 association frame | ||
| 1536 | * | ||
| 1537 | * The C3 and C4 association frames appear identical - except for the type. | ||
| 1538 | */ | ||
| 1539 | static | ||
| 1540 | int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1541 | struct sk_buff **skb, enum wlp_assoc_type type) | ||
| 1542 | { | ||
| 1543 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1544 | int result = -ENOMEM; | ||
| 1545 | struct { | ||
| 1546 | struct wlp_frame_assoc c_hdr; | ||
| 1547 | struct wlp_attr_wssid wssid; | ||
| 1548 | struct wlp_attr_wss_tag wss_tag; | ||
| 1549 | struct wlp_attr_wss_virt wss_virt; | ||
| 1550 | } *c; | ||
| 1551 | struct sk_buff *_skb; | ||
| 1552 | |||
| 1553 | _skb = dev_alloc_skb(sizeof(*c)); | ||
| 1554 | if (_skb == NULL) { | ||
| 1555 | dev_err(dev, "WLP: Unable to allocate memory for C3/C4 " | ||
| 1556 | "association frame. \n"); | ||
| 1557 | goto error_alloc; | ||
| 1558 | } | ||
| 1559 | c = (void *) _skb->data; | ||
| 1560 | c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 1561 | c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; | ||
| 1562 | c->c_hdr.type = type; | ||
| 1563 | wlp_set_version(&c->c_hdr.version, WLP_VERSION); | ||
| 1564 | wlp_set_msg_type(&c->c_hdr.msg_type, type); | ||
| 1565 | wlp_set_wssid(&c->wssid, &wss->wssid); | ||
| 1566 | wlp_set_wss_tag(&c->wss_tag, wss->tag); | ||
| 1567 | wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr); | ||
| 1568 | skb_put(_skb, sizeof(*c)); | ||
| 1569 | *skb = _skb; | ||
| 1570 | result = 0; | ||
| 1571 | error_alloc: | ||
| 1572 | return result; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | static | ||
| 1576 | int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1577 | struct sk_buff **skb) | ||
| 1578 | { | ||
| 1579 | return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3); | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | static | ||
| 1583 | int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1584 | struct sk_buff **skb) | ||
| 1585 | { | ||
| 1586 | return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4); | ||
| 1587 | } | ||
| 1588 | |||
| 1589 | |||
| 1590 | #define wlp_send_assoc(type, id) \ | ||
| 1591 | static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \ | ||
| 1592 | struct uwb_dev_addr *dev_addr) \ | ||
| 1593 | { \ | ||
| 1594 | struct device *dev = &wlp->rc->uwb_dev.dev; \ | ||
| 1595 | int result; \ | ||
| 1596 | struct sk_buff *skb = NULL; \ | ||
| 1597 | \ | ||
| 1598 | /* Build the frame */ \ | ||
| 1599 | result = wlp_build_assoc_##type(wlp, wss, &skb); \ | ||
| 1600 | if (result < 0) { \ | ||
| 1601 | dev_err(dev, "WLP: Unable to construct %s association " \ | ||
| 1602 | "frame: %d\n", wlp_assoc_frame_str(id), result);\ | ||
| 1603 | goto error_build_assoc; \ | ||
| 1604 | } \ | ||
| 1605 | /* Send the frame */ \ | ||
| 1606 | BUG_ON(wlp->xmit_frame == NULL); \ | ||
| 1607 | result = wlp->xmit_frame(wlp, skb, dev_addr); \ | ||
| 1608 | if (result < 0) { \ | ||
| 1609 | dev_err(dev, "WLP: Unable to transmit %s association " \ | ||
| 1610 | "message: %d\n", wlp_assoc_frame_str(id), \ | ||
| 1611 | result); \ | ||
| 1612 | if (result == -ENXIO) \ | ||
| 1613 | dev_err(dev, "WLP: Is network interface " \ | ||
| 1614 | "up? \n"); \ | ||
| 1615 | goto error_xmit; \ | ||
| 1616 | } \ | ||
| 1617 | return 0; \ | ||
| 1618 | error_xmit: \ | ||
| 1619 | /* We could try again ... */ \ | ||
| 1620 | dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \ | ||
| 1621 | error_build_assoc: \ | ||
| 1622 | return result; \ | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | wlp_send_assoc(d1, WLP_ASSOC_D1) | ||
| 1626 | wlp_send_assoc(c1, WLP_ASSOC_C1) | ||
| 1627 | wlp_send_assoc(c3, WLP_ASSOC_C3) | ||
| 1628 | |||
| 1629 | int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss, | ||
| 1630 | struct uwb_dev_addr *dev_addr, | ||
| 1631 | enum wlp_assoc_type type) | ||
| 1632 | { | ||
| 1633 | int result = 0; | ||
| 1634 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1635 | switch (type) { | ||
| 1636 | case WLP_ASSOC_D1: | ||
| 1637 | result = wlp_send_assoc_d1(wlp, wss, dev_addr); | ||
| 1638 | break; | ||
| 1639 | case WLP_ASSOC_C1: | ||
| 1640 | result = wlp_send_assoc_c1(wlp, wss, dev_addr); | ||
| 1641 | break; | ||
| 1642 | case WLP_ASSOC_C3: | ||
| 1643 | result = wlp_send_assoc_c3(wlp, wss, dev_addr); | ||
| 1644 | break; | ||
| 1645 | default: | ||
| 1646 | dev_err(dev, "WLP: Received request to send unknown " | ||
| 1647 | "association message.\n"); | ||
| 1648 | result = -EINVAL; | ||
| 1649 | break; | ||
| 1650 | } | ||
| 1651 | return result; | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | /** | ||
| 1655 | * Handle incoming C1 frame | ||
| 1656 | * | ||
| 1657 | * The frame has already been verified to contain an Association header with | ||
| 1658 | * the correct version number. Parse the incoming frame, construct and send | ||
| 1659 | * a C2 frame in response. | ||
| 1660 | */ | ||
| 1661 | void wlp_handle_c1_frame(struct work_struct *ws) | ||
| 1662 | { | ||
| 1663 | struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, | ||
| 1664 | struct wlp_assoc_frame_ctx, | ||
| 1665 | ws); | ||
| 1666 | struct wlp *wlp = frame_ctx->wlp; | ||
| 1667 | struct wlp_wss *wss = &wlp->wss; | ||
| 1668 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1669 | struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data; | ||
| 1670 | unsigned int len = frame_ctx->skb->len; | ||
| 1671 | struct uwb_dev_addr *src = &frame_ctx->src; | ||
| 1672 | int result; | ||
| 1673 | struct wlp_uuid wssid; | ||
| 1674 | struct sk_buff *resp = NULL; | ||
| 1675 | |||
| 1676 | /* Parse C1 frame */ | ||
| 1677 | mutex_lock(&wss->mutex); | ||
| 1678 | result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid, | ||
| 1679 | len - sizeof(*c1)); | ||
| 1680 | if (result < 0) { | ||
| 1681 | dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n"); | ||
| 1682 | goto out; | ||
| 1683 | } | ||
| 1684 | if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) | ||
| 1685 | && wss->state == WLP_WSS_STATE_ACTIVE) { | ||
| 1686 | /* Construct C2 frame */ | ||
| 1687 | result = wlp_build_assoc_c2(wlp, wss, &resp); | ||
| 1688 | if (result < 0) { | ||
| 1689 | dev_err(dev, "WLP: Unable to construct C2 message.\n"); | ||
| 1690 | goto out; | ||
| 1691 | } | ||
| 1692 | } else { | ||
| 1693 | /* Construct F0 frame */ | ||
| 1694 | result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); | ||
| 1695 | if (result < 0) { | ||
| 1696 | dev_err(dev, "WLP: Unable to construct F0 message.\n"); | ||
| 1697 | goto out; | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | /* Send C2 frame */ | ||
| 1701 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 1702 | result = wlp->xmit_frame(wlp, resp, src); | ||
| 1703 | if (result < 0) { | ||
| 1704 | dev_err(dev, "WLP: Unable to transmit response association " | ||
| 1705 | "message: %d\n", result); | ||
| 1706 | if (result == -ENXIO) | ||
| 1707 | dev_err(dev, "WLP: Is network interface up? \n"); | ||
| 1708 | /* We could try again ... */ | ||
| 1709 | dev_kfree_skb_any(resp); /* we need to free if tx fails */ | ||
| 1710 | } | ||
| 1711 | out: | ||
| 1712 | kfree_skb(frame_ctx->skb); | ||
| 1713 | kfree(frame_ctx); | ||
| 1714 | mutex_unlock(&wss->mutex); | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | /** | ||
| 1718 | * Handle incoming C3 frame | ||
| 1719 | * | ||
| 1720 | * The frame has already been verified to contain an Association header with | ||
| 1721 | * the correct version number. Parse the incoming frame, construct and send | ||
| 1722 | * a C4 frame in response. If the C3 frame identifies a WSS that is locally | ||
| 1723 | * active then we connect to this neighbor (add it to our EDA cache). | ||
| 1724 | */ | ||
| 1725 | void wlp_handle_c3_frame(struct work_struct *ws) | ||
| 1726 | { | ||
| 1727 | struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, | ||
| 1728 | struct wlp_assoc_frame_ctx, | ||
| 1729 | ws); | ||
| 1730 | struct wlp *wlp = frame_ctx->wlp; | ||
| 1731 | struct wlp_wss *wss = &wlp->wss; | ||
| 1732 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 1733 | struct sk_buff *skb = frame_ctx->skb; | ||
| 1734 | struct uwb_dev_addr *src = &frame_ctx->src; | ||
| 1735 | int result; | ||
| 1736 | struct sk_buff *resp = NULL; | ||
| 1737 | struct wlp_uuid wssid; | ||
| 1738 | u8 tag; | ||
| 1739 | struct uwb_mac_addr virt_addr; | ||
| 1740 | |||
| 1741 | /* Parse C3 frame */ | ||
| 1742 | mutex_lock(&wss->mutex); | ||
| 1743 | result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); | ||
| 1744 | if (result < 0) { | ||
| 1745 | dev_err(dev, "WLP: unable to obtain values from C3 frame.\n"); | ||
| 1746 | goto out; | ||
| 1747 | } | ||
| 1748 | if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) | ||
| 1749 | && wss->state >= WLP_WSS_STATE_ACTIVE) { | ||
| 1750 | result = wlp_eda_update_node(&wlp->eda, src, wss, | ||
| 1751 | (void *) virt_addr.data, tag, | ||
| 1752 | WLP_WSS_CONNECTED); | ||
| 1753 | if (result < 0) { | ||
| 1754 | dev_err(dev, "WLP: Unable to update EDA cache " | ||
| 1755 | "with new connected neighbor information.\n"); | ||
| 1756 | result = wlp_build_assoc_f0(wlp, &resp, | ||
| 1757 | WLP_ASSOC_ERROR_INT); | ||
| 1758 | if (result < 0) { | ||
| 1759 | dev_err(dev, "WLP: Unable to construct F0 " | ||
| 1760 | "message.\n"); | ||
| 1761 | goto out; | ||
| 1762 | } | ||
| 1763 | } else { | ||
| 1764 | wss->state = WLP_WSS_STATE_CONNECTED; | ||
| 1765 | /* Construct C4 frame */ | ||
| 1766 | result = wlp_build_assoc_c4(wlp, wss, &resp); | ||
| 1767 | if (result < 0) { | ||
| 1768 | dev_err(dev, "WLP: Unable to construct C4 " | ||
| 1769 | "message.\n"); | ||
| 1770 | goto out; | ||
| 1771 | } | ||
| 1772 | } | ||
| 1773 | } else { | ||
| 1774 | /* Construct F0 frame */ | ||
| 1775 | result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); | ||
| 1776 | if (result < 0) { | ||
| 1777 | dev_err(dev, "WLP: Unable to construct F0 message.\n"); | ||
| 1778 | goto out; | ||
| 1779 | } | ||
| 1780 | } | ||
| 1781 | /* Send C4 frame */ | ||
| 1782 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 1783 | result = wlp->xmit_frame(wlp, resp, src); | ||
| 1784 | if (result < 0) { | ||
| 1785 | dev_err(dev, "WLP: Unable to transmit response association " | ||
| 1786 | "message: %d\n", result); | ||
| 1787 | if (result == -ENXIO) | ||
| 1788 | dev_err(dev, "WLP: Is network interface up? \n"); | ||
| 1789 | /* We could try again ... */ | ||
| 1790 | dev_kfree_skb_any(resp); /* we need to free if tx fails */ | ||
| 1791 | } | ||
| 1792 | out: | ||
| 1793 | kfree_skb(frame_ctx->skb); | ||
| 1794 | kfree(frame_ctx); | ||
| 1795 | mutex_unlock(&wss->mutex); | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | |||
diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c deleted file mode 100644 index 6627c94cc854..000000000000 --- a/drivers/uwb/wlp/sysfs.c +++ /dev/null | |||
| @@ -1,708 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * sysfs functions | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Intel Corporation | ||
| 6 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: Docs | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | #include <linux/wlp.h> | ||
| 27 | |||
| 28 | #include "wlp-internal.h" | ||
| 29 | |||
| 30 | static | ||
| 31 | size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize, | ||
| 32 | struct wlp_wssid_e *wssid_e) | ||
| 33 | { | ||
| 34 | size_t used = 0; | ||
| 35 | used += scnprintf(buf, bufsize, " WSS: "); | ||
| 36 | used += wlp_wss_uuid_print(buf + used, bufsize - used, | ||
| 37 | &wssid_e->wssid); | ||
| 38 | |||
| 39 | if (wssid_e->info != NULL) { | ||
| 40 | used += scnprintf(buf + used, bufsize - used, " "); | ||
| 41 | used += uwb_mac_addr_print(buf + used, bufsize - used, | ||
| 42 | &wssid_e->info->bcast); | ||
| 43 | used += scnprintf(buf + used, bufsize - used, " %u %u %s\n", | ||
| 44 | wssid_e->info->accept_enroll, | ||
| 45 | wssid_e->info->sec_status, | ||
| 46 | wssid_e->info->name); | ||
| 47 | } | ||
| 48 | return used; | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Print out information learned from neighbor discovery | ||
| 53 | * | ||
| 54 | * Some fields being printed may not be included in the device discovery | ||
| 55 | * information (it is not mandatory). We are thus careful how the | ||
| 56 | * information is printed to ensure it is clear to the user what field is | ||
| 57 | * being referenced. | ||
| 58 | * The information being printed is for one time use - temporary storage is | ||
| 59 | * cleaned after it is printed. | ||
| 60 | * | ||
| 61 | * Ideally sysfs output should be on one line. The information printed here | ||
| 62 | * contain a few strings so it will be hard to parse if they are all | ||
| 63 | * printed on the same line - without agreeing on a standard field | ||
| 64 | * separator. | ||
| 65 | */ | ||
| 66 | static | ||
| 67 | ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf, | ||
| 68 | size_t bufsize) | ||
| 69 | { | ||
| 70 | size_t used = 0; | ||
| 71 | struct wlp_neighbor_e *neighb; | ||
| 72 | struct wlp_wssid_e *wssid_e; | ||
| 73 | |||
| 74 | mutex_lock(&wlp->nbmutex); | ||
| 75 | used = scnprintf(buf, bufsize, "#Neighbor information\n" | ||
| 76 | "#uuid dev_addr\n" | ||
| 77 | "# Device Name:\n# Model Name:\n# Manufacturer:\n" | ||
| 78 | "# Model Nr:\n# Serial:\n" | ||
| 79 | "# Pri Dev type: CategoryID OUI OUISubdiv " | ||
| 80 | "SubcategoryID\n" | ||
| 81 | "# WSS: WSSID WSS_name accept_enroll sec_status " | ||
| 82 | "bcast\n" | ||
| 83 | "# WSS: WSSID WSS_name accept_enroll sec_status " | ||
| 84 | "bcast\n\n"); | ||
| 85 | list_for_each_entry(neighb, &wlp->neighbors, node) { | ||
| 86 | if (bufsize - used <= 0) | ||
| 87 | goto out; | ||
| 88 | used += wlp_wss_uuid_print(buf + used, bufsize - used, | ||
| 89 | &neighb->uuid); | ||
| 90 | buf[used++] = ' '; | ||
| 91 | used += uwb_dev_addr_print(buf + used, bufsize - used, | ||
| 92 | &neighb->uwb_dev->dev_addr); | ||
| 93 | if (neighb->info != NULL) | ||
| 94 | used += scnprintf(buf + used, bufsize - used, | ||
| 95 | "\n Device Name: %s\n" | ||
| 96 | " Model Name: %s\n" | ||
| 97 | " Manufacturer:%s \n" | ||
| 98 | " Model Nr: %s\n" | ||
| 99 | " Serial: %s\n" | ||
| 100 | " Pri Dev type: " | ||
| 101 | "%u %02x:%02x:%02x %u %u\n", | ||
| 102 | neighb->info->name, | ||
| 103 | neighb->info->model_name, | ||
| 104 | neighb->info->manufacturer, | ||
| 105 | neighb->info->model_nr, | ||
| 106 | neighb->info->serial, | ||
| 107 | neighb->info->prim_dev_type.category, | ||
| 108 | neighb->info->prim_dev_type.OUI[0], | ||
| 109 | neighb->info->prim_dev_type.OUI[1], | ||
| 110 | neighb->info->prim_dev_type.OUI[2], | ||
| 111 | neighb->info->prim_dev_type.OUIsubdiv, | ||
| 112 | neighb->info->prim_dev_type.subID); | ||
| 113 | list_for_each_entry(wssid_e, &neighb->wssid, node) { | ||
| 114 | used += wlp_wss_wssid_e_print(buf + used, | ||
| 115 | bufsize - used, | ||
| 116 | wssid_e); | ||
| 117 | } | ||
| 118 | buf[used++] = '\n'; | ||
| 119 | wlp_remove_neighbor_tmp_info(neighb); | ||
| 120 | } | ||
| 121 | |||
| 122 | |||
| 123 | out: | ||
| 124 | mutex_unlock(&wlp->nbmutex); | ||
| 125 | return used; | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | /** | ||
| 130 | * Show properties of all WSS in neighborhood. | ||
| 131 | * | ||
| 132 | * Will trigger a complete discovery of WSS activated by this device and | ||
| 133 | * its neighbors. | ||
| 134 | */ | ||
| 135 | ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf) | ||
| 136 | { | ||
| 137 | wlp_discover(wlp); | ||
| 138 | return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE); | ||
| 139 | } | ||
| 140 | EXPORT_SYMBOL_GPL(wlp_neighborhood_show); | ||
| 141 | |||
| 142 | static | ||
| 143 | ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf, | ||
| 144 | size_t bufsize) | ||
| 145 | { | ||
| 146 | ssize_t result; | ||
| 147 | |||
| 148 | result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid); | ||
| 149 | result += scnprintf(buf + result, bufsize - result, " "); | ||
| 150 | result += uwb_mac_addr_print(buf + result, bufsize - result, | ||
| 151 | &wss->bcast); | ||
| 152 | result += scnprintf(buf + result, bufsize - result, | ||
| 153 | " 0x%02x %u ", wss->hash, wss->secure_status); | ||
| 154 | result += wlp_wss_key_print(buf + result, bufsize - result, | ||
| 155 | wss->master_key); | ||
| 156 | result += scnprintf(buf + result, bufsize - result, " 0x%02x ", | ||
| 157 | wss->tag); | ||
| 158 | result += uwb_mac_addr_print(buf + result, bufsize - result, | ||
| 159 | &wss->virtual_addr); | ||
| 160 | result += scnprintf(buf + result, bufsize - result, " %s", wss->name); | ||
| 161 | result += scnprintf(buf + result, bufsize - result, | ||
| 162 | "\n\n#WSSID\n#WSS broadcast address\n" | ||
| 163 | "#WSS hash\n#WSS secure status\n" | ||
| 164 | "#WSS master key\n#WSS local tag\n" | ||
| 165 | "#WSS local virtual EUI-48\n#WSS name\n"); | ||
| 166 | return result; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Show which WSS is activated. | ||
| 171 | */ | ||
| 172 | ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf) | ||
| 173 | { | ||
| 174 | int result = 0; | ||
| 175 | |||
| 176 | if (mutex_lock_interruptible(&wss->mutex)) | ||
| 177 | goto out; | ||
| 178 | if (wss->state >= WLP_WSS_STATE_ACTIVE) | ||
| 179 | result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); | ||
| 180 | else | ||
| 181 | result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n"); | ||
| 182 | result += scnprintf(buf + result, PAGE_SIZE - result, | ||
| 183 | "\n\n" | ||
| 184 | "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT " | ||
| 185 | "NAME #create new WSS\n" | ||
| 186 | "# echo WSSID [DEV ADDR] #enroll in and activate " | ||
| 187 | "existing WSS, can request registrar\n" | ||
| 188 | "#\n" | ||
| 189 | "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n" | ||
| 190 | "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n" | ||
| 191 | "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n" | ||
| 192 | "# NAME is the text string identifying the WSS\n" | ||
| 193 | "# DEV ADDR is the device address of neighbor " | ||
| 194 | "that should be registrar. Eg. 32:AB\n"); | ||
| 195 | |||
| 196 | mutex_unlock(&wss->mutex); | ||
| 197 | out: | ||
| 198 | return result; | ||
| 199 | |||
| 200 | } | ||
| 201 | EXPORT_SYMBOL_GPL(wlp_wss_activate_show); | ||
| 202 | |||
| 203 | /** | ||
| 204 | * Create/activate a new WSS or enroll/activate in neighboring WSS | ||
| 205 | * | ||
| 206 | * The user can provide the WSSID of a WSS in which it wants to enroll. | ||
| 207 | * Only the WSSID is necessary if the WSS have been discovered before. If | ||
| 208 | * the WSS has not been discovered before, or the user wants to use a | ||
| 209 | * particular neighbor as its registrar, then the user can also provide a | ||
| 210 | * device address or the neighbor that will be used as registrar. | ||
| 211 | * | ||
| 212 | * A new WSS is created when the user provides a WSSID, secure status, and | ||
| 213 | * WSS name. | ||
| 214 | */ | ||
| 215 | ssize_t wlp_wss_activate_store(struct wlp_wss *wss, | ||
| 216 | const char *buf, size_t size) | ||
| 217 | { | ||
| 218 | ssize_t result = -EINVAL; | ||
| 219 | struct wlp_uuid wssid; | ||
| 220 | struct uwb_dev_addr dev; | ||
| 221 | struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; | ||
| 222 | char name[65]; | ||
| 223 | unsigned sec_status, accept; | ||
| 224 | memset(name, 0, sizeof(name)); | ||
| 225 | result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | ||
| 226 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 227 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 228 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 229 | "%02hhx:%02hhx", | ||
| 230 | &wssid.data[0] , &wssid.data[1], | ||
| 231 | &wssid.data[2] , &wssid.data[3], | ||
| 232 | &wssid.data[4] , &wssid.data[5], | ||
| 233 | &wssid.data[6] , &wssid.data[7], | ||
| 234 | &wssid.data[8] , &wssid.data[9], | ||
| 235 | &wssid.data[10], &wssid.data[11], | ||
| 236 | &wssid.data[12], &wssid.data[13], | ||
| 237 | &wssid.data[14], &wssid.data[15], | ||
| 238 | &dev.data[1], &dev.data[0]); | ||
| 239 | if (result == 16 || result == 17) { | ||
| 240 | result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | ||
| 241 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 242 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 243 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 244 | "%u %u %64c", | ||
| 245 | &wssid.data[0] , &wssid.data[1], | ||
| 246 | &wssid.data[2] , &wssid.data[3], | ||
| 247 | &wssid.data[4] , &wssid.data[5], | ||
| 248 | &wssid.data[6] , &wssid.data[7], | ||
| 249 | &wssid.data[8] , &wssid.data[9], | ||
| 250 | &wssid.data[10], &wssid.data[11], | ||
| 251 | &wssid.data[12], &wssid.data[13], | ||
| 252 | &wssid.data[14], &wssid.data[15], | ||
| 253 | &sec_status, &accept, name); | ||
| 254 | if (result == 16) | ||
| 255 | result = wlp_wss_enroll_activate(wss, &wssid, &bcast); | ||
| 256 | else if (result == 19) { | ||
| 257 | sec_status = sec_status == 0 ? 0 : 1; | ||
| 258 | accept = accept == 0 ? 0 : 1; | ||
| 259 | /* We read name using %c, so the newline needs to be | ||
| 260 | * removed */ | ||
| 261 | if (strlen(name) != sizeof(name) - 1) | ||
| 262 | name[strlen(name) - 1] = '\0'; | ||
| 263 | result = wlp_wss_create_activate(wss, &wssid, name, | ||
| 264 | sec_status, accept); | ||
| 265 | } else | ||
| 266 | result = -EINVAL; | ||
| 267 | } else if (result == 18) | ||
| 268 | result = wlp_wss_enroll_activate(wss, &wssid, &dev); | ||
| 269 | else | ||
| 270 | result = -EINVAL; | ||
| 271 | return result < 0 ? result : size; | ||
| 272 | } | ||
| 273 | EXPORT_SYMBOL_GPL(wlp_wss_activate_store); | ||
| 274 | |||
| 275 | /** | ||
| 276 | * Show the UUID of this host | ||
| 277 | */ | ||
| 278 | ssize_t wlp_uuid_show(struct wlp *wlp, char *buf) | ||
| 279 | { | ||
| 280 | ssize_t result = 0; | ||
| 281 | |||
| 282 | mutex_lock(&wlp->mutex); | ||
| 283 | result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid); | ||
| 284 | buf[result++] = '\n'; | ||
| 285 | mutex_unlock(&wlp->mutex); | ||
| 286 | return result; | ||
| 287 | } | ||
| 288 | EXPORT_SYMBOL_GPL(wlp_uuid_show); | ||
| 289 | |||
| 290 | /** | ||
| 291 | * Store a new UUID for this host | ||
| 292 | * | ||
| 293 | * According to the spec this should be encoded as an octet string in the | ||
| 294 | * order the octets are shown in string representation in RFC 4122 (WLP | ||
| 295 | * 0.99 [Table 6]) | ||
| 296 | * | ||
| 297 | * We do not check value provided by user. | ||
| 298 | */ | ||
| 299 | ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size) | ||
| 300 | { | ||
| 301 | ssize_t result; | ||
| 302 | struct wlp_uuid uuid; | ||
| 303 | |||
| 304 | mutex_lock(&wlp->mutex); | ||
| 305 | result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " | ||
| 306 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 307 | "%02hhx %02hhx %02hhx %02hhx " | ||
| 308 | "%02hhx %02hhx %02hhx %02hhx ", | ||
| 309 | &uuid.data[0] , &uuid.data[1], | ||
| 310 | &uuid.data[2] , &uuid.data[3], | ||
| 311 | &uuid.data[4] , &uuid.data[5], | ||
| 312 | &uuid.data[6] , &uuid.data[7], | ||
| 313 | &uuid.data[8] , &uuid.data[9], | ||
| 314 | &uuid.data[10], &uuid.data[11], | ||
| 315 | &uuid.data[12], &uuid.data[13], | ||
| 316 | &uuid.data[14], &uuid.data[15]); | ||
| 317 | if (result != 16) { | ||
| 318 | result = -EINVAL; | ||
| 319 | goto error; | ||
| 320 | } | ||
| 321 | wlp->uuid = uuid; | ||
| 322 | error: | ||
| 323 | mutex_unlock(&wlp->mutex); | ||
| 324 | return result < 0 ? result : size; | ||
| 325 | } | ||
| 326 | EXPORT_SYMBOL_GPL(wlp_uuid_store); | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Show contents of members of device information structure | ||
| 330 | */ | ||
| 331 | #define wlp_dev_info_show(type) \ | ||
| 332 | ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \ | ||
| 333 | { \ | ||
| 334 | ssize_t result = 0; \ | ||
| 335 | mutex_lock(&wlp->mutex); \ | ||
| 336 | if (wlp->dev_info == NULL) { \ | ||
| 337 | result = __wlp_setup_device_info(wlp); \ | ||
| 338 | if (result < 0) \ | ||
| 339 | goto out; \ | ||
| 340 | } \ | ||
| 341 | result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\ | ||
| 342 | out: \ | ||
| 343 | mutex_unlock(&wlp->mutex); \ | ||
| 344 | return result; \ | ||
| 345 | } \ | ||
| 346 | EXPORT_SYMBOL_GPL(wlp_dev_##type##_show); | ||
| 347 | |||
| 348 | wlp_dev_info_show(name) | ||
| 349 | wlp_dev_info_show(model_name) | ||
| 350 | wlp_dev_info_show(model_nr) | ||
| 351 | wlp_dev_info_show(manufacturer) | ||
| 352 | wlp_dev_info_show(serial) | ||
| 353 | |||
| 354 | /** | ||
| 355 | * Store contents of members of device information structure | ||
| 356 | */ | ||
| 357 | #define wlp_dev_info_store(type, len) \ | ||
| 358 | ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\ | ||
| 359 | { \ | ||
| 360 | ssize_t result; \ | ||
| 361 | char format[10]; \ | ||
| 362 | mutex_lock(&wlp->mutex); \ | ||
| 363 | if (wlp->dev_info == NULL) { \ | ||
| 364 | result = __wlp_alloc_device_info(wlp); \ | ||
| 365 | if (result < 0) \ | ||
| 366 | goto out; \ | ||
| 367 | } \ | ||
| 368 | memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \ | ||
| 369 | sprintf(format, "%%%uc", len); \ | ||
| 370 | result = sscanf(buf, format, wlp->dev_info->type); \ | ||
| 371 | out: \ | ||
| 372 | mutex_unlock(&wlp->mutex); \ | ||
| 373 | return result < 0 ? result : size; \ | ||
| 374 | } \ | ||
| 375 | EXPORT_SYMBOL_GPL(wlp_dev_##type##_store); | ||
| 376 | |||
| 377 | wlp_dev_info_store(name, 32) | ||
| 378 | wlp_dev_info_store(manufacturer, 64) | ||
| 379 | wlp_dev_info_store(model_name, 32) | ||
| 380 | wlp_dev_info_store(model_nr, 32) | ||
| 381 | wlp_dev_info_store(serial, 32) | ||
| 382 | |||
| 383 | static | ||
| 384 | const char *__wlp_dev_category[] = { | ||
| 385 | [WLP_DEV_CAT_COMPUTER] = "Computer", | ||
| 386 | [WLP_DEV_CAT_INPUT] = "Input device", | ||
| 387 | [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or " | ||
| 388 | "Copier", | ||
| 389 | [WLP_DEV_CAT_CAMERA] = "Camera", | ||
| 390 | [WLP_DEV_CAT_STORAGE] = "Storage Network", | ||
| 391 | [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure", | ||
| 392 | [WLP_DEV_CAT_DISPLAY] = "Display", | ||
| 393 | [WLP_DEV_CAT_MULTIM] = "Multimedia device", | ||
| 394 | [WLP_DEV_CAT_GAMING] = "Gaming device", | ||
| 395 | [WLP_DEV_CAT_TELEPHONE] = "Telephone", | ||
| 396 | [WLP_DEV_CAT_OTHER] = "Other", | ||
| 397 | }; | ||
| 398 | |||
| 399 | static | ||
| 400 | const char *wlp_dev_category_str(unsigned cat) | ||
| 401 | { | ||
| 402 | if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) | ||
| 403 | || cat == WLP_DEV_CAT_OTHER) | ||
| 404 | return __wlp_dev_category[cat]; | ||
| 405 | return "unknown category"; | ||
| 406 | } | ||
| 407 | |||
| 408 | ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf) | ||
| 409 | { | ||
| 410 | ssize_t result = 0; | ||
| 411 | mutex_lock(&wlp->mutex); | ||
| 412 | if (wlp->dev_info == NULL) { | ||
| 413 | result = __wlp_setup_device_info(wlp); | ||
| 414 | if (result < 0) | ||
| 415 | goto out; | ||
| 416 | } | ||
| 417 | result = scnprintf(buf, PAGE_SIZE, "%s\n", | ||
| 418 | wlp_dev_category_str(wlp->dev_info->prim_dev_type.category)); | ||
| 419 | out: | ||
| 420 | mutex_unlock(&wlp->mutex); | ||
| 421 | return result; | ||
| 422 | } | ||
| 423 | EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show); | ||
| 424 | |||
| 425 | ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf, | ||
| 426 | size_t size) | ||
| 427 | { | ||
| 428 | ssize_t result; | ||
| 429 | u16 cat; | ||
| 430 | mutex_lock(&wlp->mutex); | ||
| 431 | if (wlp->dev_info == NULL) { | ||
| 432 | result = __wlp_alloc_device_info(wlp); | ||
| 433 | if (result < 0) | ||
| 434 | goto out; | ||
| 435 | } | ||
| 436 | result = sscanf(buf, "%hu", &cat); | ||
| 437 | if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) | ||
| 438 | || cat == WLP_DEV_CAT_OTHER) | ||
| 439 | wlp->dev_info->prim_dev_type.category = cat; | ||
| 440 | else | ||
| 441 | result = -EINVAL; | ||
| 442 | out: | ||
| 443 | mutex_unlock(&wlp->mutex); | ||
| 444 | return result < 0 ? result : size; | ||
| 445 | } | ||
| 446 | EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store); | ||
| 447 | |||
| 448 | ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf) | ||
| 449 | { | ||
| 450 | ssize_t result = 0; | ||
| 451 | mutex_lock(&wlp->mutex); | ||
| 452 | if (wlp->dev_info == NULL) { | ||
| 453 | result = __wlp_setup_device_info(wlp); | ||
| 454 | if (result < 0) | ||
| 455 | goto out; | ||
| 456 | } | ||
| 457 | result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n", | ||
| 458 | wlp->dev_info->prim_dev_type.OUI[0], | ||
| 459 | wlp->dev_info->prim_dev_type.OUI[1], | ||
| 460 | wlp->dev_info->prim_dev_type.OUI[2]); | ||
| 461 | out: | ||
| 462 | mutex_unlock(&wlp->mutex); | ||
| 463 | return result; | ||
| 464 | } | ||
| 465 | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show); | ||
| 466 | |||
| 467 | ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size) | ||
| 468 | { | ||
| 469 | ssize_t result; | ||
| 470 | u8 OUI[3]; | ||
| 471 | mutex_lock(&wlp->mutex); | ||
| 472 | if (wlp->dev_info == NULL) { | ||
| 473 | result = __wlp_alloc_device_info(wlp); | ||
| 474 | if (result < 0) | ||
| 475 | goto out; | ||
| 476 | } | ||
| 477 | result = sscanf(buf, "%hhx:%hhx:%hhx", | ||
| 478 | &OUI[0], &OUI[1], &OUI[2]); | ||
| 479 | if (result != 3) { | ||
| 480 | result = -EINVAL; | ||
| 481 | goto out; | ||
| 482 | } else | ||
| 483 | memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI)); | ||
| 484 | out: | ||
| 485 | mutex_unlock(&wlp->mutex); | ||
| 486 | return result < 0 ? result : size; | ||
| 487 | } | ||
| 488 | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store); | ||
| 489 | |||
| 490 | |||
| 491 | ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf) | ||
| 492 | { | ||
| 493 | ssize_t result = 0; | ||
| 494 | mutex_lock(&wlp->mutex); | ||
| 495 | if (wlp->dev_info == NULL) { | ||
| 496 | result = __wlp_setup_device_info(wlp); | ||
| 497 | if (result < 0) | ||
| 498 | goto out; | ||
| 499 | } | ||
| 500 | result = scnprintf(buf, PAGE_SIZE, "%u\n", | ||
| 501 | wlp->dev_info->prim_dev_type.OUIsubdiv); | ||
| 502 | out: | ||
| 503 | mutex_unlock(&wlp->mutex); | ||
| 504 | return result; | ||
| 505 | } | ||
| 506 | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show); | ||
| 507 | |||
| 508 | ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf, | ||
| 509 | size_t size) | ||
| 510 | { | ||
| 511 | ssize_t result; | ||
| 512 | unsigned sub; | ||
| 513 | u8 max_sub = ~0; | ||
| 514 | mutex_lock(&wlp->mutex); | ||
| 515 | if (wlp->dev_info == NULL) { | ||
| 516 | result = __wlp_alloc_device_info(wlp); | ||
| 517 | if (result < 0) | ||
| 518 | goto out; | ||
| 519 | } | ||
| 520 | result = sscanf(buf, "%u", &sub); | ||
| 521 | if (sub <= max_sub) | ||
| 522 | wlp->dev_info->prim_dev_type.OUIsubdiv = sub; | ||
| 523 | else | ||
| 524 | result = -EINVAL; | ||
| 525 | out: | ||
| 526 | mutex_unlock(&wlp->mutex); | ||
| 527 | return result < 0 ? result : size; | ||
| 528 | } | ||
| 529 | EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store); | ||
| 530 | |||
| 531 | ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf) | ||
| 532 | { | ||
| 533 | ssize_t result = 0; | ||
| 534 | mutex_lock(&wlp->mutex); | ||
| 535 | if (wlp->dev_info == NULL) { | ||
| 536 | result = __wlp_setup_device_info(wlp); | ||
| 537 | if (result < 0) | ||
| 538 | goto out; | ||
| 539 | } | ||
| 540 | result = scnprintf(buf, PAGE_SIZE, "%u\n", | ||
| 541 | wlp->dev_info->prim_dev_type.subID); | ||
| 542 | out: | ||
| 543 | mutex_unlock(&wlp->mutex); | ||
| 544 | return result; | ||
| 545 | } | ||
| 546 | EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show); | ||
| 547 | |||
| 548 | ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf, | ||
| 549 | size_t size) | ||
| 550 | { | ||
| 551 | ssize_t result; | ||
| 552 | unsigned sub; | ||
| 553 | __le16 max_sub = ~0; | ||
| 554 | mutex_lock(&wlp->mutex); | ||
| 555 | if (wlp->dev_info == NULL) { | ||
| 556 | result = __wlp_alloc_device_info(wlp); | ||
| 557 | if (result < 0) | ||
| 558 | goto out; | ||
| 559 | } | ||
| 560 | result = sscanf(buf, "%u", &sub); | ||
| 561 | if (sub <= max_sub) | ||
| 562 | wlp->dev_info->prim_dev_type.subID = sub; | ||
| 563 | else | ||
| 564 | result = -EINVAL; | ||
| 565 | out: | ||
| 566 | mutex_unlock(&wlp->mutex); | ||
| 567 | return result < 0 ? result : size; | ||
| 568 | } | ||
| 569 | EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store); | ||
| 570 | |||
| 571 | /** | ||
| 572 | * Subsystem implementation for interaction with individual WSS via sysfs | ||
| 573 | * | ||
| 574 | * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt | ||
| 575 | */ | ||
| 576 | |||
| 577 | #define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj) | ||
| 578 | #define attr_to_wlp_wss_attr(_attr) \ | ||
| 579 | container_of(_attr, struct wlp_wss_attribute, attr) | ||
| 580 | |||
| 581 | /** | ||
| 582 | * Sysfs subsystem: forward read calls | ||
| 583 | * | ||
| 584 | * Sysfs operation for forwarding read call to the show method of the | ||
| 585 | * attribute owner | ||
| 586 | */ | ||
| 587 | static | ||
| 588 | ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr, | ||
| 589 | char *buf) | ||
| 590 | { | ||
| 591 | struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); | ||
| 592 | struct wlp_wss *wss = kobj_to_wlp_wss(kobj); | ||
| 593 | ssize_t ret = -EIO; | ||
| 594 | |||
| 595 | if (wss_attr->show) | ||
| 596 | ret = wss_attr->show(wss, buf); | ||
| 597 | return ret; | ||
| 598 | } | ||
| 599 | /** | ||
| 600 | * Sysfs subsystem: forward write calls | ||
| 601 | * | ||
| 602 | * Sysfs operation for forwarding write call to the store method of the | ||
| 603 | * attribute owner | ||
| 604 | */ | ||
| 605 | static | ||
| 606 | ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr, | ||
| 607 | const char *buf, size_t count) | ||
| 608 | { | ||
| 609 | struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); | ||
| 610 | struct wlp_wss *wss = kobj_to_wlp_wss(kobj); | ||
| 611 | ssize_t ret = -EIO; | ||
| 612 | |||
| 613 | if (wss_attr->store) | ||
| 614 | ret = wss_attr->store(wss, buf, count); | ||
| 615 | return ret; | ||
| 616 | } | ||
| 617 | |||
| 618 | static const struct sysfs_ops wss_sysfs_ops = { | ||
| 619 | .show = wlp_wss_attr_show, | ||
| 620 | .store = wlp_wss_attr_store, | ||
| 621 | }; | ||
| 622 | |||
| 623 | struct kobj_type wss_ktype = { | ||
| 624 | .release = wlp_wss_release, | ||
| 625 | .sysfs_ops = &wss_sysfs_ops, | ||
| 626 | }; | ||
| 627 | |||
| 628 | |||
| 629 | /** | ||
| 630 | * Sysfs files for individual WSS | ||
| 631 | */ | ||
| 632 | |||
| 633 | /** | ||
| 634 | * Print static properties of this WSS | ||
| 635 | * | ||
| 636 | * The name of a WSS may not be null teminated. It's max size is 64 bytes | ||
| 637 | * so we copy it to a larger array just to make sure we print sane data. | ||
| 638 | */ | ||
| 639 | static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf) | ||
| 640 | { | ||
| 641 | int result = 0; | ||
| 642 | |||
| 643 | if (mutex_lock_interruptible(&wss->mutex)) | ||
| 644 | goto out; | ||
| 645 | result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); | ||
| 646 | mutex_unlock(&wss->mutex); | ||
| 647 | out: | ||
| 648 | return result; | ||
| 649 | } | ||
| 650 | WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL); | ||
| 651 | |||
| 652 | /** | ||
| 653 | * Print all connected members of this WSS | ||
| 654 | * The EDA cache contains all members of WSS neighborhood. | ||
| 655 | */ | ||
| 656 | static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf) | ||
| 657 | { | ||
| 658 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 659 | return wlp_eda_show(wlp, buf); | ||
| 660 | } | ||
| 661 | WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL); | ||
| 662 | |||
| 663 | static | ||
| 664 | const char *__wlp_strstate[] = { | ||
| 665 | "none", | ||
| 666 | "partially enrolled", | ||
| 667 | "enrolled", | ||
| 668 | "active", | ||
| 669 | "connected", | ||
| 670 | }; | ||
| 671 | |||
| 672 | static const char *wlp_wss_strstate(unsigned state) | ||
| 673 | { | ||
| 674 | if (state >= ARRAY_SIZE(__wlp_strstate)) | ||
| 675 | return "unknown state"; | ||
| 676 | return __wlp_strstate[state]; | ||
| 677 | } | ||
| 678 | |||
| 679 | /* | ||
| 680 | * Print current state of this WSS | ||
| 681 | */ | ||
| 682 | static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf) | ||
| 683 | { | ||
| 684 | int result = 0; | ||
| 685 | |||
| 686 | if (mutex_lock_interruptible(&wss->mutex)) | ||
| 687 | goto out; | ||
| 688 | result = scnprintf(buf, PAGE_SIZE, "%s\n", | ||
| 689 | wlp_wss_strstate(wss->state)); | ||
| 690 | mutex_unlock(&wss->mutex); | ||
| 691 | out: | ||
| 692 | return result; | ||
| 693 | } | ||
| 694 | WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL); | ||
| 695 | |||
| 696 | |||
| 697 | static | ||
| 698 | struct attribute *wss_attrs[] = { | ||
| 699 | &wss_attr_properties.attr, | ||
| 700 | &wss_attr_members.attr, | ||
| 701 | &wss_attr_state.attr, | ||
| 702 | NULL, | ||
| 703 | }; | ||
| 704 | |||
| 705 | struct attribute_group wss_attr_group = { | ||
| 706 | .name = NULL, /* we want them in the same directory */ | ||
| 707 | .attrs = wss_attrs, | ||
| 708 | }; | ||
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c deleted file mode 100644 index 05dde44b3592..000000000000 --- a/drivers/uwb/wlp/txrx.c +++ /dev/null | |||
| @@ -1,354 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * Message exchange infrastructure | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Intel Corporation | ||
| 6 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * FIXME: Docs | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/etherdevice.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | #include <linux/wlp.h> | ||
| 30 | |||
| 31 | #include "wlp-internal.h" | ||
| 32 | |||
| 33 | /* | ||
| 34 | * Direct incoming association msg to correct parsing routine | ||
| 35 | * | ||
| 36 | * We only expect D1, E1, C1, C3 messages as new. All other incoming | ||
| 37 | * association messages should form part of an established session that is | ||
| 38 | * handled elsewhere. | ||
| 39 | * The handling of these messages often require calling sleeping functions | ||
| 40 | * - this cannot be done in interrupt context. We use the kernel's | ||
| 41 | * workqueue to handle these messages. | ||
| 42 | */ | ||
| 43 | static | ||
| 44 | void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 45 | struct uwb_dev_addr *src) | ||
| 46 | { | ||
| 47 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 48 | struct wlp_frame_assoc *assoc = (void *) skb->data; | ||
| 49 | struct wlp_assoc_frame_ctx *frame_ctx; | ||
| 50 | |||
| 51 | frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC); | ||
| 52 | if (frame_ctx == NULL) { | ||
| 53 | dev_err(dev, "WLP: Unable to allocate memory for association " | ||
| 54 | "frame handling.\n"); | ||
| 55 | kfree_skb(skb); | ||
| 56 | return; | ||
| 57 | } | ||
| 58 | frame_ctx->wlp = wlp; | ||
| 59 | frame_ctx->skb = skb; | ||
| 60 | frame_ctx->src = *src; | ||
| 61 | switch (assoc->type) { | ||
| 62 | case WLP_ASSOC_D1: | ||
| 63 | INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame); | ||
| 64 | schedule_work(&frame_ctx->ws); | ||
| 65 | break; | ||
| 66 | case WLP_ASSOC_E1: | ||
| 67 | kfree_skb(skb); /* Temporary until we handle it */ | ||
| 68 | kfree(frame_ctx); /* Temporary until we handle it */ | ||
| 69 | break; | ||
| 70 | case WLP_ASSOC_C1: | ||
| 71 | INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame); | ||
| 72 | schedule_work(&frame_ctx->ws); | ||
| 73 | break; | ||
| 74 | case WLP_ASSOC_C3: | ||
| 75 | INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame); | ||
| 76 | schedule_work(&frame_ctx->ws); | ||
| 77 | break; | ||
| 78 | default: | ||
| 79 | dev_err(dev, "Received unexpected association frame. " | ||
| 80 | "Type = %d \n", assoc->type); | ||
| 81 | kfree_skb(skb); | ||
| 82 | kfree(frame_ctx); | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Process incoming association frame | ||
| 89 | * | ||
| 90 | * Although it could be possible to deal with some incoming association | ||
| 91 | * messages without creating a new session we are keeping things simple. We | ||
| 92 | * do not accept new association messages if there is a session in progress | ||
| 93 | * and the messages do not belong to that session. | ||
| 94 | * | ||
| 95 | * If an association message arrives that causes the creation of a session | ||
| 96 | * (WLP_ASSOC_E1) while we are in the process of creating a session then we | ||
| 97 | * rely on the neighbor mutex to protect the data. That is, the new session | ||
| 98 | * will not be started until the previous is completed. | ||
| 99 | */ | ||
| 100 | static | ||
| 101 | void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 102 | struct uwb_dev_addr *src) | ||
| 103 | { | ||
| 104 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 105 | struct wlp_frame_assoc *assoc = (void *) skb->data; | ||
| 106 | struct wlp_session *session = wlp->session; | ||
| 107 | u8 version; | ||
| 108 | |||
| 109 | if (wlp_get_version(wlp, &assoc->version, &version, | ||
| 110 | sizeof(assoc->version)) < 0) | ||
| 111 | goto error; | ||
| 112 | if (version != WLP_VERSION) { | ||
| 113 | dev_err(dev, "Unsupported WLP version in association " | ||
| 114 | "message.\n"); | ||
| 115 | goto error; | ||
| 116 | } | ||
| 117 | if (session != NULL) { | ||
| 118 | /* Function that created this session is still holding the | ||
| 119 | * &wlp->mutex to protect this session. */ | ||
| 120 | if (assoc->type == session->exp_message || | ||
| 121 | assoc->type == WLP_ASSOC_F0) { | ||
| 122 | if (!memcmp(&session->neighbor_addr, src, | ||
| 123 | sizeof(*src))) { | ||
| 124 | session->data = skb; | ||
| 125 | (session->cb)(wlp); | ||
| 126 | } else { | ||
| 127 | dev_err(dev, "Received expected message from " | ||
| 128 | "unexpected source. Expected message " | ||
| 129 | "%d or F0 from %02x:%02x, but received " | ||
| 130 | "it from %02x:%02x. Dropping.\n", | ||
| 131 | session->exp_message, | ||
| 132 | session->neighbor_addr.data[1], | ||
| 133 | session->neighbor_addr.data[0], | ||
| 134 | src->data[1], src->data[0]); | ||
| 135 | goto error; | ||
| 136 | } | ||
| 137 | } else { | ||
| 138 | dev_err(dev, "Association already in progress. " | ||
| 139 | "Dropping.\n"); | ||
| 140 | goto error; | ||
| 141 | } | ||
| 142 | } else { | ||
| 143 | wlp_direct_assoc_frame(wlp, skb, src); | ||
| 144 | } | ||
| 145 | return; | ||
| 146 | error: | ||
| 147 | kfree_skb(skb); | ||
| 148 | } | ||
| 149 | |||
| 150 | /* | ||
| 151 | * Verify incoming frame is from connected neighbor, prep to pass to WLP client | ||
| 152 | * | ||
| 153 | * Verification proceeds according to WLP 0.99 [7.3.1]. The source address | ||
| 154 | * is used to determine which neighbor is sending the frame and the WSS tag | ||
| 155 | * is used to know to which WSS the frame belongs (we only support one WSS | ||
| 156 | * so this test is straight forward). | ||
| 157 | * With the WSS found we need to ensure that we are connected before | ||
| 158 | * allowing the exchange of data frames. | ||
| 159 | */ | ||
| 160 | static | ||
| 161 | int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb, | ||
| 162 | struct uwb_dev_addr *src) | ||
| 163 | { | ||
| 164 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 165 | int result = -EINVAL; | ||
| 166 | struct wlp_eda_node eda_entry; | ||
| 167 | struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data; | ||
| 168 | |||
| 169 | /*verify*/ | ||
| 170 | result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry); | ||
| 171 | if (result < 0) { | ||
| 172 | if (printk_ratelimit()) | ||
| 173 | dev_err(dev, "WLP: Incoming frame is from unknown " | ||
| 174 | "neighbor %02x:%02x.\n", src->data[1], | ||
| 175 | src->data[0]); | ||
| 176 | goto out; | ||
| 177 | } | ||
| 178 | if (hdr->tag != eda_entry.tag) { | ||
| 179 | if (printk_ratelimit()) | ||
| 180 | dev_err(dev, "WLP: Tag of incoming frame from " | ||
| 181 | "%02x:%02x does not match expected tag. " | ||
| 182 | "Received 0x%02x, expected 0x%02x. \n", | ||
| 183 | src->data[1], src->data[0], hdr->tag, | ||
| 184 | eda_entry.tag); | ||
| 185 | result = -EINVAL; | ||
| 186 | goto out; | ||
| 187 | } | ||
| 188 | if (eda_entry.state != WLP_WSS_CONNECTED) { | ||
| 189 | if (printk_ratelimit()) | ||
| 190 | dev_err(dev, "WLP: Incoming frame from " | ||
| 191 | "%02x:%02x does is not from connected WSS.\n", | ||
| 192 | src->data[1], src->data[0]); | ||
| 193 | result = -EINVAL; | ||
| 194 | goto out; | ||
| 195 | } | ||
| 196 | /*prep*/ | ||
| 197 | skb_pull(skb, sizeof(*hdr)); | ||
| 198 | out: | ||
| 199 | return result; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* | ||
| 203 | * Receive a WLP frame from device | ||
| 204 | * | ||
| 205 | * @returns: 1 if calling function should free the skb | ||
| 206 | * 0 if it successfully handled skb and freed it | ||
| 207 | * 0 if error occured, will free skb in this case | ||
| 208 | */ | ||
| 209 | int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb, | ||
| 210 | struct uwb_dev_addr *src) | ||
| 211 | { | ||
| 212 | unsigned len = skb->len; | ||
| 213 | void *ptr = skb->data; | ||
| 214 | struct wlp_frame_hdr *hdr; | ||
| 215 | int result = 0; | ||
| 216 | |||
| 217 | if (len < sizeof(*hdr)) { | ||
| 218 | dev_err(dev, "Not enough data to parse WLP header.\n"); | ||
| 219 | result = -EINVAL; | ||
| 220 | goto out; | ||
| 221 | } | ||
| 222 | hdr = ptr; | ||
| 223 | if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) { | ||
| 224 | dev_err(dev, "Not a WLP frame type.\n"); | ||
| 225 | result = -EINVAL; | ||
| 226 | goto out; | ||
| 227 | } | ||
| 228 | switch (hdr->type) { | ||
| 229 | case WLP_FRAME_STANDARD: | ||
| 230 | if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) { | ||
| 231 | dev_err(dev, "Not enough data to parse Standard " | ||
| 232 | "WLP header.\n"); | ||
| 233 | goto out; | ||
| 234 | } | ||
| 235 | result = wlp_verify_prep_rx_frame(wlp, skb, src); | ||
| 236 | if (result < 0) { | ||
| 237 | if (printk_ratelimit()) | ||
| 238 | dev_err(dev, "WLP: Verification of frame " | ||
| 239 | "from neighbor %02x:%02x failed.\n", | ||
| 240 | src->data[1], src->data[0]); | ||
| 241 | goto out; | ||
| 242 | } | ||
| 243 | result = 1; | ||
| 244 | break; | ||
| 245 | case WLP_FRAME_ABBREVIATED: | ||
| 246 | dev_err(dev, "Abbreviated frame received. FIXME?\n"); | ||
| 247 | kfree_skb(skb); | ||
| 248 | break; | ||
| 249 | case WLP_FRAME_CONTROL: | ||
| 250 | dev_err(dev, "Control frame received. FIXME?\n"); | ||
| 251 | kfree_skb(skb); | ||
| 252 | break; | ||
| 253 | case WLP_FRAME_ASSOCIATION: | ||
| 254 | if (len < sizeof(struct wlp_frame_assoc)) { | ||
| 255 | dev_err(dev, "Not enough data to parse Association " | ||
| 256 | "WLP header.\n"); | ||
| 257 | goto out; | ||
| 258 | } | ||
| 259 | wlp_receive_assoc_frame(wlp, skb, src); | ||
| 260 | break; | ||
| 261 | default: | ||
| 262 | dev_err(dev, "Invalid frame received.\n"); | ||
| 263 | result = -EINVAL; | ||
| 264 | break; | ||
| 265 | } | ||
| 266 | out: | ||
| 267 | if (result < 0) { | ||
| 268 | kfree_skb(skb); | ||
| 269 | result = 0; | ||
| 270 | } | ||
| 271 | return result; | ||
| 272 | } | ||
| 273 | EXPORT_SYMBOL_GPL(wlp_receive_frame); | ||
| 274 | |||
| 275 | |||
| 276 | /* | ||
| 277 | * Verify frame from network stack, prepare for further transmission | ||
| 278 | * | ||
| 279 | * @skb: the socket buffer that needs to be prepared for transmission (it | ||
| 280 | * is in need of a WLP header). If this is a broadcast frame we take | ||
| 281 | * over the entire transmission. | ||
| 282 | * If it is a unicast the WSS connection should already be established | ||
| 283 | * and transmission will be done by the calling function. | ||
| 284 | * @dst: On return this will contain the device address to which the | ||
| 285 | * frame is destined. | ||
| 286 | * @returns: 0 on success no tx : WLP header successfully applied to skb buffer, | ||
| 287 | * calling function can proceed with tx | ||
| 288 | * 1 on success with tx : WLP will take over transmission of this | ||
| 289 | * frame | ||
| 290 | * <0 on error | ||
| 291 | * | ||
| 292 | * The network stack (WLP client) is attempting to transmit a frame. We can | ||
| 293 | * only transmit data if a local WSS is at least active (connection will be | ||
| 294 | * done here if this is a broadcast frame and neighbor also has the WSS | ||
| 295 | * active). | ||
| 296 | * | ||
| 297 | * The frame can be either broadcast or unicast. Broadcast in a WSS is | ||
| 298 | * supported via multicast, but we don't support multicast yet (until | ||
| 299 | * devices start to support MAB IEs). If a broadcast frame needs to be | ||
| 300 | * transmitted it is treated as a unicast frame to each neighbor. In this | ||
| 301 | * case the WLP takes over transmission of the skb and returns 1 | ||
| 302 | * to the caller to indicate so. Also, in this case, if a neighbor has the | ||
| 303 | * same WSS activated but is not connected then the WSS connection will be | ||
| 304 | * done at this time. The neighbor's virtual address will be learned at | ||
| 305 | * this time. | ||
| 306 | * | ||
| 307 | * The destination address in a unicast frame is the virtual address of the | ||
| 308 | * neighbor. This address only becomes known when a WSS connection is | ||
| 309 | * established. We thus rely on a broadcast frame to trigger the setup of | ||
| 310 | * WSS connections to all neighbors before we are able to send unicast | ||
| 311 | * frames to them. This seems reasonable as IP would usually use ARP first | ||
| 312 | * before any unicast frames are sent. | ||
| 313 | * | ||
| 314 | * If we are already connected to the neighbor (neighbor's virtual address | ||
| 315 | * is known) we just prepare the WLP header and the caller will continue to | ||
| 316 | * send the frame. | ||
| 317 | * | ||
| 318 | * A failure in this function usually indicates something that cannot be | ||
| 319 | * fixed automatically. So, if this function fails (@return < 0) the calling | ||
| 320 | * function should not retry to send the frame as it will very likely keep | ||
| 321 | * failing. | ||
| 322 | * | ||
| 323 | */ | ||
| 324 | int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp, | ||
| 325 | struct sk_buff *skb, struct uwb_dev_addr *dst) | ||
| 326 | { | ||
| 327 | int result = -EINVAL; | ||
| 328 | struct ethhdr *eth_hdr = (void *) skb->data; | ||
| 329 | |||
| 330 | if (is_multicast_ether_addr(eth_hdr->h_dest)) { | ||
| 331 | result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb); | ||
| 332 | if (result < 0) { | ||
| 333 | if (printk_ratelimit()) | ||
| 334 | dev_err(dev, "Unable to handle broadcast " | ||
| 335 | "frame from WLP client.\n"); | ||
| 336 | goto out; | ||
| 337 | } | ||
| 338 | dev_kfree_skb_irq(skb); | ||
| 339 | result = 1; | ||
| 340 | /* Frame will be transmitted by WLP. */ | ||
| 341 | } else { | ||
| 342 | result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst, | ||
| 343 | wlp_wss_prep_hdr, skb); | ||
| 344 | if (unlikely(result < 0)) { | ||
| 345 | if (printk_ratelimit()) | ||
| 346 | dev_err(dev, "Unable to prepare " | ||
| 347 | "skb for transmission. \n"); | ||
| 348 | goto out; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | out: | ||
| 352 | return result; | ||
| 353 | } | ||
| 354 | EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame); | ||
diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h deleted file mode 100644 index 3e8d5de7c5b9..000000000000 --- a/drivers/uwb/wlp/wlp-internal.h +++ /dev/null | |||
| @@ -1,224 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * Internal API | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Intel Corporation | ||
| 6 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #ifndef __WLP_INTERNAL_H__ | ||
| 25 | #define __WLP_INTERNAL_H__ | ||
| 26 | |||
| 27 | /** | ||
| 28 | * State of WSS connection | ||
| 29 | * | ||
| 30 | * A device needs to connect to a neighbor in an activated WSS before data | ||
| 31 | * can be transmitted. The spec also distinguishes between a new connection | ||
| 32 | * attempt and a connection attempt after previous connection attempts. The | ||
| 33 | * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99 | ||
| 34 | * [7.2.6] | ||
| 35 | */ | ||
| 36 | enum wlp_wss_connect { | ||
| 37 | WLP_WSS_UNCONNECTED = 0, | ||
| 38 | WLP_WSS_CONNECTED, | ||
| 39 | WLP_WSS_CONNECT_FAILED, | ||
| 40 | }; | ||
| 41 | |||
| 42 | extern struct kobj_type wss_ktype; | ||
| 43 | extern struct attribute_group wss_attr_group; | ||
| 44 | |||
| 45 | /* This should be changed to a dynamic array where entries are sorted | ||
| 46 | * by eth_addr and search is done in a binary form | ||
| 47 | * | ||
| 48 | * Although thinking twice about it: this technologie's maximum reach | ||
| 49 | * is 10 meters...unless you want to pack too much stuff in around | ||
| 50 | * your radio controller/WLP device, the list will probably not be | ||
| 51 | * too big. | ||
| 52 | * | ||
| 53 | * In any case, there is probably some data structure in the kernel | ||
| 54 | * than we could reused for that already. | ||
| 55 | * | ||
| 56 | * The below structure is really just good while we support one WSS per | ||
| 57 | * host. | ||
| 58 | */ | ||
| 59 | struct wlp_eda_node { | ||
| 60 | struct list_head list_node; | ||
| 61 | unsigned char eth_addr[ETH_ALEN]; | ||
| 62 | struct uwb_dev_addr dev_addr; | ||
| 63 | struct wlp_wss *wss; | ||
| 64 | unsigned char virt_addr[ETH_ALEN]; | ||
| 65 | u8 tag; | ||
| 66 | enum wlp_wss_connect state; | ||
| 67 | }; | ||
| 68 | |||
| 69 | typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *); | ||
| 70 | |||
| 71 | extern void wlp_eda_init(struct wlp_eda *); | ||
| 72 | extern void wlp_eda_release(struct wlp_eda *); | ||
| 73 | extern int wlp_eda_create_node(struct wlp_eda *, | ||
| 74 | const unsigned char eth_addr[ETH_ALEN], | ||
| 75 | const struct uwb_dev_addr *); | ||
| 76 | extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *); | ||
| 77 | extern int wlp_eda_update_node(struct wlp_eda *, | ||
| 78 | const struct uwb_dev_addr *, | ||
| 79 | struct wlp_wss *, | ||
| 80 | const unsigned char virt_addr[ETH_ALEN], | ||
| 81 | const u8, const enum wlp_wss_connect); | ||
| 82 | extern int wlp_eda_update_node_state(struct wlp_eda *, | ||
| 83 | const struct uwb_dev_addr *, | ||
| 84 | const enum wlp_wss_connect); | ||
| 85 | |||
| 86 | extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *, | ||
| 87 | struct wlp_eda_node *); | ||
| 88 | extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *); | ||
| 89 | extern int wlp_eda_for_virtual(struct wlp_eda *, | ||
| 90 | const unsigned char eth_addr[ETH_ALEN], | ||
| 91 | struct uwb_dev_addr *, | ||
| 92 | wlp_eda_for_each_f , void *); | ||
| 93 | |||
| 94 | |||
| 95 | extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *); | ||
| 96 | |||
| 97 | extern size_t wlp_wss_key_print(char *, size_t, u8 *); | ||
| 98 | |||
| 99 | /* Function called when no more references to WSS exists */ | ||
| 100 | extern void wlp_wss_release(struct kobject *); | ||
| 101 | |||
| 102 | extern void wlp_wss_reset(struct wlp_wss *); | ||
| 103 | extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *, | ||
| 104 | char *, unsigned, unsigned); | ||
| 105 | extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *, | ||
| 106 | struct uwb_dev_addr *); | ||
| 107 | extern ssize_t wlp_discover(struct wlp *); | ||
| 108 | |||
| 109 | extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *, | ||
| 110 | struct wlp_wss *, struct wlp_uuid *); | ||
| 111 | extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *, | ||
| 112 | struct uwb_dev_addr *); | ||
| 113 | |||
| 114 | struct wlp_assoc_conn_ctx { | ||
| 115 | struct work_struct ws; | ||
| 116 | struct wlp *wlp; | ||
| 117 | struct sk_buff *skb; | ||
| 118 | struct wlp_eda_node eda_entry; | ||
| 119 | }; | ||
| 120 | |||
| 121 | |||
| 122 | extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *); | ||
| 123 | extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *); | ||
| 124 | |||
| 125 | |||
| 126 | /* Message handling */ | ||
| 127 | struct wlp_assoc_frame_ctx { | ||
| 128 | struct work_struct ws; | ||
| 129 | struct wlp *wlp; | ||
| 130 | struct sk_buff *skb; | ||
| 131 | struct uwb_dev_addr src; | ||
| 132 | }; | ||
| 133 | |||
| 134 | extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *); | ||
| 135 | extern void wlp_handle_d1_frame(struct work_struct *); | ||
| 136 | extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *, | ||
| 137 | struct wlp_neighbor_e *); | ||
| 138 | extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *, | ||
| 139 | struct wlp_neighbor_e *, | ||
| 140 | struct wlp_uuid *); | ||
| 141 | extern void wlp_handle_c1_frame(struct work_struct *); | ||
| 142 | extern void wlp_handle_c3_frame(struct work_struct *); | ||
| 143 | extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *, | ||
| 144 | struct wlp_uuid *, u8 *, | ||
| 145 | struct uwb_mac_addr *); | ||
| 146 | extern int wlp_parse_f0(struct wlp *, struct sk_buff *); | ||
| 147 | extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *, | ||
| 148 | struct uwb_dev_addr *, enum wlp_assoc_type); | ||
| 149 | extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *, | ||
| 150 | u8 *, ssize_t); | ||
| 151 | extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *, | ||
| 152 | struct wlp_uuid *, ssize_t); | ||
| 153 | extern int __wlp_alloc_device_info(struct wlp *); | ||
| 154 | extern int __wlp_setup_device_info(struct wlp *); | ||
| 155 | |||
| 156 | extern struct wlp_wss_attribute wss_attribute_properties; | ||
| 157 | extern struct wlp_wss_attribute wss_attribute_members; | ||
| 158 | extern struct wlp_wss_attribute wss_attribute_state; | ||
| 159 | |||
| 160 | static inline | ||
| 161 | size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid) | ||
| 162 | { | ||
| 163 | size_t result; | ||
| 164 | |||
| 165 | result = scnprintf(buf, bufsize, | ||
| 166 | "%02x:%02x:%02x:%02x:%02x:%02x:" | ||
| 167 | "%02x:%02x:%02x:%02x:%02x:%02x:" | ||
| 168 | "%02x:%02x:%02x:%02x", | ||
| 169 | uuid->data[0], uuid->data[1], | ||
| 170 | uuid->data[2], uuid->data[3], | ||
| 171 | uuid->data[4], uuid->data[5], | ||
| 172 | uuid->data[6], uuid->data[7], | ||
| 173 | uuid->data[8], uuid->data[9], | ||
| 174 | uuid->data[10], uuid->data[11], | ||
| 175 | uuid->data[12], uuid->data[13], | ||
| 176 | uuid->data[14], uuid->data[15]); | ||
| 177 | return result; | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * FIXME: How should a nonce be displayed? | ||
| 182 | */ | ||
| 183 | static inline | ||
| 184 | size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce) | ||
| 185 | { | ||
| 186 | size_t result; | ||
| 187 | |||
| 188 | result = scnprintf(buf, bufsize, | ||
| 189 | "%02x %02x %02x %02x %02x %02x " | ||
| 190 | "%02x %02x %02x %02x %02x %02x " | ||
| 191 | "%02x %02x %02x %02x", | ||
| 192 | nonce->data[0], nonce->data[1], | ||
| 193 | nonce->data[2], nonce->data[3], | ||
| 194 | nonce->data[4], nonce->data[5], | ||
| 195 | nonce->data[6], nonce->data[7], | ||
| 196 | nonce->data[8], nonce->data[9], | ||
| 197 | nonce->data[10], nonce->data[11], | ||
| 198 | nonce->data[12], nonce->data[13], | ||
| 199 | nonce->data[14], nonce->data[15]); | ||
| 200 | return result; | ||
| 201 | } | ||
| 202 | |||
| 203 | |||
| 204 | static inline | ||
| 205 | void wlp_session_cb(struct wlp *wlp) | ||
| 206 | { | ||
| 207 | struct completion *completion = wlp->session->cb_priv; | ||
| 208 | complete(completion); | ||
| 209 | } | ||
| 210 | |||
| 211 | static inline | ||
| 212 | int wlp_uuid_is_set(struct wlp_uuid *uuid) | ||
| 213 | { | ||
| 214 | struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00, | ||
| 215 | 0x00, 0x00, 0x00, 0x00, | ||
| 216 | 0x00, 0x00, 0x00, 0x00, | ||
| 217 | 0x00, 0x00, 0x00, 0x00} }; | ||
| 218 | |||
| 219 | if (!memcmp(uuid, &zero_uuid, sizeof(*uuid))) | ||
| 220 | return 0; | ||
| 221 | return 1; | ||
| 222 | } | ||
| 223 | |||
| 224 | #endif /* __WLP_INTERNAL_H__ */ | ||
diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c deleted file mode 100644 index 7f6a630bf26c..000000000000 --- a/drivers/uwb/wlp/wlp-lc.c +++ /dev/null | |||
| @@ -1,560 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 5 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License version | ||
| 9 | * 2 as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 19 | * 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * FIXME: docs | ||
| 23 | */ | ||
| 24 | #include <linux/wlp.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | |||
| 27 | #include "wlp-internal.h" | ||
| 28 | |||
| 29 | static | ||
| 30 | void wlp_neighbor_init(struct wlp_neighbor_e *neighbor) | ||
| 31 | { | ||
| 32 | INIT_LIST_HEAD(&neighbor->wssid); | ||
| 33 | } | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Create area for device information storage | ||
| 37 | * | ||
| 38 | * wlp->mutex must be held | ||
| 39 | */ | ||
| 40 | int __wlp_alloc_device_info(struct wlp *wlp) | ||
| 41 | { | ||
| 42 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 43 | BUG_ON(wlp->dev_info != NULL); | ||
| 44 | wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); | ||
| 45 | if (wlp->dev_info == NULL) { | ||
| 46 | dev_err(dev, "WLP: Unable to allocate memory for " | ||
| 47 | "device information.\n"); | ||
| 48 | return -ENOMEM; | ||
| 49 | } | ||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | |||
| 54 | /** | ||
| 55 | * Fill in device information using function provided by driver | ||
| 56 | * | ||
| 57 | * wlp->mutex must be held | ||
| 58 | */ | ||
| 59 | static | ||
| 60 | void __wlp_fill_device_info(struct wlp *wlp) | ||
| 61 | { | ||
| 62 | wlp->fill_device_info(wlp, wlp->dev_info); | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Setup device information | ||
| 67 | * | ||
| 68 | * Allocate area for device information and populate it. | ||
| 69 | * | ||
| 70 | * wlp->mutex must be held | ||
| 71 | */ | ||
| 72 | int __wlp_setup_device_info(struct wlp *wlp) | ||
| 73 | { | ||
| 74 | int result; | ||
| 75 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 76 | |||
| 77 | result = __wlp_alloc_device_info(wlp); | ||
| 78 | if (result < 0) { | ||
| 79 | dev_err(dev, "WLP: Unable to allocate area for " | ||
| 80 | "device information.\n"); | ||
| 81 | return result; | ||
| 82 | } | ||
| 83 | __wlp_fill_device_info(wlp); | ||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Remove information about neighbor stored temporarily | ||
| 89 | * | ||
| 90 | * Information learned during discovey should only be stored when the | ||
| 91 | * device enrolls in the neighbor's WSS. We do need to store this | ||
| 92 | * information temporarily in order to present it to the user. | ||
| 93 | * | ||
| 94 | * We are only interested in keeping neighbor WSS information if that | ||
| 95 | * neighbor is accepting enrollment. | ||
| 96 | * | ||
| 97 | * should be called with wlp->nbmutex held | ||
| 98 | */ | ||
| 99 | void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor) | ||
| 100 | { | ||
| 101 | struct wlp_wssid_e *wssid_e, *next; | ||
| 102 | u8 keep; | ||
| 103 | if (!list_empty(&neighbor->wssid)) { | ||
| 104 | list_for_each_entry_safe(wssid_e, next, &neighbor->wssid, | ||
| 105 | node) { | ||
| 106 | if (wssid_e->info != NULL) { | ||
| 107 | keep = wssid_e->info->accept_enroll; | ||
| 108 | kfree(wssid_e->info); | ||
| 109 | wssid_e->info = NULL; | ||
| 110 | if (!keep) { | ||
| 111 | list_del(&wssid_e->node); | ||
| 112 | kfree(wssid_e); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | if (neighbor->info != NULL) { | ||
| 118 | kfree(neighbor->info); | ||
| 119 | neighbor->info = NULL; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * Populate WLP neighborhood cache with neighbor information | ||
| 125 | * | ||
| 126 | * A new neighbor is found. If it is discoverable then we add it to the | ||
| 127 | * neighborhood cache. | ||
| 128 | * | ||
| 129 | */ | ||
| 130 | static | ||
| 131 | int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev) | ||
| 132 | { | ||
| 133 | int result = 0; | ||
| 134 | int discoverable; | ||
| 135 | struct wlp_neighbor_e *neighbor; | ||
| 136 | |||
| 137 | /* | ||
| 138 | * FIXME: | ||
| 139 | * Use contents of WLP IE found in beacon cache to determine if | ||
| 140 | * neighbor is discoverable. | ||
| 141 | * The device does not support WLP IE yet so this still needs to be | ||
| 142 | * done. Until then we assume all devices are discoverable. | ||
| 143 | */ | ||
| 144 | discoverable = 1; /* will be changed when FIXME disappears */ | ||
| 145 | if (discoverable) { | ||
| 146 | /* Add neighbor to cache for discovery */ | ||
| 147 | neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL); | ||
| 148 | if (neighbor == NULL) { | ||
| 149 | dev_err(&dev->dev, "Unable to create memory for " | ||
| 150 | "new neighbor. \n"); | ||
| 151 | result = -ENOMEM; | ||
| 152 | goto error_no_mem; | ||
| 153 | } | ||
| 154 | wlp_neighbor_init(neighbor); | ||
| 155 | uwb_dev_get(dev); | ||
| 156 | neighbor->uwb_dev = dev; | ||
| 157 | list_add(&neighbor->node, &wlp->neighbors); | ||
| 158 | } | ||
| 159 | error_no_mem: | ||
| 160 | return result; | ||
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Remove one neighbor from cache | ||
| 165 | */ | ||
| 166 | static | ||
| 167 | void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor) | ||
| 168 | { | ||
| 169 | struct wlp_wssid_e *wssid_e, *next_wssid_e; | ||
| 170 | |||
| 171 | list_for_each_entry_safe(wssid_e, next_wssid_e, | ||
| 172 | &neighbor->wssid, node) { | ||
| 173 | list_del(&wssid_e->node); | ||
| 174 | kfree(wssid_e); | ||
| 175 | } | ||
| 176 | uwb_dev_put(neighbor->uwb_dev); | ||
| 177 | list_del(&neighbor->node); | ||
| 178 | kfree(neighbor); | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Clear entire neighborhood cache. | ||
| 183 | */ | ||
| 184 | static | ||
| 185 | void __wlp_neighbors_release(struct wlp *wlp) | ||
| 186 | { | ||
| 187 | struct wlp_neighbor_e *neighbor, *next; | ||
| 188 | if (list_empty(&wlp->neighbors)) | ||
| 189 | return; | ||
| 190 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { | ||
| 191 | __wlp_neighbor_release(neighbor); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | static | ||
| 196 | void wlp_neighbors_release(struct wlp *wlp) | ||
| 197 | { | ||
| 198 | mutex_lock(&wlp->nbmutex); | ||
| 199 | __wlp_neighbors_release(wlp); | ||
| 200 | mutex_unlock(&wlp->nbmutex); | ||
| 201 | } | ||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | /** | ||
| 206 | * Send D1 message to neighbor, receive D2 message | ||
| 207 | * | ||
| 208 | * @neighbor: neighbor to which D1 message will be sent | ||
| 209 | * @wss: if not NULL, it is an enrollment request for this WSS | ||
| 210 | * @wssid: if wss not NULL, this is the wssid of the WSS in which we | ||
| 211 | * want to enroll | ||
| 212 | * | ||
| 213 | * A D1/D2 exchange is done for one of two reasons: discovery or | ||
| 214 | * enrollment. If done for discovery the D1 message is sent to the neighbor | ||
| 215 | * and the contents of the D2 response is stored in a temporary cache. | ||
| 216 | * If done for enrollment the @wss and @wssid are provided also. In this | ||
| 217 | * case the D1 message is sent to the neighbor, the D2 response is parsed | ||
| 218 | * for enrollment of the WSS with wssid. | ||
| 219 | * | ||
| 220 | * &wss->mutex is held | ||
| 221 | */ | ||
| 222 | static | ||
| 223 | int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor, | ||
| 224 | struct wlp_wss *wss, struct wlp_uuid *wssid) | ||
| 225 | { | ||
| 226 | int result; | ||
| 227 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 228 | DECLARE_COMPLETION_ONSTACK(completion); | ||
| 229 | struct wlp_session session; | ||
| 230 | struct sk_buff *skb; | ||
| 231 | struct wlp_frame_assoc *resp; | ||
| 232 | struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; | ||
| 233 | |||
| 234 | mutex_lock(&wlp->mutex); | ||
| 235 | if (!wlp_uuid_is_set(&wlp->uuid)) { | ||
| 236 | dev_err(dev, "WLP: UUID is not set. Set via sysfs to " | ||
| 237 | "proceed.\n"); | ||
| 238 | result = -ENXIO; | ||
| 239 | goto out; | ||
| 240 | } | ||
| 241 | /* Send D1 association frame */ | ||
| 242 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1); | ||
| 243 | if (result < 0) { | ||
| 244 | dev_err(dev, "Unable to send D1 frame to neighbor " | ||
| 245 | "%02x:%02x (%d)\n", dev_addr->data[1], | ||
| 246 | dev_addr->data[0], result); | ||
| 247 | goto out; | ||
| 248 | } | ||
| 249 | /* Create session, wait for response */ | ||
| 250 | session.exp_message = WLP_ASSOC_D2; | ||
| 251 | session.cb = wlp_session_cb; | ||
| 252 | session.cb_priv = &completion; | ||
| 253 | session.neighbor_addr = *dev_addr; | ||
| 254 | BUG_ON(wlp->session != NULL); | ||
| 255 | wlp->session = &session; | ||
| 256 | /* Wait for D2/F0 frame */ | ||
| 257 | result = wait_for_completion_interruptible_timeout(&completion, | ||
| 258 | WLP_PER_MSG_TIMEOUT * HZ); | ||
| 259 | if (result == 0) { | ||
| 260 | result = -ETIMEDOUT; | ||
| 261 | dev_err(dev, "Timeout while sending D1 to neighbor " | ||
| 262 | "%02x:%02x.\n", dev_addr->data[1], | ||
| 263 | dev_addr->data[0]); | ||
| 264 | goto error_session; | ||
| 265 | } | ||
| 266 | if (result < 0) { | ||
| 267 | dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n", | ||
| 268 | dev_addr->data[1], dev_addr->data[0]); | ||
| 269 | goto error_session; | ||
| 270 | } | ||
| 271 | /* Parse message in session->data: it will be either D2 or F0 */ | ||
| 272 | skb = session.data; | ||
| 273 | resp = (void *) skb->data; | ||
| 274 | |||
| 275 | if (resp->type == WLP_ASSOC_F0) { | ||
| 276 | result = wlp_parse_f0(wlp, skb); | ||
| 277 | if (result < 0) | ||
| 278 | dev_err(dev, "WLP: Unable to parse F0 from neighbor " | ||
| 279 | "%02x:%02x.\n", dev_addr->data[1], | ||
| 280 | dev_addr->data[0]); | ||
| 281 | result = -EINVAL; | ||
| 282 | goto error_resp_parse; | ||
| 283 | } | ||
| 284 | if (wss == NULL) { | ||
| 285 | /* Discovery */ | ||
| 286 | result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor); | ||
| 287 | if (result < 0) { | ||
| 288 | dev_err(dev, "WLP: Unable to parse D2 message from " | ||
| 289 | "neighbor %02x:%02x for discovery.\n", | ||
| 290 | dev_addr->data[1], dev_addr->data[0]); | ||
| 291 | goto error_resp_parse; | ||
| 292 | } | ||
| 293 | } else { | ||
| 294 | /* Enrollment */ | ||
| 295 | result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor, | ||
| 296 | wssid); | ||
| 297 | if (result < 0) { | ||
| 298 | dev_err(dev, "WLP: Unable to parse D2 message from " | ||
| 299 | "neighbor %02x:%02x for enrollment.\n", | ||
| 300 | dev_addr->data[1], dev_addr->data[0]); | ||
| 301 | goto error_resp_parse; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | error_resp_parse: | ||
| 305 | kfree_skb(skb); | ||
| 306 | error_session: | ||
| 307 | wlp->session = NULL; | ||
| 308 | out: | ||
| 309 | mutex_unlock(&wlp->mutex); | ||
| 310 | return result; | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * Enroll into WSS of provided WSSID by using neighbor as registrar | ||
| 315 | * | ||
| 316 | * &wss->mutex is held | ||
| 317 | */ | ||
| 318 | int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor, | ||
| 319 | struct wlp_wss *wss, struct wlp_uuid *wssid) | ||
| 320 | { | ||
| 321 | int result = 0; | ||
| 322 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 323 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 324 | struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; | ||
| 325 | |||
| 326 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | ||
| 327 | |||
| 328 | result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid); | ||
| 329 | if (result < 0) { | ||
| 330 | dev_err(dev, "WLP: D1/D2 message exchange for enrollment " | ||
| 331 | "failed. result = %d \n", result); | ||
| 332 | goto out; | ||
| 333 | } | ||
| 334 | if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { | ||
| 335 | dev_err(dev, "WLP: Unable to enroll into WSS %s using " | ||
| 336 | "neighbor %02x:%02x. \n", buf, | ||
| 337 | dev_addr->data[1], dev_addr->data[0]); | ||
| 338 | result = -EINVAL; | ||
| 339 | goto out; | ||
| 340 | } | ||
| 341 | if (wss->secure_status == WLP_WSS_SECURE) { | ||
| 342 | dev_err(dev, "FIXME: need to complete secure enrollment.\n"); | ||
| 343 | result = -EINVAL; | ||
| 344 | goto error; | ||
| 345 | } else { | ||
| 346 | wss->state = WLP_WSS_STATE_ENROLLED; | ||
| 347 | dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS " | ||
| 348 | "%s using neighbor %02x:%02x. \n", | ||
| 349 | buf, dev_addr->data[1], dev_addr->data[0]); | ||
| 350 | } | ||
| 351 | out: | ||
| 352 | return result; | ||
| 353 | error: | ||
| 354 | wlp_wss_reset(wss); | ||
| 355 | return result; | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * Discover WSS information of neighbor's active WSS | ||
| 360 | */ | ||
| 361 | static | ||
| 362 | int wlp_discover_neighbor(struct wlp *wlp, | ||
| 363 | struct wlp_neighbor_e *neighbor) | ||
| 364 | { | ||
| 365 | return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL); | ||
| 366 | } | ||
| 367 | |||
| 368 | |||
| 369 | /** | ||
| 370 | * Each neighbor in the neighborhood cache is discoverable. Discover it. | ||
| 371 | * | ||
| 372 | * Discovery is done through sending of D1 association frame and parsing | ||
| 373 | * the D2 association frame response. Only wssid from D2 will be included | ||
| 374 | * in neighbor cache, rest is just displayed to user and forgotten. | ||
| 375 | * | ||
| 376 | * The discovery is not done in parallel. This is simple and enables us to | ||
| 377 | * maintain only one association context. | ||
| 378 | * | ||
| 379 | * The discovery of one neighbor does not affect the other, but if the | ||
| 380 | * discovery of a neighbor fails it is removed from the neighborhood cache. | ||
| 381 | */ | ||
| 382 | static | ||
| 383 | int wlp_discover_all_neighbors(struct wlp *wlp) | ||
| 384 | { | ||
| 385 | int result = 0; | ||
| 386 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 387 | struct wlp_neighbor_e *neighbor, *next; | ||
| 388 | |||
| 389 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { | ||
| 390 | result = wlp_discover_neighbor(wlp, neighbor); | ||
| 391 | if (result < 0) { | ||
| 392 | dev_err(dev, "WLP: Unable to discover neighbor " | ||
| 393 | "%02x:%02x, removing from neighborhood. \n", | ||
| 394 | neighbor->uwb_dev->dev_addr.data[1], | ||
| 395 | neighbor->uwb_dev->dev_addr.data[0]); | ||
| 396 | __wlp_neighbor_release(neighbor); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | return result; | ||
| 400 | } | ||
| 401 | |||
| 402 | static int wlp_add_neighbor_helper(struct device *dev, void *priv) | ||
| 403 | { | ||
| 404 | struct wlp *wlp = priv; | ||
| 405 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||
| 406 | |||
| 407 | return wlp_add_neighbor(wlp, uwb_dev); | ||
| 408 | } | ||
| 409 | |||
| 410 | /** | ||
| 411 | * Discover WLP neighborhood | ||
| 412 | * | ||
| 413 | * Will send D1 association frame to all devices in beacon group that have | ||
| 414 | * discoverable bit set in WLP IE. D2 frames will be received, information | ||
| 415 | * displayed to user in @buf. Partial information (from D2 association | ||
| 416 | * frame) will be cached to assist with future association | ||
| 417 | * requests. | ||
| 418 | * | ||
| 419 | * The discovery of the WLP neighborhood is triggered by the user. This | ||
| 420 | * should occur infrequently and we thus free current cache and re-allocate | ||
| 421 | * memory if needed. | ||
| 422 | * | ||
| 423 | * If one neighbor fails during initial discovery (determining if it is a | ||
| 424 | * neighbor or not), we fail all - note that interaction with neighbor has | ||
| 425 | * not occured at this point so if a failure occurs we know something went wrong | ||
| 426 | * locally. We thus undo everything. | ||
| 427 | */ | ||
| 428 | ssize_t wlp_discover(struct wlp *wlp) | ||
| 429 | { | ||
| 430 | int result = 0; | ||
| 431 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 432 | |||
| 433 | mutex_lock(&wlp->nbmutex); | ||
| 434 | /* Clear current neighborhood cache. */ | ||
| 435 | __wlp_neighbors_release(wlp); | ||
| 436 | /* Determine which devices in neighborhood. Repopulate cache. */ | ||
| 437 | result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp); | ||
| 438 | if (result < 0) { | ||
| 439 | /* May have partial neighbor information, release all. */ | ||
| 440 | __wlp_neighbors_release(wlp); | ||
| 441 | goto error_dev_for_each; | ||
| 442 | } | ||
| 443 | /* Discover the properties of devices in neighborhood. */ | ||
| 444 | result = wlp_discover_all_neighbors(wlp); | ||
| 445 | /* In case of failure we still print our partial results. */ | ||
| 446 | if (result < 0) { | ||
| 447 | dev_err(dev, "Unable to fully discover neighborhood. \n"); | ||
| 448 | result = 0; | ||
| 449 | } | ||
| 450 | error_dev_for_each: | ||
| 451 | mutex_unlock(&wlp->nbmutex); | ||
| 452 | return result; | ||
| 453 | } | ||
| 454 | |||
| 455 | /** | ||
| 456 | * Handle events from UWB stack | ||
| 457 | * | ||
| 458 | * We handle events conservatively. If a neighbor goes off the air we | ||
| 459 | * remove it from the neighborhood. If an association process is in | ||
| 460 | * progress this function will block waiting for the nbmutex to become | ||
| 461 | * free. The association process will thus be allowed to complete before it | ||
| 462 | * is removed. | ||
| 463 | */ | ||
| 464 | static | ||
| 465 | void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev, | ||
| 466 | enum uwb_notifs event) | ||
| 467 | { | ||
| 468 | struct wlp *wlp = _wlp; | ||
| 469 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 470 | struct wlp_neighbor_e *neighbor, *next; | ||
| 471 | int result; | ||
| 472 | switch (event) { | ||
| 473 | case UWB_NOTIF_ONAIR: | ||
| 474 | result = wlp_eda_create_node(&wlp->eda, | ||
| 475 | uwb_dev->mac_addr.data, | ||
| 476 | &uwb_dev->dev_addr); | ||
| 477 | if (result < 0) | ||
| 478 | dev_err(dev, "WLP: Unable to add new neighbor " | ||
| 479 | "%02x:%02x to EDA cache.\n", | ||
| 480 | uwb_dev->dev_addr.data[1], | ||
| 481 | uwb_dev->dev_addr.data[0]); | ||
| 482 | break; | ||
| 483 | case UWB_NOTIF_OFFAIR: | ||
| 484 | wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr); | ||
| 485 | mutex_lock(&wlp->nbmutex); | ||
| 486 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { | ||
| 487 | if (neighbor->uwb_dev == uwb_dev) | ||
| 488 | __wlp_neighbor_release(neighbor); | ||
| 489 | } | ||
| 490 | mutex_unlock(&wlp->nbmutex); | ||
| 491 | break; | ||
| 492 | default: | ||
| 493 | dev_err(dev, "don't know how to handle event %d from uwb\n", | ||
| 494 | event); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | static void wlp_channel_changed(struct uwb_pal *pal, int channel) | ||
| 499 | { | ||
| 500 | struct wlp *wlp = container_of(pal, struct wlp, pal); | ||
| 501 | |||
| 502 | if (channel < 0) | ||
| 503 | netif_carrier_off(wlp->ndev); | ||
| 504 | else | ||
| 505 | netif_carrier_on(wlp->ndev); | ||
| 506 | } | ||
| 507 | |||
| 508 | int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev) | ||
| 509 | { | ||
| 510 | int result; | ||
| 511 | |||
| 512 | BUG_ON(wlp->fill_device_info == NULL); | ||
| 513 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 514 | BUG_ON(wlp->stop_queue == NULL); | ||
| 515 | BUG_ON(wlp->start_queue == NULL); | ||
| 516 | |||
| 517 | wlp->rc = rc; | ||
| 518 | wlp->ndev = ndev; | ||
| 519 | wlp_eda_init(&wlp->eda);/* Set up address cache */ | ||
| 520 | wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb; | ||
| 521 | wlp->uwb_notifs_handler.data = wlp; | ||
| 522 | uwb_notifs_register(rc, &wlp->uwb_notifs_handler); | ||
| 523 | |||
| 524 | uwb_pal_init(&wlp->pal); | ||
| 525 | wlp->pal.rc = rc; | ||
| 526 | wlp->pal.channel_changed = wlp_channel_changed; | ||
| 527 | result = uwb_pal_register(&wlp->pal); | ||
| 528 | if (result < 0) | ||
| 529 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); | ||
| 530 | |||
| 531 | return result; | ||
| 532 | } | ||
| 533 | EXPORT_SYMBOL_GPL(wlp_setup); | ||
| 534 | |||
| 535 | void wlp_remove(struct wlp *wlp) | ||
| 536 | { | ||
| 537 | wlp_neighbors_release(wlp); | ||
| 538 | uwb_pal_unregister(&wlp->pal); | ||
| 539 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); | ||
| 540 | wlp_eda_release(&wlp->eda); | ||
| 541 | mutex_lock(&wlp->mutex); | ||
| 542 | if (wlp->dev_info != NULL) | ||
| 543 | kfree(wlp->dev_info); | ||
| 544 | mutex_unlock(&wlp->mutex); | ||
| 545 | wlp->rc = NULL; | ||
| 546 | } | ||
| 547 | EXPORT_SYMBOL_GPL(wlp_remove); | ||
| 548 | |||
| 549 | /** | ||
| 550 | * wlp_reset_all - reset the WLP hardware | ||
| 551 | * @wlp: the WLP device to reset. | ||
| 552 | * | ||
| 553 | * This schedules a full hardware reset of the WLP device. The radio | ||
| 554 | * controller and any other PALs will also be reset. | ||
| 555 | */ | ||
| 556 | void wlp_reset_all(struct wlp *wlp) | ||
| 557 | { | ||
| 558 | uwb_rc_reset_all(wlp->rc); | ||
| 559 | } | ||
| 560 | EXPORT_SYMBOL_GPL(wlp_reset_all); | ||
diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c deleted file mode 100644 index 67872c83b679..000000000000 --- a/drivers/uwb/wlp/wss-lc.c +++ /dev/null | |||
| @@ -1,959 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007 Intel Corporation | ||
| 5 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License version | ||
| 9 | * 2 as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 19 | * 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * Implementation of the WLP association protocol. | ||
| 23 | * | ||
| 24 | * FIXME: Docs | ||
| 25 | * | ||
| 26 | * A UWB network interface will configure a WSS through wlp_wss_setup() after | ||
| 27 | * the interface has been assigned a MAC address, typically after | ||
| 28 | * "ifconfig" has been called. When the interface goes down it should call | ||
| 29 | * wlp_wss_remove(). | ||
| 30 | * | ||
| 31 | * When the WSS is ready for use the user interacts via sysfs to create, | ||
| 32 | * discover, and activate WSS. | ||
| 33 | * | ||
| 34 | * wlp_wss_enroll_activate() | ||
| 35 | * | ||
| 36 | * wlp_wss_create_activate() | ||
| 37 | * wlp_wss_set_wssid_hash() | ||
| 38 | * wlp_wss_comp_wssid_hash() | ||
| 39 | * wlp_wss_sel_bcast_addr() | ||
| 40 | * wlp_wss_sysfs_add() | ||
| 41 | * | ||
| 42 | * Called when no more references to WSS exist: | ||
| 43 | * wlp_wss_release() | ||
| 44 | * wlp_wss_reset() | ||
| 45 | */ | ||
| 46 | #include <linux/etherdevice.h> /* for is_valid_ether_addr */ | ||
| 47 | #include <linux/skbuff.h> | ||
| 48 | #include <linux/slab.h> | ||
| 49 | #include <linux/wlp.h> | ||
| 50 | |||
| 51 | #include "wlp-internal.h" | ||
| 52 | |||
| 53 | size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key) | ||
| 54 | { | ||
| 55 | size_t result; | ||
| 56 | |||
| 57 | result = scnprintf(buf, bufsize, | ||
| 58 | "%02x %02x %02x %02x %02x %02x " | ||
| 59 | "%02x %02x %02x %02x %02x %02x " | ||
| 60 | "%02x %02x %02x %02x", | ||
| 61 | key[0], key[1], key[2], key[3], | ||
| 62 | key[4], key[5], key[6], key[7], | ||
| 63 | key[8], key[9], key[10], key[11], | ||
| 64 | key[12], key[13], key[14], key[15]); | ||
| 65 | return result; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Compute WSSID hash | ||
| 70 | * WLP Draft 0.99 [7.2.1] | ||
| 71 | * | ||
| 72 | * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR | ||
| 73 | * of all octets in the WSSID. | ||
| 74 | */ | ||
| 75 | static | ||
| 76 | u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid) | ||
| 77 | { | ||
| 78 | return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2] | ||
| 79 | ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5] | ||
| 80 | ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8] | ||
| 81 | ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11] | ||
| 82 | ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14] | ||
| 83 | ^ wssid->data[15]; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Select a multicast EUI-48 for the WSS broadcast address. | ||
| 88 | * WLP Draft 0.99 [7.2.1] | ||
| 89 | * | ||
| 90 | * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP | ||
| 91 | * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive. | ||
| 92 | * | ||
| 93 | * This address is currently hardcoded. | ||
| 94 | * FIXME? | ||
| 95 | */ | ||
| 96 | static | ||
| 97 | struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss) | ||
| 98 | { | ||
| 99 | struct uwb_mac_addr bcast = { | ||
| 100 | .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 } | ||
| 101 | }; | ||
| 102 | return bcast; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Clear the contents of the WSS structure - all except kobj, mutex, virtual | ||
| 107 | * | ||
| 108 | * We do not want to reinitialize - the internal kobj should not change as | ||
| 109 | * it still points to the parent received during setup. The mutex should | ||
| 110 | * remain also. We thus just reset values individually. | ||
| 111 | * The virutal address assigned to WSS will remain the same for the | ||
| 112 | * lifetime of the WSS. We only reset the fields that can change during its | ||
| 113 | * lifetime. | ||
| 114 | */ | ||
| 115 | void wlp_wss_reset(struct wlp_wss *wss) | ||
| 116 | { | ||
| 117 | memset(&wss->wssid, 0, sizeof(wss->wssid)); | ||
| 118 | wss->hash = 0; | ||
| 119 | memset(&wss->name[0], 0, sizeof(wss->name)); | ||
| 120 | memset(&wss->bcast, 0, sizeof(wss->bcast)); | ||
| 121 | wss->secure_status = WLP_WSS_UNSECURE; | ||
| 122 | memset(&wss->master_key[0], 0, sizeof(wss->master_key)); | ||
| 123 | wss->tag = 0; | ||
| 124 | wss->state = WLP_WSS_STATE_NONE; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * Create sysfs infrastructure for WSS | ||
| 129 | * | ||
| 130 | * The WSS is configured to have the interface as parent (see wlp_wss_setup()) | ||
| 131 | * a new sysfs directory that includes wssid as its name is created in the | ||
| 132 | * interface's sysfs directory. The group of files interacting with WSS are | ||
| 133 | * created also. | ||
| 134 | */ | ||
| 135 | static | ||
| 136 | int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str) | ||
| 137 | { | ||
| 138 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 139 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 140 | int result; | ||
| 141 | |||
| 142 | result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str); | ||
| 143 | if (result < 0) | ||
| 144 | return result; | ||
| 145 | wss->kobj.ktype = &wss_ktype; | ||
| 146 | result = kobject_init_and_add(&wss->kobj, | ||
| 147 | &wss_ktype, wss->kobj.parent, "wlp"); | ||
| 148 | if (result < 0) { | ||
| 149 | dev_err(dev, "WLP: Cannot register WSS kobject.\n"); | ||
| 150 | goto error_kobject_register; | ||
| 151 | } | ||
| 152 | result = sysfs_create_group(&wss->kobj, &wss_attr_group); | ||
| 153 | if (result < 0) { | ||
| 154 | dev_err(dev, "WLP: Cannot register WSS attributes: %d\n", | ||
| 155 | result); | ||
| 156 | goto error_sysfs_create_group; | ||
| 157 | } | ||
| 158 | return 0; | ||
| 159 | error_sysfs_create_group: | ||
| 160 | |||
| 161 | kobject_put(&wss->kobj); /* will free name if needed */ | ||
| 162 | return result; | ||
| 163 | error_kobject_register: | ||
| 164 | kfree(wss->kobj.name); | ||
| 165 | wss->kobj.name = NULL; | ||
| 166 | wss->kobj.ktype = NULL; | ||
| 167 | return result; | ||
| 168 | } | ||
| 169 | |||
| 170 | |||
| 171 | /** | ||
| 172 | * Release WSS | ||
| 173 | * | ||
| 174 | * No more references exist to this WSS. We should undo everything that was | ||
| 175 | * done in wlp_wss_create_activate() except removing the group. The group | ||
| 176 | * is not removed because an object can be unregistered before the group is | ||
| 177 | * created. We also undo any additional operations on the WSS after this | ||
| 178 | * (addition of members). | ||
| 179 | * | ||
| 180 | * If memory was allocated for the kobject's name then it will | ||
| 181 | * be freed by the kobject system during this time. | ||
| 182 | * | ||
| 183 | * The EDA cache is removed and reinitialized when the WSS is removed. We | ||
| 184 | * thus loose knowledge of members of this WSS at that time and need not do | ||
| 185 | * it here. | ||
| 186 | */ | ||
| 187 | void wlp_wss_release(struct kobject *kobj) | ||
| 188 | { | ||
| 189 | struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj); | ||
| 190 | |||
| 191 | wlp_wss_reset(wss); | ||
| 192 | } | ||
| 193 | |||
| 194 | /** | ||
| 195 | * Enroll into a WSS using provided neighbor as registrar | ||
| 196 | * | ||
| 197 | * First search the neighborhood information to learn which neighbor is | ||
| 198 | * referred to, next proceed with enrollment. | ||
| 199 | * | ||
| 200 | * &wss->mutex is held | ||
| 201 | */ | ||
| 202 | static | ||
| 203 | int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid, | ||
| 204 | struct uwb_dev_addr *dest) | ||
| 205 | { | ||
| 206 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 207 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 208 | struct wlp_neighbor_e *neighbor; | ||
| 209 | int result = -ENXIO; | ||
| 210 | struct uwb_dev_addr *dev_addr; | ||
| 211 | |||
| 212 | mutex_lock(&wlp->nbmutex); | ||
| 213 | list_for_each_entry(neighbor, &wlp->neighbors, node) { | ||
| 214 | dev_addr = &neighbor->uwb_dev->dev_addr; | ||
| 215 | if (!memcmp(dest, dev_addr, sizeof(*dest))) { | ||
| 216 | result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid); | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | if (result == -ENXIO) | ||
| 221 | dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n", | ||
| 222 | dest->data[1], dest->data[0]); | ||
| 223 | mutex_unlock(&wlp->nbmutex); | ||
| 224 | return result; | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * Enroll into a WSS previously discovered | ||
| 229 | * | ||
| 230 | * User provides WSSID of WSS, search for neighbor that has this WSS | ||
| 231 | * activated and attempt to enroll. | ||
| 232 | * | ||
| 233 | * &wss->mutex is held | ||
| 234 | */ | ||
| 235 | static | ||
| 236 | int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid) | ||
| 237 | { | ||
| 238 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 239 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 240 | struct wlp_neighbor_e *neighbor; | ||
| 241 | struct wlp_wssid_e *wssid_e; | ||
| 242 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 243 | int result = -ENXIO; | ||
| 244 | |||
| 245 | |||
| 246 | mutex_lock(&wlp->nbmutex); | ||
| 247 | list_for_each_entry(neighbor, &wlp->neighbors, node) { | ||
| 248 | list_for_each_entry(wssid_e, &neighbor->wssid, node) { | ||
| 249 | if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) { | ||
| 250 | result = wlp_enroll_neighbor(wlp, neighbor, | ||
| 251 | wss, wssid); | ||
| 252 | if (result == 0) /* enrollment success */ | ||
| 253 | goto out; | ||
| 254 | break; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | out: | ||
| 259 | if (result == -ENXIO) { | ||
| 260 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | ||
| 261 | dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf); | ||
| 262 | } | ||
| 263 | mutex_unlock(&wlp->nbmutex); | ||
| 264 | return result; | ||
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * Enroll into WSS with provided WSSID, registrar may be provided | ||
| 269 | * | ||
| 270 | * @wss: out WSS that will be enrolled | ||
| 271 | * @wssid: wssid of neighboring WSS that we want to enroll in | ||
| 272 | * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any | ||
| 273 | * neighbor can be used as registrar. | ||
| 274 | * | ||
| 275 | * &wss->mutex is held | ||
| 276 | */ | ||
| 277 | static | ||
| 278 | int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid, | ||
| 279 | struct uwb_dev_addr *devaddr) | ||
| 280 | { | ||
| 281 | int result; | ||
| 282 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 283 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 284 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 285 | struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; | ||
| 286 | |||
| 287 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | ||
| 288 | |||
| 289 | if (wss->state != WLP_WSS_STATE_NONE) { | ||
| 290 | dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf); | ||
| 291 | result = -EEXIST; | ||
| 292 | goto error; | ||
| 293 | } | ||
| 294 | if (!memcmp(&bcast, devaddr, sizeof(bcast))) | ||
| 295 | result = wlp_wss_enroll_discovered(wss, wssid); | ||
| 296 | else | ||
| 297 | result = wlp_wss_enroll_target(wss, wssid, devaddr); | ||
| 298 | if (result < 0) { | ||
| 299 | dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n", | ||
| 300 | buf, result); | ||
| 301 | goto error; | ||
| 302 | } | ||
| 303 | dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf); | ||
| 304 | result = wlp_wss_sysfs_add(wss, buf); | ||
| 305 | if (result < 0) { | ||
| 306 | dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n"); | ||
| 307 | wlp_wss_reset(wss); | ||
| 308 | } | ||
| 309 | error: | ||
| 310 | return result; | ||
| 311 | |||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * Activate given WSS | ||
| 316 | * | ||
| 317 | * Prior to activation a WSS must be enrolled. To activate a WSS a device | ||
| 318 | * includes the WSS hash in the WLP IE in its beacon in each superframe. | ||
| 319 | * WLP 0.99 [7.2.5]. | ||
| 320 | * | ||
| 321 | * The WSS tag is also computed at this time. We only support one activated | ||
| 322 | * WSS so we can use the hash as a tag - there will never be a conflict. | ||
| 323 | * | ||
| 324 | * We currently only support one activated WSS so only one WSS hash is | ||
| 325 | * included in the WLP IE. | ||
| 326 | */ | ||
| 327 | static | ||
| 328 | int wlp_wss_activate(struct wlp_wss *wss) | ||
| 329 | { | ||
| 330 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 331 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 332 | struct uwb_rc *uwb_rc = wlp->rc; | ||
| 333 | int result; | ||
| 334 | struct { | ||
| 335 | struct wlp_ie wlp_ie; | ||
| 336 | u8 hash; /* only include one hash */ | ||
| 337 | } ie_data; | ||
| 338 | |||
| 339 | BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED); | ||
| 340 | wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid); | ||
| 341 | wss->tag = wss->hash; | ||
| 342 | memset(&ie_data, 0, sizeof(ie_data)); | ||
| 343 | ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP; | ||
| 344 | ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr); | ||
| 345 | wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash)); | ||
| 346 | ie_data.hash = wss->hash; | ||
| 347 | result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr, | ||
| 348 | sizeof(ie_data)); | ||
| 349 | if (result < 0) { | ||
| 350 | dev_err(dev, "WLP: Unable to add WLP IE to beacon. " | ||
| 351 | "result = %d.\n", result); | ||
| 352 | goto error_wlp_ie; | ||
| 353 | } | ||
| 354 | wss->state = WLP_WSS_STATE_ACTIVE; | ||
| 355 | result = 0; | ||
| 356 | error_wlp_ie: | ||
| 357 | return result; | ||
| 358 | } | ||
| 359 | |||
| 360 | /** | ||
| 361 | * Enroll in and activate WSS identified by provided WSSID | ||
| 362 | * | ||
| 363 | * The neighborhood cache should contain a list of all neighbors and the | ||
| 364 | * WSS they have activated. Based on that cache we search which neighbor we | ||
| 365 | * can perform the association process with. The user also has option to | ||
| 366 | * specify which neighbor it prefers as registrar. | ||
| 367 | * Successful enrollment is followed by activation. | ||
| 368 | * Successful activation will create the sysfs directory containing | ||
| 369 | * specific information regarding this WSS. | ||
| 370 | */ | ||
| 371 | int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, | ||
| 372 | struct uwb_dev_addr *devaddr) | ||
| 373 | { | ||
| 374 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 375 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 376 | int result = 0; | ||
| 377 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 378 | |||
| 379 | mutex_lock(&wss->mutex); | ||
| 380 | result = wlp_wss_enroll(wss, wssid, devaddr); | ||
| 381 | if (result < 0) { | ||
| 382 | wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid); | ||
| 383 | dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf); | ||
| 384 | goto error_enroll; | ||
| 385 | } | ||
| 386 | result = wlp_wss_activate(wss); | ||
| 387 | if (result < 0) { | ||
| 388 | dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment " | ||
| 389 | "result = %d \n", result); | ||
| 390 | /* Undo enrollment */ | ||
| 391 | wlp_wss_reset(wss); | ||
| 392 | goto error_activate; | ||
| 393 | } | ||
| 394 | error_activate: | ||
| 395 | error_enroll: | ||
| 396 | mutex_unlock(&wss->mutex); | ||
| 397 | return result; | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * Create, enroll, and activate a new WSS | ||
| 402 | * | ||
| 403 | * @wssid: new wssid provided by user | ||
| 404 | * @name: WSS name requested by used. | ||
| 405 | * @sec_status: security status requested by user | ||
| 406 | * | ||
| 407 | * A user requested the creation of a new WSS. All operations are done | ||
| 408 | * locally. The new WSS will be stored locally, the hash will be included | ||
| 409 | * in the WLP IE, and the sysfs infrastructure for this WSS will be | ||
| 410 | * created. | ||
| 411 | */ | ||
| 412 | int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, | ||
| 413 | char *name, unsigned sec_status, unsigned accept) | ||
| 414 | { | ||
| 415 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 416 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 417 | int result = 0; | ||
| 418 | char buf[WLP_WSS_UUID_STRSIZE]; | ||
| 419 | |||
| 420 | result = wlp_wss_uuid_print(buf, sizeof(buf), wssid); | ||
| 421 | |||
| 422 | if (!mutex_trylock(&wss->mutex)) { | ||
| 423 | dev_err(dev, "WLP: WLP association session in progress.\n"); | ||
| 424 | return -EBUSY; | ||
| 425 | } | ||
| 426 | if (wss->state != WLP_WSS_STATE_NONE) { | ||
| 427 | dev_err(dev, "WLP: WSS already exists. Not creating new.\n"); | ||
| 428 | result = -EEXIST; | ||
| 429 | goto out; | ||
| 430 | } | ||
| 431 | if (wss->kobj.parent == NULL) { | ||
| 432 | dev_err(dev, "WLP: WSS parent not ready. Is network interface " | ||
| 433 | "up?\n"); | ||
| 434 | result = -ENXIO; | ||
| 435 | goto out; | ||
| 436 | } | ||
| 437 | if (sec_status == WLP_WSS_SECURE) { | ||
| 438 | dev_err(dev, "WLP: FIXME Creation of secure WSS not " | ||
| 439 | "supported yet.\n"); | ||
| 440 | result = -EINVAL; | ||
| 441 | goto out; | ||
| 442 | } | ||
| 443 | wss->wssid = *wssid; | ||
| 444 | memcpy(wss->name, name, sizeof(wss->name)); | ||
| 445 | wss->bcast = wlp_wss_sel_bcast_addr(wss); | ||
| 446 | wss->secure_status = sec_status; | ||
| 447 | wss->accept_enroll = accept; | ||
| 448 | /*wss->virtual_addr is initialized in call to wlp_wss_setup*/ | ||
| 449 | /* sysfs infrastructure */ | ||
| 450 | result = wlp_wss_sysfs_add(wss, buf); | ||
| 451 | if (result < 0) { | ||
| 452 | dev_err(dev, "Cannot set up sysfs for WSS kobject.\n"); | ||
| 453 | wlp_wss_reset(wss); | ||
| 454 | goto out; | ||
| 455 | } else | ||
| 456 | result = 0; | ||
| 457 | wss->state = WLP_WSS_STATE_ENROLLED; | ||
| 458 | result = wlp_wss_activate(wss); | ||
| 459 | if (result < 0) { | ||
| 460 | dev_err(dev, "WLP: Unable to activate WSS. Undoing " | ||
| 461 | "enrollment\n"); | ||
| 462 | wlp_wss_reset(wss); | ||
| 463 | goto out; | ||
| 464 | } | ||
| 465 | result = 0; | ||
| 466 | out: | ||
| 467 | mutex_unlock(&wss->mutex); | ||
| 468 | return result; | ||
| 469 | } | ||
| 470 | |||
| 471 | /** | ||
| 472 | * Determine if neighbor has WSS activated | ||
| 473 | * | ||
| 474 | * @returns: 1 if neighbor has WSS activated, zero otherwise | ||
| 475 | * | ||
| 476 | * This can be done in two ways: | ||
| 477 | * - send a C1 frame, parse C2/F0 response | ||
| 478 | * - examine the WLP IE sent by the neighbor | ||
| 479 | * | ||
| 480 | * The WLP IE is not fully supported in hardware so we use the C1/C2 frame | ||
| 481 | * exchange to determine if a WSS is activated. Using the WLP IE should be | ||
| 482 | * faster and should be used when it becomes possible. | ||
| 483 | */ | ||
| 484 | int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss, | ||
| 485 | struct uwb_dev_addr *dev_addr) | ||
| 486 | { | ||
| 487 | int result = 0; | ||
| 488 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 489 | DECLARE_COMPLETION_ONSTACK(completion); | ||
| 490 | struct wlp_session session; | ||
| 491 | struct sk_buff *skb; | ||
| 492 | struct wlp_frame_assoc *resp; | ||
| 493 | struct wlp_uuid wssid; | ||
| 494 | |||
| 495 | mutex_lock(&wlp->mutex); | ||
| 496 | /* Send C1 association frame */ | ||
| 497 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1); | ||
| 498 | if (result < 0) { | ||
| 499 | dev_err(dev, "Unable to send C1 frame to neighbor " | ||
| 500 | "%02x:%02x (%d)\n", dev_addr->data[1], | ||
| 501 | dev_addr->data[0], result); | ||
| 502 | result = 0; | ||
| 503 | goto out; | ||
| 504 | } | ||
| 505 | /* Create session, wait for response */ | ||
| 506 | session.exp_message = WLP_ASSOC_C2; | ||
| 507 | session.cb = wlp_session_cb; | ||
| 508 | session.cb_priv = &completion; | ||
| 509 | session.neighbor_addr = *dev_addr; | ||
| 510 | BUG_ON(wlp->session != NULL); | ||
| 511 | wlp->session = &session; | ||
| 512 | /* Wait for C2/F0 frame */ | ||
| 513 | result = wait_for_completion_interruptible_timeout(&completion, | ||
| 514 | WLP_PER_MSG_TIMEOUT * HZ); | ||
| 515 | if (result == 0) { | ||
| 516 | dev_err(dev, "Timeout while sending C1 to neighbor " | ||
| 517 | "%02x:%02x.\n", dev_addr->data[1], | ||
| 518 | dev_addr->data[0]); | ||
| 519 | goto out; | ||
| 520 | } | ||
| 521 | if (result < 0) { | ||
| 522 | dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n", | ||
| 523 | dev_addr->data[1], dev_addr->data[0]); | ||
| 524 | result = 0; | ||
| 525 | goto out; | ||
| 526 | } | ||
| 527 | /* Parse message in session->data: it will be either C2 or F0 */ | ||
| 528 | skb = session.data; | ||
| 529 | resp = (void *) skb->data; | ||
| 530 | if (resp->type == WLP_ASSOC_F0) { | ||
| 531 | result = wlp_parse_f0(wlp, skb); | ||
| 532 | if (result < 0) | ||
| 533 | dev_err(dev, "WLP: unable to parse incoming F0 " | ||
| 534 | "frame from neighbor %02x:%02x.\n", | ||
| 535 | dev_addr->data[1], dev_addr->data[0]); | ||
| 536 | result = 0; | ||
| 537 | goto error_resp_parse; | ||
| 538 | } | ||
| 539 | /* WLP version and message type fields have already been parsed */ | ||
| 540 | result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid, | ||
| 541 | skb->len - sizeof(*resp)); | ||
| 542 | if (result < 0) { | ||
| 543 | dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n"); | ||
| 544 | result = 0; | ||
| 545 | goto error_resp_parse; | ||
| 546 | } | ||
| 547 | if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) | ||
| 548 | result = 1; | ||
| 549 | else { | ||
| 550 | dev_err(dev, "WLP: Received a C2 frame without matching " | ||
| 551 | "WSSID.\n"); | ||
| 552 | result = 0; | ||
| 553 | } | ||
| 554 | error_resp_parse: | ||
| 555 | kfree_skb(skb); | ||
| 556 | out: | ||
| 557 | wlp->session = NULL; | ||
| 558 | mutex_unlock(&wlp->mutex); | ||
| 559 | return result; | ||
| 560 | } | ||
| 561 | |||
| 562 | /** | ||
| 563 | * Activate connection with neighbor by updating EDA cache | ||
| 564 | * | ||
| 565 | * @wss: local WSS to which neighbor wants to connect | ||
| 566 | * @dev_addr: neighbor's address | ||
| 567 | * @wssid: neighbor's WSSID - must be same as our WSS's WSSID | ||
| 568 | * @tag: neighbor's WSS tag used to identify frames transmitted by it | ||
| 569 | * @virt_addr: neighbor's virtual EUI-48 | ||
| 570 | */ | ||
| 571 | static | ||
| 572 | int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss, | ||
| 573 | struct uwb_dev_addr *dev_addr, | ||
| 574 | struct wlp_uuid *wssid, u8 *tag, | ||
| 575 | struct uwb_mac_addr *virt_addr) | ||
| 576 | { | ||
| 577 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 578 | int result = 0; | ||
| 579 | |||
| 580 | if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) { | ||
| 581 | /* Update EDA cache */ | ||
| 582 | result = wlp_eda_update_node(&wlp->eda, dev_addr, wss, | ||
| 583 | (void *) virt_addr->data, *tag, | ||
| 584 | WLP_WSS_CONNECTED); | ||
| 585 | if (result < 0) | ||
| 586 | dev_err(dev, "WLP: Unable to update EDA cache " | ||
| 587 | "with new connected neighbor information.\n"); | ||
| 588 | } else { | ||
| 589 | dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n"); | ||
| 590 | result = -EINVAL; | ||
| 591 | } | ||
| 592 | return result; | ||
| 593 | } | ||
| 594 | |||
| 595 | /** | ||
| 596 | * Connect to WSS neighbor | ||
| 597 | * | ||
| 598 | * Use C3/C4 exchange to determine if neighbor has WSS activated and | ||
| 599 | * retrieve the WSS tag and virtual EUI-48 of the neighbor. | ||
| 600 | */ | ||
| 601 | static | ||
| 602 | int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss, | ||
| 603 | struct uwb_dev_addr *dev_addr) | ||
| 604 | { | ||
| 605 | int result; | ||
| 606 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 607 | struct wlp_uuid wssid; | ||
| 608 | u8 tag; | ||
| 609 | struct uwb_mac_addr virt_addr; | ||
| 610 | DECLARE_COMPLETION_ONSTACK(completion); | ||
| 611 | struct wlp_session session; | ||
| 612 | struct wlp_frame_assoc *resp; | ||
| 613 | struct sk_buff *skb; | ||
| 614 | |||
| 615 | mutex_lock(&wlp->mutex); | ||
| 616 | /* Send C3 association frame */ | ||
| 617 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3); | ||
| 618 | if (result < 0) { | ||
| 619 | dev_err(dev, "Unable to send C3 frame to neighbor " | ||
| 620 | "%02x:%02x (%d)\n", dev_addr->data[1], | ||
| 621 | dev_addr->data[0], result); | ||
| 622 | goto out; | ||
| 623 | } | ||
| 624 | /* Create session, wait for response */ | ||
| 625 | session.exp_message = WLP_ASSOC_C4; | ||
| 626 | session.cb = wlp_session_cb; | ||
| 627 | session.cb_priv = &completion; | ||
| 628 | session.neighbor_addr = *dev_addr; | ||
| 629 | BUG_ON(wlp->session != NULL); | ||
| 630 | wlp->session = &session; | ||
| 631 | /* Wait for C4/F0 frame */ | ||
| 632 | result = wait_for_completion_interruptible_timeout(&completion, | ||
| 633 | WLP_PER_MSG_TIMEOUT * HZ); | ||
| 634 | if (result == 0) { | ||
| 635 | dev_err(dev, "Timeout while sending C3 to neighbor " | ||
| 636 | "%02x:%02x.\n", dev_addr->data[1], | ||
| 637 | dev_addr->data[0]); | ||
| 638 | result = -ETIMEDOUT; | ||
| 639 | goto out; | ||
| 640 | } | ||
| 641 | if (result < 0) { | ||
| 642 | dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n", | ||
| 643 | dev_addr->data[1], dev_addr->data[0]); | ||
| 644 | goto out; | ||
| 645 | } | ||
| 646 | /* Parse message in session->data: it will be either C4 or F0 */ | ||
| 647 | skb = session.data; | ||
| 648 | resp = (void *) skb->data; | ||
| 649 | if (resp->type == WLP_ASSOC_F0) { | ||
| 650 | result = wlp_parse_f0(wlp, skb); | ||
| 651 | if (result < 0) | ||
| 652 | dev_err(dev, "WLP: unable to parse incoming F0 " | ||
| 653 | "frame from neighbor %02x:%02x.\n", | ||
| 654 | dev_addr->data[1], dev_addr->data[0]); | ||
| 655 | result = -EINVAL; | ||
| 656 | goto error_resp_parse; | ||
| 657 | } | ||
| 658 | result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); | ||
| 659 | if (result < 0) { | ||
| 660 | dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n"); | ||
| 661 | goto error_resp_parse; | ||
| 662 | } | ||
| 663 | result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag, | ||
| 664 | &virt_addr); | ||
| 665 | if (result < 0) { | ||
| 666 | dev_err(dev, "WLP: Unable to activate connection to " | ||
| 667 | "neighbor %02x:%02x.\n", dev_addr->data[1], | ||
| 668 | dev_addr->data[0]); | ||
| 669 | goto error_resp_parse; | ||
| 670 | } | ||
| 671 | error_resp_parse: | ||
| 672 | kfree_skb(skb); | ||
| 673 | out: | ||
| 674 | /* Record that we unsuccessfully tried to connect to this neighbor */ | ||
| 675 | if (result < 0) | ||
| 676 | wlp_eda_update_node_state(&wlp->eda, dev_addr, | ||
| 677 | WLP_WSS_CONNECT_FAILED); | ||
| 678 | wlp->session = NULL; | ||
| 679 | mutex_unlock(&wlp->mutex); | ||
| 680 | return result; | ||
| 681 | } | ||
| 682 | |||
| 683 | /** | ||
| 684 | * Connect to neighbor with common WSS, send pending frame | ||
| 685 | * | ||
| 686 | * This function is scheduled when a frame is destined to a neighbor with | ||
| 687 | * which we do not have a connection. A copy of the EDA cache entry is | ||
| 688 | * provided - not the actual cache entry (because it is protected by a | ||
| 689 | * spinlock). | ||
| 690 | * | ||
| 691 | * First determine if neighbor has the same WSS activated, connect if it | ||
| 692 | * does. The C3/C4 exchange is dual purpose to determine if neighbor has | ||
| 693 | * WSS activated and proceed with the connection. | ||
| 694 | * | ||
| 695 | * The frame that triggered the connection setup is sent after connection | ||
| 696 | * setup. | ||
| 697 | * | ||
| 698 | * network queue is stopped - we need to restart when done | ||
| 699 | * | ||
| 700 | */ | ||
| 701 | static | ||
| 702 | void wlp_wss_connect_send(struct work_struct *ws) | ||
| 703 | { | ||
| 704 | struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws, | ||
| 705 | struct wlp_assoc_conn_ctx, | ||
| 706 | ws); | ||
| 707 | struct wlp *wlp = conn_ctx->wlp; | ||
| 708 | struct sk_buff *skb = conn_ctx->skb; | ||
| 709 | struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry; | ||
| 710 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | ||
| 711 | struct wlp_wss *wss = &wlp->wss; | ||
| 712 | int result; | ||
| 713 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 714 | |||
| 715 | mutex_lock(&wss->mutex); | ||
| 716 | if (wss->state < WLP_WSS_STATE_ACTIVE) { | ||
| 717 | if (printk_ratelimit()) | ||
| 718 | dev_err(dev, "WLP: Attempting to connect with " | ||
| 719 | "WSS that is not active or connected.\n"); | ||
| 720 | dev_kfree_skb(skb); | ||
| 721 | goto out; | ||
| 722 | } | ||
| 723 | /* Establish connection - send C3 rcv C4 */ | ||
| 724 | result = wlp_wss_connect_neighbor(wlp, wss, dev_addr); | ||
| 725 | if (result < 0) { | ||
| 726 | if (printk_ratelimit()) | ||
| 727 | dev_err(dev, "WLP: Unable to establish connection " | ||
| 728 | "with neighbor %02x:%02x.\n", | ||
| 729 | dev_addr->data[1], dev_addr->data[0]); | ||
| 730 | dev_kfree_skb(skb); | ||
| 731 | goto out; | ||
| 732 | } | ||
| 733 | /* EDA entry changed, update the local copy being used */ | ||
| 734 | result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry); | ||
| 735 | if (result < 0) { | ||
| 736 | if (printk_ratelimit()) | ||
| 737 | dev_err(dev, "WLP: Cannot find EDA entry for " | ||
| 738 | "neighbor %02x:%02x \n", | ||
| 739 | dev_addr->data[1], dev_addr->data[0]); | ||
| 740 | } | ||
| 741 | result = wlp_wss_prep_hdr(wlp, eda_entry, skb); | ||
| 742 | if (result < 0) { | ||
| 743 | if (printk_ratelimit()) | ||
| 744 | dev_err(dev, "WLP: Unable to prepare frame header for " | ||
| 745 | "transmission (neighbor %02x:%02x). \n", | ||
| 746 | dev_addr->data[1], dev_addr->data[0]); | ||
| 747 | dev_kfree_skb(skb); | ||
| 748 | goto out; | ||
| 749 | } | ||
| 750 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 751 | result = wlp->xmit_frame(wlp, skb, dev_addr); | ||
| 752 | if (result < 0) { | ||
| 753 | if (printk_ratelimit()) | ||
| 754 | dev_err(dev, "WLP: Unable to transmit frame: %d\n", | ||
| 755 | result); | ||
| 756 | if (result == -ENXIO) | ||
| 757 | dev_err(dev, "WLP: Is network interface up? \n"); | ||
| 758 | /* We could try again ... */ | ||
| 759 | dev_kfree_skb(skb);/*we need to free if tx fails */ | ||
| 760 | } | ||
| 761 | out: | ||
| 762 | kfree(conn_ctx); | ||
| 763 | BUG_ON(wlp->start_queue == NULL); | ||
| 764 | wlp->start_queue(wlp); | ||
| 765 | mutex_unlock(&wss->mutex); | ||
| 766 | } | ||
| 767 | |||
| 768 | /** | ||
| 769 | * Add WLP header to outgoing skb | ||
| 770 | * | ||
| 771 | * @eda_entry: pointer to neighbor's entry in the EDA cache | ||
| 772 | * @_skb: skb containing data destined to the neighbor | ||
| 773 | */ | ||
| 774 | int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry, | ||
| 775 | void *_skb) | ||
| 776 | { | ||
| 777 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 778 | int result = 0; | ||
| 779 | unsigned char *eth_addr = eda_entry->eth_addr; | ||
| 780 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | ||
| 781 | struct sk_buff *skb = _skb; | ||
| 782 | struct wlp_frame_std_abbrv_hdr *std_hdr; | ||
| 783 | |||
| 784 | if (eda_entry->state == WLP_WSS_CONNECTED) { | ||
| 785 | /* Add WLP header */ | ||
| 786 | BUG_ON(skb_headroom(skb) < sizeof(*std_hdr)); | ||
| 787 | std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr)); | ||
| 788 | std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | ||
| 789 | std_hdr->hdr.type = WLP_FRAME_STANDARD; | ||
| 790 | std_hdr->tag = eda_entry->wss->tag; | ||
| 791 | } else { | ||
| 792 | if (printk_ratelimit()) | ||
| 793 | dev_err(dev, "WLP: Destination neighbor (Ethernet: " | ||
| 794 | "%pM, Dev: %02x:%02x) is not connected.\n", | ||
| 795 | eth_addr, dev_addr->data[1], dev_addr->data[0]); | ||
| 796 | result = -EINVAL; | ||
| 797 | } | ||
| 798 | return result; | ||
| 799 | } | ||
| 800 | |||
| 801 | |||
| 802 | /** | ||
| 803 | * Prepare skb for neighbor: connect if not already and prep WLP header | ||
| 804 | * | ||
| 805 | * This function is called in interrupt context, but it needs to sleep. We | ||
| 806 | * temporarily stop the net queue to establish the WLP connection. | ||
| 807 | * Setup of the WLP connection and restart of queue is scheduled | ||
| 808 | * on the default work queue. | ||
| 809 | * | ||
| 810 | * run with eda->lock held (spinlock) | ||
| 811 | */ | ||
| 812 | int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry, | ||
| 813 | void *_skb) | ||
| 814 | { | ||
| 815 | int result = 0; | ||
| 816 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 817 | struct sk_buff *skb = _skb; | ||
| 818 | struct wlp_assoc_conn_ctx *conn_ctx; | ||
| 819 | |||
| 820 | if (eda_entry->state == WLP_WSS_UNCONNECTED) { | ||
| 821 | /* We don't want any more packets while we set up connection */ | ||
| 822 | BUG_ON(wlp->stop_queue == NULL); | ||
| 823 | wlp->stop_queue(wlp); | ||
| 824 | conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC); | ||
| 825 | if (conn_ctx == NULL) { | ||
| 826 | if (printk_ratelimit()) | ||
| 827 | dev_err(dev, "WLP: Unable to allocate memory " | ||
| 828 | "for connection handling.\n"); | ||
| 829 | result = -ENOMEM; | ||
| 830 | goto out; | ||
| 831 | } | ||
| 832 | conn_ctx->wlp = wlp; | ||
| 833 | conn_ctx->skb = skb; | ||
| 834 | conn_ctx->eda_entry = *eda_entry; | ||
| 835 | INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send); | ||
| 836 | schedule_work(&conn_ctx->ws); | ||
| 837 | result = 1; | ||
| 838 | } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) { | ||
| 839 | /* Previous connection attempts failed, don't retry - see | ||
| 840 | * conditions for connection in WLP 0.99 [7.6.2] */ | ||
| 841 | if (printk_ratelimit()) | ||
| 842 | dev_err(dev, "Could not connect to neighbor " | ||
| 843 | "previously. Not retrying. \n"); | ||
| 844 | result = -ENONET; | ||
| 845 | goto out; | ||
| 846 | } else /* eda_entry->state == WLP_WSS_CONNECTED */ | ||
| 847 | result = wlp_wss_prep_hdr(wlp, eda_entry, skb); | ||
| 848 | out: | ||
| 849 | return result; | ||
| 850 | } | ||
| 851 | |||
| 852 | /** | ||
| 853 | * Emulate broadcast: copy skb, send copy to neighbor (connect if not already) | ||
| 854 | * | ||
| 855 | * We need to copy skbs in the case where we emulate broadcast through | ||
| 856 | * unicast. We copy instead of clone because we are modifying the data of | ||
| 857 | * the frame after copying ... clones share data so we cannot emulate | ||
| 858 | * broadcast using clones. | ||
| 859 | * | ||
| 860 | * run with eda->lock held (spinlock) | ||
| 861 | */ | ||
| 862 | int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry, | ||
| 863 | void *_skb) | ||
| 864 | { | ||
| 865 | int result = -ENOMEM; | ||
| 866 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 867 | struct sk_buff *skb = _skb; | ||
| 868 | struct sk_buff *copy; | ||
| 869 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | ||
| 870 | |||
| 871 | copy = skb_copy(skb, GFP_ATOMIC); | ||
| 872 | if (copy == NULL) { | ||
| 873 | if (printk_ratelimit()) | ||
| 874 | dev_err(dev, "WLP: Unable to copy skb for " | ||
| 875 | "transmission.\n"); | ||
| 876 | goto out; | ||
| 877 | } | ||
| 878 | result = wlp_wss_connect_prep(wlp, eda_entry, copy); | ||
| 879 | if (result < 0) { | ||
| 880 | if (printk_ratelimit()) | ||
| 881 | dev_err(dev, "WLP: Unable to connect/send skb " | ||
| 882 | "to neighbor.\n"); | ||
| 883 | dev_kfree_skb_irq(copy); | ||
| 884 | goto out; | ||
| 885 | } else if (result == 1) | ||
| 886 | /* Frame will be transmitted separately */ | ||
| 887 | goto out; | ||
| 888 | BUG_ON(wlp->xmit_frame == NULL); | ||
| 889 | result = wlp->xmit_frame(wlp, copy, dev_addr); | ||
| 890 | if (result < 0) { | ||
| 891 | if (printk_ratelimit()) | ||
| 892 | dev_err(dev, "WLP: Unable to transmit frame: %d\n", | ||
| 893 | result); | ||
| 894 | if ((result == -ENXIO) && printk_ratelimit()) | ||
| 895 | dev_err(dev, "WLP: Is network interface up? \n"); | ||
| 896 | /* We could try again ... */ | ||
| 897 | dev_kfree_skb_irq(copy);/*we need to free if tx fails */ | ||
| 898 | } | ||
| 899 | out: | ||
| 900 | return result; | ||
| 901 | } | ||
| 902 | |||
| 903 | |||
| 904 | /** | ||
| 905 | * Setup WSS | ||
| 906 | * | ||
| 907 | * Should be called by network driver after the interface has been given a | ||
| 908 | * MAC address. | ||
| 909 | */ | ||
| 910 | int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss) | ||
| 911 | { | ||
| 912 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 913 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
| 914 | int result = 0; | ||
| 915 | |||
| 916 | mutex_lock(&wss->mutex); | ||
| 917 | wss->kobj.parent = &net_dev->dev.kobj; | ||
| 918 | if (!is_valid_ether_addr(net_dev->dev_addr)) { | ||
| 919 | dev_err(dev, "WLP: Invalid MAC address. Cannot use for" | ||
| 920 | "virtual.\n"); | ||
| 921 | result = -EINVAL; | ||
| 922 | goto out; | ||
| 923 | } | ||
| 924 | memcpy(wss->virtual_addr.data, net_dev->dev_addr, | ||
| 925 | sizeof(wss->virtual_addr.data)); | ||
| 926 | out: | ||
| 927 | mutex_unlock(&wss->mutex); | ||
| 928 | return result; | ||
| 929 | } | ||
| 930 | EXPORT_SYMBOL_GPL(wlp_wss_setup); | ||
| 931 | |||
| 932 | /** | ||
| 933 | * Remove WSS | ||
| 934 | * | ||
| 935 | * Called by client that configured WSS through wlp_wss_setup(). This | ||
| 936 | * function is called when client no longer needs WSS, eg. client shuts | ||
| 937 | * down. | ||
| 938 | * | ||
| 939 | * We remove the WLP IE from the beacon before initiating local cleanup. | ||
| 940 | */ | ||
| 941 | void wlp_wss_remove(struct wlp_wss *wss) | ||
| 942 | { | ||
| 943 | struct wlp *wlp = container_of(wss, struct wlp, wss); | ||
| 944 | |||
| 945 | mutex_lock(&wss->mutex); | ||
| 946 | if (wss->state == WLP_WSS_STATE_ACTIVE) | ||
| 947 | uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP); | ||
| 948 | if (wss->state != WLP_WSS_STATE_NONE) { | ||
| 949 | sysfs_remove_group(&wss->kobj, &wss_attr_group); | ||
| 950 | kobject_put(&wss->kobj); | ||
| 951 | } | ||
| 952 | wss->kobj.parent = NULL; | ||
| 953 | memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr)); | ||
| 954 | /* Cleanup EDA cache */ | ||
| 955 | wlp_eda_release(&wlp->eda); | ||
| 956 | wlp_eda_init(&wlp->eda); | ||
| 957 | mutex_unlock(&wss->mutex); | ||
| 958 | } | ||
| 959 | EXPORT_SYMBOL_GPL(wlp_wss_remove); | ||
diff --git a/include/linux/wlp.h b/include/linux/wlp.h deleted file mode 100644 index c76fe2392506..000000000000 --- a/include/linux/wlp.h +++ /dev/null | |||
| @@ -1,736 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * WiMedia Logical Link Control Protocol (WLP) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2006 Intel Corporation | ||
| 5 | * Reinette Chatre <reinette.chatre@intel.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License version | ||
| 9 | * 2 as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 19 | * 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * FIXME: docs | ||
| 23 | * | ||
| 24 | * - Does not (yet) include support for WLP control frames | ||
| 25 | * WLP Draft 0.99 [6.5]. | ||
| 26 | * | ||
| 27 | * A visual representation of the data structures. | ||
| 28 | * | ||
| 29 | * wssidB wssidB | ||
| 30 | * ^ ^ | ||
| 31 | * | | | ||
| 32 | * wssidA wssidA | ||
| 33 | * wlp interface { ^ ^ | ||
| 34 | * ... | | | ||
| 35 | * ... ... wssid wssid ... | ||
| 36 | * wlp --- ... | | | ||
| 37 | * }; neighbors --> neighbA --> neighbB | ||
| 38 | * ... | ||
| 39 | * wss | ||
| 40 | * ... | ||
| 41 | * eda cache --> neighborA --> neighborB --> neighborC ... | ||
| 42 | */ | ||
| 43 | |||
| 44 | #ifndef __LINUX__WLP_H_ | ||
| 45 | #define __LINUX__WLP_H_ | ||
| 46 | |||
| 47 | #include <linux/netdevice.h> | ||
| 48 | #include <linux/skbuff.h> | ||
| 49 | #include <linux/list.h> | ||
| 50 | #include <linux/uwb.h> | ||
| 51 | |||
| 52 | /** | ||
| 53 | * WLP Protocol ID | ||
| 54 | * WLP Draft 0.99 [6.2] | ||
| 55 | * | ||
| 56 | * The MUX header for all WLP frames | ||
| 57 | */ | ||
| 58 | #define WLP_PROTOCOL_ID 0x0100 | ||
| 59 | |||
| 60 | /** | ||
| 61 | * WLP Version | ||
| 62 | * WLP version placed in the association frames (WLP 0.99 [6.6]) | ||
| 63 | */ | ||
| 64 | #define WLP_VERSION 0x10 | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Bytes needed to print UUID as string | ||
| 68 | */ | ||
| 69 | #define WLP_WSS_UUID_STRSIZE 48 | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Bytes needed to print nonce as string | ||
| 73 | */ | ||
| 74 | #define WLP_WSS_NONCE_STRSIZE 48 | ||
| 75 | |||
| 76 | |||
| 77 | /** | ||
| 78 | * Size used for WLP name size | ||
| 79 | * | ||
| 80 | * The WSS name is set to 65 bytes, 1 byte larger than the maximum | ||
| 81 | * allowed by the WLP spec. This is to have a null terminated string | ||
| 82 | * for display to the user. A maximum of 64 bytes will still be used | ||
| 83 | * when placing the WSS name field in association frames. | ||
| 84 | */ | ||
| 85 | #define WLP_WSS_NAME_SIZE 65 | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Number of bytes added by WLP to data frame | ||
| 89 | * | ||
| 90 | * A data frame transmitted from a host will be placed in a Standard or | ||
| 91 | * Abbreviated WLP frame. These have an extra 4 bytes of header (struct | ||
| 92 | * wlp_frame_std_abbrv_hdr). | ||
| 93 | * When the stack sends this data frame for transmission it needs to ensure | ||
| 94 | * there is enough headroom for this header. | ||
| 95 | */ | ||
| 96 | #define WLP_DATA_HLEN 4 | ||
| 97 | |||
| 98 | /** | ||
| 99 | * State of device regarding WLP Service Set | ||
| 100 | * | ||
| 101 | * WLP_WSS_STATE_NONE: the host does not participate in any WSS | ||
| 102 | * WLP_WSS_STATE_PART_ENROLLED: used as part of the enrollment sequence | ||
| 103 | * ("Partial Enroll"). This state is used to | ||
| 104 | * indicate the first part of enrollment that is | ||
| 105 | * unsecure. If the WSS is unsecure then the | ||
| 106 | * state will promptly go to WLP_WSS_STATE_ENROLLED, | ||
| 107 | * if the WSS is not secure then the enrollment | ||
| 108 | * procedure is a few more steps before we are | ||
| 109 | * enrolled. | ||
| 110 | * WLP_WSS_STATE_ENROLLED: the host is enrolled in a WSS | ||
| 111 | * WLP_WSS_STATE_ACTIVE: WSS is activated | ||
| 112 | * WLP_WSS_STATE_CONNECTED: host is connected to neighbor in WSS | ||
| 113 | * | ||
| 114 | */ | ||
| 115 | enum wlp_wss_state { | ||
| 116 | WLP_WSS_STATE_NONE = 0, | ||
| 117 | WLP_WSS_STATE_PART_ENROLLED, | ||
| 118 | WLP_WSS_STATE_ENROLLED, | ||
| 119 | WLP_WSS_STATE_ACTIVE, | ||
| 120 | WLP_WSS_STATE_CONNECTED, | ||
| 121 | }; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * WSS Secure status | ||
| 125 | * WLP 0.99 Table 6 | ||
| 126 | * | ||
| 127 | * Set to one if the WSS is secure, zero if it is not secure | ||
| 128 | */ | ||
| 129 | enum wlp_wss_sec_status { | ||
| 130 | WLP_WSS_UNSECURE = 0, | ||
| 131 | WLP_WSS_SECURE, | ||
| 132 | }; | ||
| 133 | |||
| 134 | /** | ||
| 135 | * WLP frame type | ||
| 136 | * WLP Draft 0.99 [6.2 Table 1] | ||
| 137 | */ | ||
| 138 | enum wlp_frame_type { | ||
| 139 | WLP_FRAME_STANDARD = 0, | ||
| 140 | WLP_FRAME_ABBREVIATED, | ||
| 141 | WLP_FRAME_CONTROL, | ||
| 142 | WLP_FRAME_ASSOCIATION, | ||
| 143 | }; | ||
| 144 | |||
| 145 | /** | ||
| 146 | * WLP Association Message Type | ||
| 147 | * WLP Draft 0.99 [6.6.1.2 Table 8] | ||
| 148 | */ | ||
| 149 | enum wlp_assoc_type { | ||
| 150 | WLP_ASSOC_D1 = 2, | ||
| 151 | WLP_ASSOC_D2 = 3, | ||
| 152 | WLP_ASSOC_M1 = 4, | ||
| 153 | WLP_ASSOC_M2 = 5, | ||
| 154 | WLP_ASSOC_M3 = 7, | ||
| 155 | WLP_ASSOC_M4 = 8, | ||
| 156 | WLP_ASSOC_M5 = 9, | ||
| 157 | WLP_ASSOC_M6 = 10, | ||
| 158 | WLP_ASSOC_M7 = 11, | ||
| 159 | WLP_ASSOC_M8 = 12, | ||
| 160 | WLP_ASSOC_F0 = 14, | ||
| 161 | WLP_ASSOC_E1 = 32, | ||
| 162 | WLP_ASSOC_E2 = 33, | ||
| 163 | WLP_ASSOC_C1 = 34, | ||
| 164 | WLP_ASSOC_C2 = 35, | ||
| 165 | WLP_ASSOC_C3 = 36, | ||
| 166 | WLP_ASSOC_C4 = 37, | ||
| 167 | }; | ||
| 168 | |||
| 169 | /** | ||
| 170 | * WLP Attribute Type | ||
| 171 | * WLP Draft 0.99 [6.6.1 Table 6] | ||
| 172 | */ | ||
| 173 | enum wlp_attr_type { | ||
| 174 | WLP_ATTR_AUTH = 0x1005, /* Authenticator */ | ||
| 175 | WLP_ATTR_DEV_NAME = 0x1011, /* Device Name */ | ||
| 176 | WLP_ATTR_DEV_PWD_ID = 0x1012, /* Device Password ID */ | ||
| 177 | WLP_ATTR_E_HASH1 = 0x1014, /* E-Hash1 */ | ||
| 178 | WLP_ATTR_E_HASH2 = 0x1015, /* E-Hash2 */ | ||
| 179 | WLP_ATTR_E_SNONCE1 = 0x1016, /* E-SNonce1 */ | ||
| 180 | WLP_ATTR_E_SNONCE2 = 0x1017, /* E-SNonce2 */ | ||
| 181 | WLP_ATTR_ENCR_SET = 0x1018, /* Encrypted Settings */ | ||
| 182 | WLP_ATTR_ENRL_NONCE = 0x101A, /* Enrollee Nonce */ | ||
| 183 | WLP_ATTR_KEYWRAP_AUTH = 0x101E, /* Key Wrap Authenticator */ | ||
| 184 | WLP_ATTR_MANUF = 0x1021, /* Manufacturer */ | ||
| 185 | WLP_ATTR_MSG_TYPE = 0x1022, /* Message Type */ | ||
| 186 | WLP_ATTR_MODEL_NAME = 0x1023, /* Model Name */ | ||
| 187 | WLP_ATTR_MODEL_NR = 0x1024, /* Model Number */ | ||
| 188 | WLP_ATTR_PUB_KEY = 0x1032, /* Public Key */ | ||
| 189 | WLP_ATTR_REG_NONCE = 0x1039, /* Registrar Nonce */ | ||
| 190 | WLP_ATTR_R_HASH1 = 0x103D, /* R-Hash1 */ | ||
| 191 | WLP_ATTR_R_HASH2 = 0x103E, /* R-Hash2 */ | ||
| 192 | WLP_ATTR_R_SNONCE1 = 0x103F, /* R-SNonce1 */ | ||
| 193 | WLP_ATTR_R_SNONCE2 = 0x1040, /* R-SNonce2 */ | ||
| 194 | WLP_ATTR_SERIAL = 0x1042, /* Serial number */ | ||
| 195 | WLP_ATTR_UUID_E = 0x1047, /* UUID-E */ | ||
| 196 | WLP_ATTR_UUID_R = 0x1048, /* UUID-R */ | ||
| 197 | WLP_ATTR_PRI_DEV_TYPE = 0x1054, /* Primary Device Type */ | ||
| 198 | WLP_ATTR_SEC_DEV_TYPE = 0x1055, /* Secondary Device Type */ | ||
| 199 | WLP_ATTR_PORT_DEV = 0x1056, /* Portable Device */ | ||
| 200 | WLP_ATTR_APP_EXT = 0x1058, /* Application Extension */ | ||
| 201 | WLP_ATTR_WLP_VER = 0x2000, /* WLP Version */ | ||
| 202 | WLP_ATTR_WSSID = 0x2001, /* WSSID */ | ||
| 203 | WLP_ATTR_WSS_NAME = 0x2002, /* WSS Name */ | ||
| 204 | WLP_ATTR_WSS_SEC_STAT = 0x2003, /* WSS Secure Status */ | ||
| 205 | WLP_ATTR_WSS_BCAST = 0x2004, /* WSS Broadcast Address */ | ||
| 206 | WLP_ATTR_WSS_M_KEY = 0x2005, /* WSS Master Key */ | ||
| 207 | WLP_ATTR_ACC_ENRL = 0x2006, /* Accepting Enrollment */ | ||
| 208 | WLP_ATTR_WSS_INFO = 0x2007, /* WSS Information */ | ||
| 209 | WLP_ATTR_WSS_SEL_MTHD = 0x2008, /* WSS Selection Method */ | ||
| 210 | WLP_ATTR_ASSC_MTHD_LIST = 0x2009, /* Association Methods List */ | ||
| 211 | WLP_ATTR_SEL_ASSC_MTHD = 0x200A, /* Selected Association Method */ | ||
| 212 | WLP_ATTR_ENRL_HASH_COMM = 0x200B, /* Enrollee Hash Commitment */ | ||
| 213 | WLP_ATTR_WSS_TAG = 0x200C, /* WSS Tag */ | ||
| 214 | WLP_ATTR_WSS_VIRT = 0x200D, /* WSS Virtual EUI-48 */ | ||
| 215 | WLP_ATTR_WLP_ASSC_ERR = 0x200E, /* WLP Association Error */ | ||
| 216 | WLP_ATTR_VNDR_EXT = 0x200F, /* Vendor Extension */ | ||
| 217 | }; | ||
| 218 | |||
| 219 | /** | ||
| 220 | * WLP Category ID of primary/secondary device | ||
| 221 | * WLP Draft 0.99 [6.6.1.8 Table 12] | ||
| 222 | */ | ||
| 223 | enum wlp_dev_category_id { | ||
| 224 | WLP_DEV_CAT_COMPUTER = 1, | ||
| 225 | WLP_DEV_CAT_INPUT, | ||
| 226 | WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER, | ||
| 227 | WLP_DEV_CAT_CAMERA, | ||
| 228 | WLP_DEV_CAT_STORAGE, | ||
| 229 | WLP_DEV_CAT_INFRASTRUCTURE, | ||
| 230 | WLP_DEV_CAT_DISPLAY, | ||
| 231 | WLP_DEV_CAT_MULTIM, | ||
| 232 | WLP_DEV_CAT_GAMING, | ||
| 233 | WLP_DEV_CAT_TELEPHONE, | ||
| 234 | WLP_DEV_CAT_OTHER = 65535, | ||
| 235 | }; | ||
| 236 | |||
| 237 | /** | ||
| 238 | * WLP WSS selection method | ||
| 239 | * WLP Draft 0.99 [6.6.1.6 Table 10] | ||
| 240 | */ | ||
| 241 | enum wlp_wss_sel_mthd { | ||
| 242 | WLP_WSS_ENRL_SELECT = 1, /* Enrollee selects */ | ||
| 243 | WLP_WSS_REG_SELECT, /* Registrar selects */ | ||
| 244 | }; | ||
| 245 | |||
| 246 | /** | ||
| 247 | * WLP association error values | ||
| 248 | * WLP Draft 0.99 [6.6.1.5 Table 9] | ||
| 249 | */ | ||
| 250 | enum wlp_assc_error { | ||
| 251 | WLP_ASSOC_ERROR_NONE, | ||
| 252 | WLP_ASSOC_ERROR_AUTH, /* Authenticator Failure */ | ||
| 253 | WLP_ASSOC_ERROR_ROGUE, /* Rogue activity suspected */ | ||
| 254 | WLP_ASSOC_ERROR_BUSY, /* Device busy */ | ||
| 255 | WLP_ASSOC_ERROR_LOCK, /* Setup Locked */ | ||
| 256 | WLP_ASSOC_ERROR_NOT_READY, /* Registrar not ready */ | ||
| 257 | WLP_ASSOC_ERROR_INV, /* Invalid WSS selection */ | ||
| 258 | WLP_ASSOC_ERROR_MSG_TIME, /* Message timeout */ | ||
| 259 | WLP_ASSOC_ERROR_ENR_TIME, /* Enrollment session timeout */ | ||
| 260 | WLP_ASSOC_ERROR_PW, /* Device password invalid */ | ||
| 261 | WLP_ASSOC_ERROR_VER, /* Unsupported version */ | ||
| 262 | WLP_ASSOC_ERROR_INT, /* Internal error */ | ||
| 263 | WLP_ASSOC_ERROR_UNDEF, /* Undefined error */ | ||
| 264 | WLP_ASSOC_ERROR_NUM, /* Numeric comparison failure */ | ||
| 265 | WLP_ASSOC_ERROR_WAIT, /* Waiting for user input */ | ||
| 266 | }; | ||
| 267 | |||
| 268 | /** | ||
| 269 | * WLP Parameters | ||
| 270 | * WLP 0.99 [7.7] | ||
| 271 | */ | ||
| 272 | enum wlp_parameters { | ||
| 273 | WLP_PER_MSG_TIMEOUT = 15, /* Seconds to wait for response to | ||
| 274 | association message. */ | ||
| 275 | }; | ||
| 276 | |||
| 277 | /** | ||
| 278 | * WLP IE | ||
| 279 | * | ||
| 280 | * The WLP IE should be included in beacons by all devices. | ||
| 281 | * | ||
| 282 | * The driver can set only a few of the fields in this information element, | ||
| 283 | * most fields are managed by the device self. When the driver needs to set | ||
| 284 | * a field it will only provide values for the fields of interest, the rest | ||
| 285 | * will be filled with zeroes. The fields of interest are: | ||
| 286 | * | ||
| 287 | * Element ID | ||
| 288 | * Length | ||
| 289 | * Capabilities (only to include WSSID Hash list length) | ||
| 290 | * WSSID Hash List fields | ||
| 291 | * | ||
| 292 | * WLP 0.99 [6.7] | ||
| 293 | * | ||
| 294 | * Only the fields that will be used are detailed in this structure, rest | ||
| 295 | * are not detailed or marked as "notused". | ||
| 296 | */ | ||
| 297 | struct wlp_ie { | ||
| 298 | struct uwb_ie_hdr hdr; | ||
| 299 | __le16 capabilities; | ||
| 300 | __le16 cycle_param; | ||
| 301 | __le16 acw_anchor_addr; | ||
| 302 | u8 wssid_hash_list[]; | ||
| 303 | } __packed; | ||
| 304 | |||
| 305 | static inline int wlp_ie_hash_length(struct wlp_ie *ie) | ||
| 306 | { | ||
| 307 | return (le16_to_cpu(ie->capabilities) >> 12) & 0xf; | ||
| 308 | } | ||
| 309 | |||
| 310 | static inline void wlp_ie_set_hash_length(struct wlp_ie *ie, int hash_length) | ||
| 311 | { | ||
| 312 | u16 caps = le16_to_cpu(ie->capabilities); | ||
| 313 | caps = (caps & ~(0xf << 12)) | (hash_length << 12); | ||
| 314 | ie->capabilities = cpu_to_le16(caps); | ||
| 315 | } | ||
| 316 | |||
| 317 | /** | ||
| 318 | * WLP nonce | ||
| 319 | * WLP Draft 0.99 [6.6.1 Table 6] | ||
| 320 | * | ||
| 321 | * A 128-bit random number often used (E-SNonce1, E-SNonce2, Enrollee | ||
| 322 | * Nonce, Registrar Nonce, R-SNonce1, R-SNonce2). It is passed to HW so | ||
| 323 | * it is packed. | ||
| 324 | */ | ||
| 325 | struct wlp_nonce { | ||
| 326 | u8 data[16]; | ||
| 327 | } __packed; | ||
| 328 | |||
| 329 | /** | ||
| 330 | * WLP UUID | ||
| 331 | * WLP Draft 0.99 [6.6.1 Table 6] | ||
| 332 | * | ||
| 333 | * Universally Unique Identifier (UUID) encoded as an octet string in the | ||
| 334 | * order the octets are shown in string representation in RFC4122. A UUID | ||
| 335 | * is often used (UUID-E, UUID-R, WSSID). It is passed to HW so it is packed. | ||
| 336 | */ | ||
| 337 | struct wlp_uuid { | ||
| 338 | u8 data[16]; | ||
| 339 | } __packed; | ||
| 340 | |||
| 341 | |||
| 342 | /** | ||
| 343 | * Primary and secondary device type attributes | ||
| 344 | * WLP Draft 0.99 [6.6.1.8] | ||
| 345 | */ | ||
| 346 | struct wlp_dev_type { | ||
| 347 | enum wlp_dev_category_id category:16; | ||
| 348 | u8 OUI[3]; | ||
| 349 | u8 OUIsubdiv; | ||
| 350 | __le16 subID; | ||
| 351 | } __packed; | ||
| 352 | |||
| 353 | /** | ||
| 354 | * WLP frame header | ||
| 355 | * WLP Draft 0.99 [6.2] | ||
| 356 | */ | ||
| 357 | struct wlp_frame_hdr { | ||
| 358 | __le16 mux_hdr; /* WLP_PROTOCOL_ID */ | ||
| 359 | enum wlp_frame_type type:8; | ||
| 360 | } __packed; | ||
| 361 | |||
| 362 | /** | ||
| 363 | * WLP attribute field header | ||
| 364 | * WLP Draft 0.99 [6.6.1] | ||
| 365 | * | ||
| 366 | * Header of each attribute found in an association frame | ||
| 367 | */ | ||
| 368 | struct wlp_attr_hdr { | ||
| 369 | __le16 type; | ||
| 370 | __le16 length; | ||
| 371 | } __packed; | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Device information commonly used together | ||
| 375 | * | ||
| 376 | * Each of these device information elements has a specified range in which it | ||
| 377 | * should fit (WLP 0.99 [Table 6]). This range provided in the spec does not | ||
| 378 | * include the termination null '\0' character (when used in the | ||
| 379 | * association protocol the attribute fields are accompanied | ||
| 380 | * with a "length" field so the full range from the spec can be used for | ||
| 381 | * the value). We thus allocate an extra byte to be able to store a string | ||
| 382 | * of max length with a terminating '\0'. | ||
| 383 | */ | ||
| 384 | struct wlp_device_info { | ||
| 385 | char name[33]; | ||
| 386 | char model_name[33]; | ||
| 387 | char manufacturer[65]; | ||
| 388 | char model_nr[33]; | ||
| 389 | char serial[33]; | ||
| 390 | struct wlp_dev_type prim_dev_type; | ||
| 391 | }; | ||
| 392 | |||
| 393 | /** | ||
| 394 | * Macros for the WLP attributes | ||
| 395 | * | ||
| 396 | * There are quite a few attributes (total is 43). The attribute layout can be | ||
| 397 | * in one of three categories: one value, an array, an enum forced to 8 bits. | ||
| 398 | * These macros help with their definitions. | ||
| 399 | */ | ||
| 400 | #define wlp_attr(type, name) \ | ||
| 401 | struct wlp_attr_##name { \ | ||
| 402 | struct wlp_attr_hdr hdr; \ | ||
| 403 | type name; \ | ||
| 404 | } __packed; | ||
| 405 | |||
| 406 | #define wlp_attr_array(type, name) \ | ||
| 407 | struct wlp_attr_##name { \ | ||
| 408 | struct wlp_attr_hdr hdr; \ | ||
| 409 | type name[]; \ | ||
| 410 | } __packed; | ||
| 411 | |||
| 412 | /** | ||
| 413 | * WLP association attribute fields | ||
| 414 | * WLP Draft 0.99 [6.6.1 Table 6] | ||
| 415 | * | ||
| 416 | * Attributes appear in same order as the Table in the spec | ||
| 417 | * FIXME Does not define all attributes yet | ||
| 418 | */ | ||
| 419 | |||
| 420 | /* Device name: Friendly name of sending device */ | ||
| 421 | wlp_attr_array(u8, dev_name) | ||
| 422 | |||
| 423 | /* Enrollee Nonce: Random number generated by enrollee for an enrollment | ||
| 424 | * session */ | ||
| 425 | wlp_attr(struct wlp_nonce, enonce) | ||
| 426 | |||
| 427 | /* Manufacturer name: Name of manufacturer of the sending device */ | ||
| 428 | wlp_attr_array(u8, manufacturer) | ||
| 429 | |||
| 430 | /* WLP Message Type */ | ||
| 431 | wlp_attr(u8, msg_type) | ||
| 432 | |||
| 433 | /* WLP Model name: Model name of sending device */ | ||
| 434 | wlp_attr_array(u8, model_name) | ||
| 435 | |||
| 436 | /* WLP Model number: Model number of sending device */ | ||
| 437 | wlp_attr_array(u8, model_nr) | ||
| 438 | |||
| 439 | /* Registrar Nonce: Random number generated by registrar for an enrollment | ||
| 440 | * session */ | ||
| 441 | wlp_attr(struct wlp_nonce, rnonce) | ||
| 442 | |||
| 443 | /* Serial number of device */ | ||
| 444 | wlp_attr_array(u8, serial) | ||
| 445 | |||
| 446 | /* UUID of enrollee */ | ||
| 447 | wlp_attr(struct wlp_uuid, uuid_e) | ||
| 448 | |||
| 449 | /* UUID of registrar */ | ||
| 450 | wlp_attr(struct wlp_uuid, uuid_r) | ||
| 451 | |||
| 452 | /* WLP Primary device type */ | ||
| 453 | wlp_attr(struct wlp_dev_type, prim_dev_type) | ||
| 454 | |||
| 455 | /* WLP Secondary device type */ | ||
| 456 | wlp_attr(struct wlp_dev_type, sec_dev_type) | ||
| 457 | |||
| 458 | /* WLP protocol version */ | ||
| 459 | wlp_attr(u8, version) | ||
| 460 | |||
| 461 | /* WLP service set identifier */ | ||
| 462 | wlp_attr(struct wlp_uuid, wssid) | ||
| 463 | |||
| 464 | /* WLP WSS name */ | ||
| 465 | wlp_attr_array(u8, wss_name) | ||
| 466 | |||
| 467 | /* WLP WSS Secure Status */ | ||
| 468 | wlp_attr(u8, wss_sec_status) | ||
| 469 | |||
| 470 | /* WSS Broadcast Address */ | ||
| 471 | wlp_attr(struct uwb_mac_addr, wss_bcast) | ||
| 472 | |||
| 473 | /* WLP Accepting Enrollment */ | ||
| 474 | wlp_attr(u8, accept_enrl) | ||
| 475 | |||
| 476 | /** | ||
| 477 | * WSS information attributes | ||
| 478 | * WLP Draft 0.99 [6.6.3 Table 15] | ||
| 479 | */ | ||
| 480 | struct wlp_wss_info { | ||
| 481 | struct wlp_attr_wssid wssid; | ||
| 482 | struct wlp_attr_wss_name name; | ||
| 483 | struct wlp_attr_accept_enrl accept; | ||
| 484 | struct wlp_attr_wss_sec_status sec_stat; | ||
| 485 | struct wlp_attr_wss_bcast bcast; | ||
| 486 | } __packed; | ||
| 487 | |||
| 488 | /* WLP WSS Information */ | ||
| 489 | wlp_attr_array(struct wlp_wss_info, wss_info) | ||
| 490 | |||
| 491 | /* WLP WSS Selection method */ | ||
| 492 | wlp_attr(u8, wss_sel_mthd) | ||
| 493 | |||
| 494 | /* WLP WSS tag */ | ||
| 495 | wlp_attr(u8, wss_tag) | ||
| 496 | |||
| 497 | /* WSS Virtual Address */ | ||
| 498 | wlp_attr(struct uwb_mac_addr, wss_virt) | ||
| 499 | |||
| 500 | /* WLP association error */ | ||
| 501 | wlp_attr(u8, wlp_assc_err) | ||
| 502 | |||
| 503 | /** | ||
| 504 | * WLP standard and abbreviated frames | ||
| 505 | * | ||
| 506 | * WLP Draft 0.99 [6.3] and [6.4] | ||
| 507 | * | ||
| 508 | * The difference between the WLP standard frame and the WLP | ||
| 509 | * abbreviated frame is that the standard frame includes the src | ||
| 510 | * and dest addresses from the Ethernet header, the abbreviated frame does | ||
| 511 | * not. | ||
| 512 | * The src/dest (as well as the type/length and client data) are already | ||
| 513 | * defined as part of the Ethernet header, we do not do this here. | ||
| 514 | * From this perspective the standard and abbreviated frames appear the | ||
| 515 | * same - they will be treated differently though. | ||
| 516 | * | ||
| 517 | * The size of this header is also captured in WLP_DATA_HLEN to enable | ||
| 518 | * interfaces to prepare their headroom. | ||
| 519 | */ | ||
| 520 | struct wlp_frame_std_abbrv_hdr { | ||
| 521 | struct wlp_frame_hdr hdr; | ||
| 522 | u8 tag; | ||
| 523 | } __packed; | ||
| 524 | |||
| 525 | /** | ||
| 526 | * WLP association frames | ||
| 527 | * | ||
| 528 | * WLP Draft 0.99 [6.6] | ||
| 529 | */ | ||
| 530 | struct wlp_frame_assoc { | ||
| 531 | struct wlp_frame_hdr hdr; | ||
| 532 | enum wlp_assoc_type type:8; | ||
| 533 | struct wlp_attr_version version; | ||
| 534 | struct wlp_attr_msg_type msg_type; | ||
| 535 | u8 attr[]; | ||
| 536 | } __packed; | ||
| 537 | |||
| 538 | /* Ethernet to dev address mapping */ | ||
| 539 | struct wlp_eda { | ||
| 540 | spinlock_t lock; | ||
| 541 | struct list_head cache; /* Eth<->Dev Addr cache */ | ||
| 542 | }; | ||
| 543 | |||
| 544 | /** | ||
| 545 | * WSS information temporary storage | ||
| 546 | * | ||
| 547 | * This information is only stored temporarily during discovery. It should | ||
| 548 | * not be stored unless the device is enrolled in the advertised WSS. This | ||
| 549 | * is done mainly because we follow the letter of the spec in this regard. | ||
| 550 | * See WLP 0.99 [7.2.3]. | ||
| 551 | * When the device does become enrolled in a WSS the WSS information will | ||
| 552 | * be stored as part of the more comprehensive struct wlp_wss. | ||
| 553 | */ | ||
| 554 | struct wlp_wss_tmp_info { | ||
| 555 | char name[WLP_WSS_NAME_SIZE]; | ||
| 556 | u8 accept_enroll; | ||
| 557 | u8 sec_status; | ||
| 558 | struct uwb_mac_addr bcast; | ||
| 559 | }; | ||
| 560 | |||
| 561 | struct wlp_wssid_e { | ||
| 562 | struct list_head node; | ||
| 563 | struct wlp_uuid wssid; | ||
| 564 | struct wlp_wss_tmp_info *info; | ||
| 565 | }; | ||
| 566 | |||
| 567 | /** | ||
| 568 | * A cache entry of WLP neighborhood | ||
| 569 | * | ||
| 570 | * @node: head of list is wlp->neighbors | ||
| 571 | * @wssid: list of wssids of this neighbor, element is wlp_wssid_e | ||
| 572 | * @info: temporary storage for information learned during discovery. This | ||
| 573 | * storage is used together with the wssid_e temporary storage | ||
| 574 | * during discovery. | ||
| 575 | */ | ||
| 576 | struct wlp_neighbor_e { | ||
| 577 | struct list_head node; | ||
| 578 | struct wlp_uuid uuid; | ||
| 579 | struct uwb_dev *uwb_dev; | ||
| 580 | struct list_head wssid; /* Elements are wlp_wssid_e */ | ||
| 581 | struct wlp_device_info *info; | ||
| 582 | }; | ||
| 583 | |||
| 584 | struct wlp; | ||
| 585 | /** | ||
| 586 | * Information for an association session in progress. | ||
| 587 | * | ||
| 588 | * @exp_message: The type of the expected message. Both this message and a | ||
| 589 | * F0 message (which can be sent in response to any | ||
| 590 | * association frame) will be accepted as a valid message for | ||
| 591 | * this session. | ||
| 592 | * @cb: The function that will be called upon receipt of this | ||
| 593 | * message. | ||
| 594 | * @cb_priv: Private data of callback | ||
| 595 | * @data: Data used in association process (always a sk_buff?) | ||
| 596 | * @neighbor: Address of neighbor with which association session is in | ||
| 597 | * progress. | ||
| 598 | */ | ||
| 599 | struct wlp_session { | ||
| 600 | enum wlp_assoc_type exp_message; | ||
| 601 | void (*cb)(struct wlp *); | ||
| 602 | void *cb_priv; | ||
| 603 | void *data; | ||
| 604 | struct uwb_dev_addr neighbor_addr; | ||
| 605 | }; | ||
| 606 | |||
| 607 | /** | ||
| 608 | * WLP Service Set | ||
| 609 | * | ||
| 610 | * @mutex: used to protect entire WSS structure. | ||
| 611 | * | ||
| 612 | * @name: The WSS name is set to 65 bytes, 1 byte larger than the maximum | ||
| 613 | * allowed by the WLP spec. This is to have a null terminated string | ||
| 614 | * for display to the user. A maximum of 64 bytes will still be used | ||
| 615 | * when placing the WSS name field in association frames. | ||
| 616 | * | ||
| 617 | * @accept_enroll: Accepting enrollment: Set to one if registrar is | ||
| 618 | * accepting enrollment in WSS, or zero otherwise. | ||
| 619 | * | ||
| 620 | * Global and local information for each WSS in which we are enrolled. | ||
| 621 | * WLP 0.99 Section 7.2.1 and Section 7.2.2 | ||
| 622 | */ | ||
| 623 | struct wlp_wss { | ||
| 624 | struct mutex mutex; | ||
| 625 | struct kobject kobj; | ||
| 626 | /* Global properties. */ | ||
| 627 | struct wlp_uuid wssid; | ||
| 628 | u8 hash; | ||
| 629 | char name[WLP_WSS_NAME_SIZE]; | ||
| 630 | struct uwb_mac_addr bcast; | ||
| 631 | u8 secure_status:1; | ||
| 632 | u8 master_key[16]; | ||
| 633 | /* Local properties. */ | ||
| 634 | u8 tag; | ||
| 635 | struct uwb_mac_addr virtual_addr; | ||
| 636 | /* Extra */ | ||
| 637 | u8 accept_enroll:1; | ||
| 638 | enum wlp_wss_state state; | ||
| 639 | }; | ||
| 640 | |||
| 641 | /** | ||
| 642 | * WLP main structure | ||
| 643 | * @mutex: protect changes to WLP structure. We only allow changes to the | ||
| 644 | * uuid, so currently this mutex only protects this field. | ||
| 645 | */ | ||
| 646 | struct wlp { | ||
| 647 | struct mutex mutex; | ||
| 648 | struct uwb_rc *rc; /* UWB radio controller */ | ||
| 649 | struct net_device *ndev; | ||
| 650 | struct uwb_pal pal; | ||
| 651 | struct wlp_eda eda; | ||
| 652 | struct wlp_uuid uuid; | ||
| 653 | struct wlp_session *session; | ||
| 654 | struct wlp_wss wss; | ||
| 655 | struct mutex nbmutex; /* Neighbor mutex protects neighbors list */ | ||
| 656 | struct list_head neighbors; /* Elements are wlp_neighbor_e */ | ||
| 657 | struct uwb_notifs_handler uwb_notifs_handler; | ||
| 658 | struct wlp_device_info *dev_info; | ||
| 659 | void (*fill_device_info)(struct wlp *wlp, struct wlp_device_info *info); | ||
| 660 | int (*xmit_frame)(struct wlp *, struct sk_buff *, | ||
| 661 | struct uwb_dev_addr *); | ||
| 662 | void (*stop_queue)(struct wlp *); | ||
| 663 | void (*start_queue)(struct wlp *); | ||
| 664 | }; | ||
| 665 | |||
| 666 | /* sysfs */ | ||
| 667 | |||
| 668 | |||
| 669 | struct wlp_wss_attribute { | ||
| 670 | struct attribute attr; | ||
| 671 | ssize_t (*show)(struct wlp_wss *wss, char *buf); | ||
| 672 | ssize_t (*store)(struct wlp_wss *wss, const char *buf, size_t count); | ||
| 673 | }; | ||
| 674 | |||
| 675 | #define WSS_ATTR(_name, _mode, _show, _store) \ | ||
| 676 | static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode, \ | ||
| 677 | _show, _store) | ||
| 678 | |||
| 679 | extern int wlp_setup(struct wlp *, struct uwb_rc *, struct net_device *ndev); | ||
| 680 | extern void wlp_remove(struct wlp *); | ||
| 681 | extern ssize_t wlp_neighborhood_show(struct wlp *, char *); | ||
| 682 | extern int wlp_wss_setup(struct net_device *, struct wlp_wss *); | ||
| 683 | extern void wlp_wss_remove(struct wlp_wss *); | ||
| 684 | extern ssize_t wlp_wss_activate_show(struct wlp_wss *, char *); | ||
| 685 | extern ssize_t wlp_wss_activate_store(struct wlp_wss *, const char *, size_t); | ||
| 686 | extern ssize_t wlp_eda_show(struct wlp *, char *); | ||
| 687 | extern ssize_t wlp_eda_store(struct wlp *, const char *, size_t); | ||
| 688 | extern ssize_t wlp_uuid_show(struct wlp *, char *); | ||
| 689 | extern ssize_t wlp_uuid_store(struct wlp *, const char *, size_t); | ||
| 690 | extern ssize_t wlp_dev_name_show(struct wlp *, char *); | ||
| 691 | extern ssize_t wlp_dev_name_store(struct wlp *, const char *, size_t); | ||
| 692 | extern ssize_t wlp_dev_manufacturer_show(struct wlp *, char *); | ||
| 693 | extern ssize_t wlp_dev_manufacturer_store(struct wlp *, const char *, size_t); | ||
| 694 | extern ssize_t wlp_dev_model_name_show(struct wlp *, char *); | ||
| 695 | extern ssize_t wlp_dev_model_name_store(struct wlp *, const char *, size_t); | ||
| 696 | extern ssize_t wlp_dev_model_nr_show(struct wlp *, char *); | ||
| 697 | extern ssize_t wlp_dev_model_nr_store(struct wlp *, const char *, size_t); | ||
| 698 | extern ssize_t wlp_dev_serial_show(struct wlp *, char *); | ||
| 699 | extern ssize_t wlp_dev_serial_store(struct wlp *, const char *, size_t); | ||
| 700 | extern ssize_t wlp_dev_prim_category_show(struct wlp *, char *); | ||
| 701 | extern ssize_t wlp_dev_prim_category_store(struct wlp *, const char *, | ||
| 702 | size_t); | ||
| 703 | extern ssize_t wlp_dev_prim_OUI_show(struct wlp *, char *); | ||
| 704 | extern ssize_t wlp_dev_prim_OUI_store(struct wlp *, const char *, size_t); | ||
| 705 | extern ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *, char *); | ||
| 706 | extern ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *, const char *, | ||
| 707 | size_t); | ||
| 708 | extern ssize_t wlp_dev_prim_subcat_show(struct wlp *, char *); | ||
| 709 | extern ssize_t wlp_dev_prim_subcat_store(struct wlp *, const char *, | ||
| 710 | size_t); | ||
| 711 | extern int wlp_receive_frame(struct device *, struct wlp *, struct sk_buff *, | ||
| 712 | struct uwb_dev_addr *); | ||
| 713 | extern int wlp_prepare_tx_frame(struct device *, struct wlp *, | ||
| 714 | struct sk_buff *, struct uwb_dev_addr *); | ||
| 715 | void wlp_reset_all(struct wlp *wlp); | ||
| 716 | |||
| 717 | /** | ||
| 718 | * Initialize WSS | ||
| 719 | */ | ||
| 720 | static inline | ||
| 721 | void wlp_wss_init(struct wlp_wss *wss) | ||
| 722 | { | ||
| 723 | mutex_init(&wss->mutex); | ||
| 724 | } | ||
| 725 | |||
| 726 | static inline | ||
| 727 | void wlp_init(struct wlp *wlp) | ||
| 728 | { | ||
| 729 | INIT_LIST_HEAD(&wlp->neighbors); | ||
| 730 | mutex_init(&wlp->mutex); | ||
| 731 | mutex_init(&wlp->nbmutex); | ||
| 732 | wlp_wss_init(&wlp->wss); | ||
| 733 | } | ||
| 734 | |||
| 735 | |||
| 736 | #endif /* #ifndef __LINUX__WLP_H_ */ | ||
