Currently we are in the process of replacing the WEXT interface with the cfg80211 API. WEXT code is currently within a sub directory and not included in the module build. The driver is designed with various layers of abstraction; - The main layer contains the net_device_ops callbacks. Also included in this layer is the rx/tx code. - The SDIO layer contains code specific to the SDIO hardware. - The cfg80211 layer contains the implementation of the cfg80211 configuration API. - The HIF (Host InterFace) layer provides driver policy and interfaces between the cfg80211 layer and the FIL. - The FIL (Firmware Interface Layer) provides mechanism for interfacing with the firmware. The firmware interface is derived from the WEXT driver, if we write code to interface with the firmware as a separate abstraction layer then the cfg80211 code and the rest of the driver functionality can be written cleanly from scratch. The separate layers are restricted to calls as indicated by the following diagram, A --> B indicates layer A calls functions in layer B. ----> main (tx/rx) <---> SDIO | | | | | v v cfg80211 <---> HIF | | v FIL Implementation status is outlined in README.rst. A todo list is included in TODO.rst. Add initial cfg80211 implementation. Signed-off-by: Tobin C. Harding <me@xxxxxxxx> --- drivers/staging/ks7010/Kconfig | 2 + drivers/staging/ks7010/Makefile | 28 +- drivers/staging/ks7010/README.rst | 91 +++ drivers/staging/ks7010/TODO.rst | 30 + drivers/staging/ks7010/cfg80211.c | 981 +++++++++++++++++++++++++++ drivers/staging/ks7010/cfg80211.h | 40 ++ drivers/staging/ks7010/common.h | 33 + drivers/staging/ks7010/eap.h | 73 ++ drivers/staging/ks7010/fil.c | 1294 ++++++++++++++++++++++++++++++++++++ drivers/staging/ks7010/fil.h | 559 ++++++++++++++++ drivers/staging/ks7010/fil_types.h | 851 ++++++++++++++++++++++++ drivers/staging/ks7010/hif.c | 505 ++++++++++++++ drivers/staging/ks7010/hif.h | 202 ++++++ drivers/staging/ks7010/ks7010.h | 309 +++++++++ drivers/staging/ks7010/main.c | 337 ++++++++++ drivers/staging/ks7010/rx.c | 130 ++++ drivers/staging/ks7010/sdio.c | 691 +++++++++++++++++++ drivers/staging/ks7010/sdio.h | 37 ++ drivers/staging/ks7010/tx.c | 170 +++++ 19 files changed, 6362 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/ks7010/README.rst create mode 100644 drivers/staging/ks7010/TODO.rst create mode 100644 drivers/staging/ks7010/cfg80211.c create mode 100644 drivers/staging/ks7010/cfg80211.h create mode 100644 drivers/staging/ks7010/common.h create mode 100644 drivers/staging/ks7010/eap.h create mode 100644 drivers/staging/ks7010/fil.c create mode 100644 drivers/staging/ks7010/fil.h create mode 100644 drivers/staging/ks7010/fil_types.h create mode 100644 drivers/staging/ks7010/hif.c create mode 100644 drivers/staging/ks7010/hif.h create mode 100644 drivers/staging/ks7010/ks7010.h create mode 100644 drivers/staging/ks7010/main.c create mode 100644 drivers/staging/ks7010/rx.c create mode 100644 drivers/staging/ks7010/sdio.c create mode 100644 drivers/staging/ks7010/sdio.h create mode 100644 drivers/staging/ks7010/tx.c diff --git a/drivers/staging/ks7010/Kconfig b/drivers/staging/ks7010/Kconfig index 437b928..71f2026 100644 --- a/drivers/staging/ks7010/Kconfig +++ b/drivers/staging/ks7010/Kconfig @@ -1,5 +1,7 @@ config KS7010 tristate "KeyStream KS7010 SDIO support" + depends on CFG80211 + depends on MMC ---help--- This is a driver for KeyStream KS7010 based SDIO WIFI cards. It is found on at least later Spectec SDW-821 (FCC-ID "S2Y-WLAN-11G-K" only, diff --git a/drivers/staging/ks7010/Makefile b/drivers/staging/ks7010/Makefile index 9444885..1cd570e 100644 --- a/drivers/staging/ks7010/Makefile +++ b/drivers/staging/ks7010/Makefile @@ -1 +1,27 @@ -# Makefile intentionally left blank +#------------------------------------------------------------------------- +# Driver for KeyStream wireless LAN cards. +# +# Copyright (C) 2005-2008 KeyStream Corp. +# Copyright (C) 2009 Renesas Technology Corp. +# Copyright (C) 2017 Tobin C. Harding. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +#------------------------------------------------------------------------- + +ccflags-y += -DDEBUG + +obj-$(CONFIG_KS7010) += ks7010.o +ks7010-y += main.o +ks7010-y += tx.o +ks7010-y += rx.o +ks7010-y += sdio.o +ks7010-y += cfg80211.o +ks7010-y += fil.o +ks7010-y += hif.o diff --git a/drivers/staging/ks7010/README.rst b/drivers/staging/ks7010/README.rst new file mode 100644 index 0000000..2343300 --- /dev/null +++ b/drivers/staging/ks7010/README.rst @@ -0,0 +1,91 @@ +============================= +Key Stream SDIO Device Driver +============================= + +Current Status +-------------- + +- Firmware Interface Layer complete. +- Host Interface Layer, partial skeleton implementation. +- cfg80211 minimal implementation. +- SDIO mostly implemented. + +See drivers/staging/ks7010/TODO.rst for TODO's. + +Notes +----- + +Some enum mib_attribute identifiers have been renamed. This reduces +the continuity between the WEXT driver and the cfg80211 driver. To +combat this, new identifiers are prefixed with 'MIB_' (instead of +'DOT11_' or 'LOCAL_'). + +We need to add readiness checks to a lot of functions, check vif is ready (cfg80211), +check sdio function is enabled (sdio), check ks7010 device state (all). + +Description +----------- + +Driver conversion from WEXT interface to cfg80211 API. + +The original KeyStream SDIO wireless driver brought into staging +implements the WEXT interface. + +The WEXT driver is based on source code from the Ben Nanonote extra repository [1] +which is based on the original v007 release from Renesas [2]. + +[1] http://projects.qi-hardware.com/index.php/p/openwrt-packages/source/tree/master/ks7010/src +[2] http://downloads.qi-hardware.com/software/ks7010_sdio_v007.tar.bz2 + +Extensive refactoring has been done to the WEXT driver whilst in staging +and the current mainline tip is untested. + +WEXT driver files :- + - ks7010_sdio.[ch] - SDIO code. + - ks_hostif.[ch] - Device interface. + - ks_wlan_net.c - WEXT interface. + - mic.[ch] - Custom Michael MIC implementation. + - eap_packet.h - EAP headers. + - ks_wlan_ioctl.h - WEXT IOCTL. + +cfg80211 driver files :- + - main.c - Main driver file (net_device_ops etc). + - ks7010.h - Main driver header file. + - common.h - Constant definitions and forward declarations. + - eap.h - EAPOL structure descriptions. + - sdio.[ch] - SDIO code. + - fil.[ch] - Firmware Interface Layer. + - fil_types.h - Internal FIL types. + - hif.[ch] - Host Interface Layer. + - cfg80211.c - cfg80211 API implementation. + - tx.c - Transmit path functions. + - rx.c - Recive path functions. + +cfg80211 driver files to do :- + - mic.[ch] - Interface to the kernel Michael MIC implementation. + +WEXT driver code now resides in the sub directory drivers/staging/ks7010/wext +while the cfg80211 driver has the 'root' directory (drivers/staging/ks7010). + +Other Information +================= + +Hardware +-------- +https://wikidevi.com/wiki/Spectec_SDW-821_(KeyStream) +https://wikidevi.com/wiki/Spectec_SDW-823 + +Kernel Config +------------- +http://cateee.net/lkddb/web-lkddb/KS7010.html + +also enable + - MMC_DEBUG + +Testing +------- +http://elinux.org/Tests:SDIO-KS7010 + +Writing SDIO Linux Drivers +-------------------------- +http://www.varsanofiev.com/inside/WritingLinuxSDIODrivers.htm diff --git a/drivers/staging/ks7010/TODO.rst b/drivers/staging/ks7010/TODO.rst new file mode 100644 index 0000000..553d860 --- /dev/null +++ b/drivers/staging/ks7010/TODO.rst @@ -0,0 +1,30 @@ +====== +TODO's +====== + +- Handle instances of /* FIXME ... */ +- Handle instances of /* TODO ... */ +- Audit locking. +- Audit WEP/WPA/RSN code (handling of keys etc). +- See README.rst for what more needs implementing. + +Any comments/suggestions on the the driver most happily +accepted. Especially, but not limited to, the design (abstraction +layers, FIL, HIF, etc), *locking*, Wi-Fi protocol details. Also +general device driver suggestions welcome as well as Linux kernel +development tips. + +Thanks for taking the time to look at this driver. + +Please send ideas and suggestions, no matter how small, to: +Tobin C. Harding <me@xxxxxxxx> + +For design discussions (especially cfg80211), please CC: +Linux Wireless List <linux-wireless@xxxxxxxxxxxxxxx> + + +Please send patches to: +Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> +Wolfram Sang <wsa@xxxxxxxxxxxxx> +Tobin C. Harding <me@xxxxxxxx> +Linux Driver Project Developer List <driverdev-devel@xxxxxxxxxxxxxxxxxxxxxx> diff --git a/drivers/staging/ks7010/cfg80211.c b/drivers/staging/ks7010/cfg80211.c new file mode 100644 index 0000000..2a56010 --- /dev/null +++ b/drivers/staging/ks7010/cfg80211.c @@ -0,0 +1,981 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <net/cfg80211.h> +#include <linux/inetdevice.h> + +#include "ks7010.h" +#include "cfg80211.h" + +#define RATE_TAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .flags = (_flags), \ + .hw_value = (_rateid), \ +} + +#define CHAN_TAB_ENT(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_rate ks7010_rates[] = { + RATE_TAB_ENT(10, 0x1, 0), + RATE_TAB_ENT(20, 0x2, 0), + RATE_TAB_ENT(55, 0x4, 0), + RATE_TAB_ENT(110, 0x8, 0), + RATE_TAB_ENT(60, 0x10, 0), + RATE_TAB_ENT(90, 0x20, 0), + RATE_TAB_ENT(120, 0x40, 0), + RATE_TAB_ENT(180, 0x80, 0), + RATE_TAB_ENT(240, 0x100, 0), + RATE_TAB_ENT(360, 0x200, 0), + RATE_TAB_ENT(480, 0x400, 0), + RATE_TAB_ENT(540, 0x800, 0), +}; + +static struct ieee80211_channel ks7010_2ghz_channels[] = { + CHAN_TAB_ENT(1, 2412, 0), + CHAN_TAB_ENT(2, 2417, 0), + CHAN_TAB_ENT(3, 2422, 0), + CHAN_TAB_ENT(4, 2427, 0), + CHAN_TAB_ENT(5, 2432, 0), + CHAN_TAB_ENT(6, 2437, 0), + CHAN_TAB_ENT(7, 2442, 0), + CHAN_TAB_ENT(8, 2447, 0), + CHAN_TAB_ENT(9, 2452, 0), + CHAN_TAB_ENT(10, 2457, 0), + CHAN_TAB_ENT(11, 2462, 0), + CHAN_TAB_ENT(12, 2467, 0), + CHAN_TAB_ENT(13, 2472, 0), + CHAN_TAB_ENT(14, 2484, 0), +}; + +static struct ieee80211_supported_band ks7010_band_2ghz = { + .n_channels = ARRAY_SIZE(ks7010_2ghz_channels), + .channels = ks7010_2ghz_channels, + .n_bitrates = ARRAY_SIZE(ks7010_rates), + .bitrates = ks7010_rates, +}; + +static bool ks7010_cfg80211_ready(struct ks7010_vif *vif) +{ + ks_debug("not implemented"); + return false; +} + +static void ks7010_set_wpa_version(struct ks7010_vif *vif, + enum nl80211_wpa_versions wpa_version) +{ + ks_debug("%s: %u\n", __func__, wpa_version); + + vif->wpa_enabled = true; + + if (wpa_version & NL80211_WPA_VERSION_1) + vif->auth_mode = AUTH_WPA; + else + vif->auth_mode = AUTH_WPA2; +} + +static int +ks7010_set_dot11_auth_mode(struct ks7010_vif *vif, enum nl80211_auth_type auth) +{ + ks_debug("%s: 0x%x\n", __func__, auth); + + switch (auth) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + vif->dot11_auth_mode = DOT11_AUTH_OPEN; + break; + + case NL80211_AUTHTYPE_SHARED_KEY: + vif->dot11_auth_mode = DOT11_AUTH_SHARED; + break; + + case NL80211_AUTHTYPE_AUTOMATIC: + vif->dot11_auth_mode = DOT11_AUTH_OPEN | DOT11_AUTH_SHARED; + break; + + default: + ks_err("%s: 0x%x not supported\n", __func__, auth); + return -ENOTSUPP; + } + + return 0; +} + +static int _set_cipher(struct ks7010_vif *vif, u32 cipher, bool ucast) +{ + enum hif_crypt_type *type; + size_t *size; + + type = ucast ? &vif->pairwise_crypto : &vif->group_crypto; + size = ucast ? &vif->pairwise_crypto_size : &vif->group_crypto_size; + + ks_debug("%s: cipher 0x%x, ucast %u\n", __func__, cipher, ucast); + + switch (cipher) { + case 0: + *type = CRYPT_NONE; + *size = 0; + break; + + case WLAN_CIPHER_SUITE_WEP40: + *type = CRYPT_WEP; + *size = WLAN_KEY_LEN_WEP40; + break; + + case WLAN_CIPHER_SUITE_WEP104: + *type = CRYPT_WEP; + *size = WLAN_KEY_LEN_WEP104; + break; + + case WLAN_CIPHER_SUITE_TKIP: + *type = CRYPT_TKIP; + *size = WLAN_KEY_LEN_TKIP; /* FIXME ath6kl uses 0 here? */ + break; + + case WLAN_CIPHER_SUITE_CCMP: + *type = CRYPT_AES; + *size = 0; /* FIXME what value? */ + break; + + default: + ks_err("cipher 0x%x not supported\n", cipher); + return -ENOTSUPP; + } + + return 0; +} + +static int ks7010_set_cipher_ucast(struct ks7010_vif *vif, u32 cipher) +{ + return _set_cipher(vif, cipher, true); +} + +static int ks7010_set_cipher_mcast(struct ks7010_vif *vif, u32 cipher) +{ + return _set_cipher(vif, cipher, false); +} + +static void ks7010_set_key_mgmt(struct ks7010_vif *vif, u32 key_mgmt) +{ + ks_debug("%s: 0x%x\n", __func__, key_mgmt); + + if (key_mgmt == WLAN_AKM_SUITE_PSK) { + if (vif->auth_mode == AUTH_WPA) + vif->auth_mode = AUTH_WPA_PSK; + + else if (vif->auth_mode == AUTH_WPA2) + vif->auth_mode = AUTH_WPA2_PSK; + + /* FIXME understand this */ + } else if (key_mgmt != WLAN_AKM_SUITE_8021X) { + vif->auth_mode = AUTH_NONE; + } +} + +static int +ks7010_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) +{ + struct ks7010_vif *vif = ks7010_wdev_to_vif(request->wdev); + struct ks7010 *ks = vif->ks; + struct hif_channels channels; + struct hif_ssid ssid; + int n_channels; + int i; + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + memset(&channels, 0, sizeof(channels)); + memset(&ssid, 0, sizeof(ssid)); + + vif->scan_req = request; + + n_channels = request->n_channels; + if (n_channels > HIF_MAX_CHANNELS) { + ks_warn("only scanning first %d channels of request", + HIF_MAX_CHANNELS); + n_channels = HIF_MAX_CHANNELS; + } + channels.size = n_channels; + + for (i = 0; i < channels.size; i++) { + u16 ch = request->channels[i]->center_freq; + + if (ch > MAX_U8_VAL) + ks_debug("channel overflows u8"); + + channels.list[i] = (u8)ch; + } + + if (request->n_ssids > 0) { + struct cfg80211_ssid *ptr = &request->ssids[0]; + + if (request->n_ssids > 1) { + char buf[IEEE80211_MAX_SSID_LEN + 1]; + + strncpy(buf, ptr->ssid, IEEE80211_MAX_SSID_LEN); + buf[IEEE80211_MAX_SSID_LEN] = '\0'; + + ks_warn("driver supports single SSID only, scanning %s", + buf); + } + + ssid.size = ptr->ssid_len; + /* src/dst buffers are the same size */ + memcpy(&ssid.buf[0], &ptr->ssid[0], ptr->ssid_len); + } + + /* FIXME should we be using request->rates */ + ks7010_hif_scan(ks, vif->scan_type, &channels, &ssid); + + return 0; +} + +static void _scan_event(struct ks7010_vif *vif, bool aborted) +{ + struct cfg80211_scan_info info = { + .aborted = aborted, + }; + + if (!vif->scan_req) + return; + + cfg80211_scan_done(vif->scan_req, &info); + vif->scan_req = NULL; +} + +void ks7010_cfg80211_scan_aborted(struct ks7010 *ks) +{ + _scan_event(ks->vif, false); +} + +void ks7010_cfg80211_scan_complete(struct ks7010 *ks) +{ + _scan_event(ks->vif, true); +} + +/* key handling is still a bit messy, let's document some assumptions here */ +static void _debug_add_wpa_key(struct ks7010_vif *vif, int key_index, + bool pairwise, struct key_params *params) +{ + enum hif_crypt_type key_type; + + if (params->cipher == WLAN_CIPHER_SUITE_TKIP) { + key_type = CRYPT_TKIP; + } else if (params->cipher == WLAN_CIPHER_SUITE_CCMP) { + key_type = CRYPT_AES; + } else { + ks_debug("unknown key type"); + return; + } + + if (!vif->wpa_enabled) + ks_debug("adding WPA key without WPA enabled"); + + if (key_type == CRYPT_TKIP) { + if (!(vif->auth_mode == AUTH_WPA || + vif->auth_mode == AUTH_WPA_PSK)) { + ks_debug("WPA TKIP cryto mismatch"); + } + } + + if (key_type == CRYPT_AES) { + if (!(vif->auth_mode == AUTH_WPA2 || + vif->auth_mode == AUTH_WPA2_PSK)) { + ks_debug("WPA2 AES cryto mismatch"); + } + } + + if (pairwise && key_index != 0) + ks_debug("unusual index for pairwise key (is this the PTK?)"); + + if (!pairwise && !(key_index == 1 || key_index == 2)) + ks_debug("unusual index for group key (is this the GTK?)"); +} + +/* key handling is still a bit messy, let's document some assumptions here */ +static void +_debug_add_wep_key(struct ks7010_vif *vif, int key_index, bool pairwise) +{ + if (!vif->privacy_invoked) + ks_debug("adding WEP key without WEP enabled"); + + if (pairwise && !vif->pairwise_crypto) + ks_debug("adding pairwise WEP key without cipher suite"); + + if (!pairwise && !vif->group_crypto) + ks_debug("adding group WEP key without group suite"); + + if (vif->wpa_enabled) + ks_debug("adding WEP key with WPA enabled"); +} + +static int _add_wep_key(struct ks7010_vif *vif, int key_index, + const u8 *key_val, size_t key_size) +{ + struct ks7010 *ks = vif->ks; + struct ks7010_wep_key *key; + int ret; + + if (key_index > KS7010_MAX_WEP_KEY_INDEX) { + ks_debug("key index %d out of bounds\n", key_index); + return -ENOENT; + } + + if (key_size > KS7010_WEP_KEY_MAX_SIZE) + return -EOVERFLOW; + + key = &vif->wep_keys[key_index]; + memcpy(key->key_val, key_val, key_size); + + ret = ks7010_hif_add_wep_key(ks, key_index); + if (ret) { + ks_debug("failed to add WEP key"); + return ret; + } + + return 0; +} + +static int _add_wpa_key(struct ks7010_vif *vif, int key_index, bool pairwise, + struct key_params *params) +{ + struct ks7010 *ks = vif->ks; + struct ks7010_wpa_key *key; + int ret; + + if (key_index > KS7010_MAX_WPA_KEY_INDEX) + return -EINVAL; + + if (params->key_len > WLAN_MAX_KEY_LEN) + return -EOVERFLOW; + + if (params->seq_len > KS7010_KEY_SEQ_MAX_SIZE) { + ks_debug("seq overflow"); + return -EOVERFLOW; + } + + _debug_add_wpa_key(vif, key_index, pairwise, params); + + key = &vif->wpa_keys[key_index]; + + /* FIXME what about the tx_mic_key/rx_mic_key? */ + memset(key, 0, sizeof(*key)); + + memcpy(key->key_val, params->key, params->key_len); + key->key_size = params->key_len; + + memcpy(key->seq, params->seq, params->seq_len); + key->seq_size = params->seq_len; + + key->cipher = params->cipher; + + ret = ks7010_hif_add_wpa_key(ks, key_index); + if (ret) { + ks_debug("failed to add WPA key"); + return ret; + } + + return 0; +} + +static int ks7010_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + int ret; + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + if (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104) { + if (key_index > KS7010_MAX_WEP_KEY_INDEX) { + ks_debug("WEP key index %d out of bounds\n", key_index); + return -ENOENT; + } + + _debug_add_wep_key(vif, key_index, pairwise); + ret = _add_wep_key(vif, key_index, params->key, + params->key_len); + if (ret) { + ks_debug("failed to add WEP key"); + return ret; + } + + return 0; + } + + if (params->cipher == WLAN_CIPHER_SUITE_TKIP || + params->cipher == WLAN_CIPHER_SUITE_CCMP) { + if (key_index > KS7010_MAX_WPA_KEY_INDEX) { + ks_debug("WPA key index %d out of bounds\n", key_index); + return -ENOENT; + } + + ret = _add_wpa_key(vif, key_index, pairwise, params); + if (ret) { + ks_debug("failed to add WPA key"); + return ret; + } + + return 0; + } + + ks_debug("cipher suite unsupported"); + return -ENOTSUPP; +} + +static int ks7010_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + /* FIXME is this a WEP key or a WPA key? + * firmware does not support removing of keys so the best we + * can do is clear the entry in the VIF + */ + + return 0; +} + +static int ks7010_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + struct ks7010 *ks = vif->ks; + int ret; + + if (key_index > KS7010_MAX_WEP_KEY_INDEX) { + ks_debug("key index %d out of bounds", key_index); + return -ENOENT; + } + + if (key_index > KS7010_MAX_WPA_KEY_INDEX) { + ks_debug("key index %d too big for WPA, was this a WEP key?", + key_index); + } + + ret = ks7010_hif_set_default_key(ks, key_index); + if (ret) { + ks_debug("failed to set default key"); + return ret; + } + + return 0; +} + +static int ks7010_cfg80211_get_key( + struct wiphy *wiphy, struct net_device *ndev, u8 key_index, + bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params *)) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + struct ks7010_wpa_key *key = NULL; + struct key_params params; + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + if (key_index > KS7010_MAX_WPA_KEY_INDEX) { + ks_debug("key index %d out of bounds\n", key_index); + return -ENOENT; + } + + /* FIXME is this only called for WPA keys? */ + key = &vif->wpa_keys[key_index]; + + if (key->key_size == 0) + return -ENOENT; + + memset(¶ms, 0, sizeof(params)); + params.cipher = key->cipher; + params.key_len = key->key_size; + params.seq_len = key->seq_size; + params.seq = key->seq; + params.key = key->key_val; + + callback(cookie, ¶ms); + + return 0; +} + +static int _connect_with_reconnect_flag(struct ks7010_vif *vif, + bool is_reconnect) +{ + struct ks7010 *ks = vif->ks; + int ret; + + if (is_reconnect) + ret = ks7010_hif_reconnect(ks); + else + ret = ks7010_hif_connect(ks); + + if (ret == -EINVAL) { + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + ks_debug("invalid request\n"); + + return -ENOENT; + + } else if (ret) { + return -EIO; + } + + set_bit(CONNECT_PEND, &vif->flags); + + return 0; +} + +static int _reconnect(struct ks7010_vif *vif) +{ + int ret; + + ret = _connect_with_reconnect_flag(vif, true); + if (ret) { + ks_debug("failed to reconnect"); + return ret; + } + + return 0; +} + +static int _connect(struct ks7010_vif *vif, struct cfg80211_connect_params *sme) +{ + struct ks7010 *ks = vif->ks; + u32 cipher; + int ret; + + ks7010_hif_disconnect(ks); + + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = sme->ssid_len; + memcpy(vif->ssid, sme->ssid, sme->ssid_len); + + if (sme->channel) + vif->ch_hint = sme->channel->center_freq; + + memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); + if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) + memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid)); + + if (sme->crypto.wpa_versions) + ks7010_set_wpa_version(vif, sme->crypto.wpa_versions); + + ret = ks7010_set_dot11_auth_mode(vif, sme->auth_type); + if (ret) { + ks_debug("failed to set dot11 auth mode"); + return ret; + } + + cipher = 0; + if (sme->crypto.n_ciphers_pairwise) { + if (sme->crypto.n_ciphers_pairwise > 1) + ks_debug("only using first cipher"); + + cipher = sme->crypto.ciphers_pairwise[0]; + } + + ret = ks7010_set_cipher_ucast(vif, cipher); + if (ret) { + ks_debug("failed to set ucast cipher"); + return ret; + } + + ret = ks7010_set_cipher_mcast(vif, sme->crypto.cipher_group); + if (ret) { + ks_debug("failed to set mcast cipher"); + return ret; + } + + if (sme->crypto.n_akm_suites > 0) { + if (sme->crypto.n_akm_suites > 1) + ks_debug("only using first akm cipher"); + + ks7010_set_key_mgmt(vif, sme->crypto.akm_suites[0]); + } + + /* FIXME is this correct? */ + if (sme->key_len && vif->privacy_invoked && + vif->auth_mode == AUTH_NONE && + vif->pairwise_crypto == CRYPT_WEP) { + _add_wep_key(vif, sme->key_idx, sme->key, sme->key_len); + ks7010_hif_set_default_key(ks, sme->key_idx); + } + + ret = _connect_with_reconnect_flag(vif, false); + if (ret) { + ks_debug("failed to connect"); + return ret; + } + + return 0; +} + +static int ks7010_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + bool connect_to_cur_ssid = false; + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + /* FIXME ath6kl uses a binary semaphore here? */ + + if (vif->ssid_len == sme->ssid_len && + (memcmp(vif->ssid, sme->ssid, vif->ssid_len) == 0)) + connect_to_cur_ssid = true; + + if (connect_to_cur_ssid && + test_bit(CONNECTED, &vif->flags)) { + return _reconnect(vif); + } + + return _connect(vif, sme); +} + +static int ks7010_cfg80211_disconnect( + struct wiphy *wiphy, struct net_device *ndev, u16 reason_code) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + + ks_debug("disconnect reason=%u\n", reason_code); + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + ks7010_hif_disconnect(vif->ks); + + memset(vif->ssid, 0, sizeof(vif->ssid)); + vif->ssid_len = 0; + + return 0; +} + +static int ks7010_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct ks7010 *ks = (struct ks7010 *)wiphy_priv(wiphy); + struct ks7010_vif *vif = ks->vif; + + ks_debug("%s: changed 0x%x\n", __func__, changed); + + if (!ks7010_cfg80211_ready(vif)) + return -EIO; + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + ks7010_hif_set_rts_thresh(ks, wiphy->rts_threshold); + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + ks7010_hif_set_frag_thresh(ks, wiphy->frag_threshold); + + return 0; +} + +static struct cfg80211_ops ks7010_cfg80211_ops = { + .scan = ks7010_cfg80211_scan, + .add_key = ks7010_cfg80211_add_key, + .get_key = ks7010_cfg80211_get_key, + .del_key = ks7010_cfg80211_del_key, + .set_default_key = ks7010_cfg80211_set_default_key, + .connect = ks7010_cfg80211_connect, + .disconnect = ks7010_cfg80211_disconnect, + .set_wiphy_params = ks7010_cfg80211_set_wiphy_params, +}; + +static const struct ethtool_ops ks7010_ethtool_ops = { + .get_drvinfo = cfg80211_get_drvinfo, + .get_link = ethtool_op_get_link, +}; + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +/* FIXME understand this */ +static const struct ieee80211_txrx_stypes +ks7010_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + +void ks7010_cfg80211_stop(struct ks7010_vif *vif) +{ + if (test_bit(CONNECT_PEND, &vif->flags)) { + cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } + + if (test_bit(CONNECTED, &vif->flags)) + cfg80211_disconnected(vif->ndev, 0, NULL, 0, true, GFP_KERNEL); + + clear_bit(CONNECTED, &vif->flags); + clear_bit(CONNECT_PEND, &vif->flags); + + if (vif->scan_req) + ks7010_cfg80211_scan_aborted(vif->ks); +} + +static int ks7010_cfg80211_hw_init(struct ks7010_vif *vif) +{ + struct ks7010 *ks = vif->ks; + + ks7010_hif_get_mac_addr(ks); + + /* FIXME add completion */ + + if (!ks->mac_addr_valid) + return -ENODEV; + + ks7010_hif_get_fw_version(ks); + + ks7010_hif_set_rts_thresh(ks, vif->rts_thresh); + ks7010_hif_set_frag_thresh(ks, vif->frag_thresh); + + return 0; +} + +/* called from ks7010_cfg80211_add_interface() */ +static int ks7010_cfg80211_vif_init(struct ks7010_vif *vif, + enum hif_network_type nw_type) +{ + int ret; + + vif->ssid_len = 0; + memset(vif->ssid, 0, sizeof(vif->ssid)); + + vif->nw_type = nw_type; + vif->dot11_auth_mode = DOT11_AUTH_OPEN; + + vif->auth_mode = AUTH_NONE; + vif->pairwise_crypto = CRYPT_NONE; + vif->pairwise_crypto_size = 0; + vif->group_crypto = CRYPT_NONE; + vif->group_crypto_size = 0; + vif->privacy_invoked = false; + vif->wpa_enabled = false; + + memset(vif->wep_keys, 0, sizeof(vif->wep_keys)); + memset(vif->wpa_keys, 0, sizeof(vif->wpa_keys)); + memset(vif->bssid, 0, sizeof(vif->bssid)); + + spin_lock_init(&vif->if_lock); + + vif->scan_type = BSS_SCAN_ACTIVE; + vif->tx_rate = TX_RATE_AUTO; + vif->preamble = PREAMBLE_LONG; + vif->power_mgmt = POWER_MGMT_ACTIVE; + + vif->beacon_lost_count = KS7010_DEFAULT_BEACON_LOST_COUNT; + vif->rts_thresh = KS7010_DEFAULT_RTS_THRESHOLD; + vif->frag_thresh = KS7010_DEFAULT_FRAG_THRESHOLD; + + /* FIXME default to 802.11g? */ + vif->phy_type = PHY_MODE_11BG_COMPATIBLE; + + vif->cts_mode = CTS_MODE_FALSE; + + ret = ks7010_cfg80211_hw_init(vif); + if (ret) { + ks_err("failed to init hw"); + return ret; + } + + return 0; +} + +/** + * ks7010_cfg80211_rm_interface() - Remove virtual interface. + * @ks: The ks7010 device. + * + * Caller must hold the RTNL lock. + */ +void ks7010_cfg80211_rm_interface(struct ks7010 *ks) +{ + struct ks7010_vif *vif = ks->vif; + + unregister_netdevice(vif->ndev); + + if (ks->vif) + ks->vif = NULL; +} + +/** + * ks7010_cfg80211_add_interface() - Initializes and adds a virtual interface. + * @vif: The ks7010 device virtual interface. + * @name: Device name format string, passed to alloc_netdev(). + * @name_assign_type: Origin of device name, passed to alloc_netdev(). + * @type: &enum hif_network_type network type. + * + * Caller must hold the RTNL lock. + */ +struct wireless_dev * +ks7010_cfg80211_add_interface(struct ks7010 *ks, const char *name, + unsigned char name_assign_type, + enum hif_network_type nw_type) +{ + struct net_device *ndev; + struct ks7010_vif *vif; + enum nl80211_iftype nl_iftype; + int ret; + + if (nw_type != INFRA_NETWORK) { + ks_debug("unsupported network type"); + return ERR_PTR(-EINVAL); + } + nl_iftype = NL80211_IFTYPE_STATION; + + ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, ether_setup); + if (!ndev) + return ERR_PTR(-ENOMEM); + + vif = netdev_priv(ndev); + vif->ndev = ndev; + + ks->vif = vif; + vif->ks = ks; + + ndev->ieee80211_ptr = &vif->wdev; + vif->wdev.wiphy = ks->wiphy; + SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy)); + vif->wdev.netdev = ndev; + vif->wdev.iftype = nl_iftype; + + ks7010_init_netdev(ndev); + + ret = ks7010_cfg80211_vif_init(vif, nw_type); + if (ret) + goto err_cleanup; + + netdev_set_default_ethtool_ops(ndev, &ks7010_ethtool_ops); + if (!ks->mac_addr_valid) { + ret = -ENODEV; + goto err_cleanup; + } + + ether_addr_copy(ndev->dev_addr, ks->mac_addr); + + if (register_netdevice(ndev)) { + ret = -ENODEV; + goto err_cleanup; + } + + ks->vif = vif; + + return &vif->wdev; + +err_cleanup: + ks7010_cfg80211_rm_interface(ks); + return ERR_PTR(ret); +} + +/** + * ks7010_cfg80211_init() - cfg80211 initialization. + * @ks: The ks7010 device. + */ +int ks7010_cfg80211_init(struct ks7010 *ks) +{ + struct wiphy *wiphy = ks->wiphy; + int ret; + + wiphy->mgmt_stypes = ks7010_mgmt_stypes; + + /* set device pointer for wiphy */ + set_wiphy_dev(wiphy, ks->dev); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + ret = wiphy_register(wiphy); + if (ret < 0) { + ks_err("couldn't register wiphy device\n"); + return ret; + } + + wiphy->bands[NL80211_BAND_2GHZ] = &ks7010_band_2ghz; + + wiphy->cipher_suites = cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + ks->wiphy_registered = true; + + return 0; +} + +/** + * ks7010_cfg80211_cleanup() - cfg80211 cleanup. + * @ks: The ks7010 device. + */ +void ks7010_cfg80211_cleanup(struct ks7010 *ks) +{ + wiphy_unregister(ks->wiphy); + + ks->wiphy_registered = false; +} + +/** + * ks7010_cfg80211_create() - Create wiphy. + */ +struct ks7010 *ks7010_cfg80211_create(void) +{ + struct ks7010 *ks; + struct wiphy *wiphy; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&ks7010_cfg80211_ops, sizeof(*ks)); + + if (!wiphy) { + ks_err("couldn't allocate wiphy device\n"); + return NULL; + } + + ks = wiphy_priv(wiphy); + ks->wiphy = wiphy; + + return ks; +} + +/** + * ks7010_cfg80211_destroy() - Free wiphy. + * @ks: The ks7010 device. + */ +void ks7010_cfg80211_destroy(struct ks7010 *ks) +{ + wiphy_free(ks->wiphy); +} + diff --git a/drivers/staging/ks7010/cfg80211.h b/drivers/staging/ks7010/cfg80211.h new file mode 100644 index 0000000..3a7e18c --- /dev/null +++ b/drivers/staging/ks7010/cfg80211.h @@ -0,0 +1,40 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_CFG80211_H +#define _KS7010_CFG80211_H + +#include "common.h" + +void ks7010_cfg80211_rm_interface(struct ks7010 *ks); +struct wireless_dev *ks7010_cfg80211_add_interface( + struct ks7010 *ks, const char *name, + unsigned char name_assign_type, + enum hif_network_type nw_type); + +void ks7010_cfg80211_scan_aborted(struct ks7010 *ks); +void ks7010_cfg80211_scan_complete(struct ks7010 *ks); + +void ks7010_cfg80211_stop(struct ks7010_vif *vif); + +int ks7010_cfg80211_init(struct ks7010 *ks); +void ks7010_cfg80211_cleanup(struct ks7010 *ks); + +struct ks7010 *ks7010_cfg80211_create(void); +void ks7010_cfg80211_destroy(struct ks7010 *ks); + +#endif /* _KS7010_CFG80211_H */ diff --git a/drivers/staging/ks7010/common.h b/drivers/staging/ks7010/common.h new file mode 100644 index 0000000..1c61e97 --- /dev/null +++ b/drivers/staging/ks7010/common.h @@ -0,0 +1,33 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_COMMON_H +#define _KS7010_COMMON_H + +struct ks7010; + +/* FIXME does the kernel already define these? */ +#define MAX_U16_VAL 0xFFFF +#define MAX_U8_VAL 0xFF + +#define IE_MAX_SIZE 128 + +#define KS7010_DEFAULT_BEACON_LOST_COUNT 20 +#define KS7010_DEFAULT_RTS_THRESHOLD 2347UL +#define KS7010_DEFAULT_FRAG_THRESHOLD 2346UL + +#endif /* _KS7010_COMMON_H */ diff --git a/drivers/staging/ks7010/eap.h b/drivers/staging/ks7010/eap.h new file mode 100644 index 0000000..8a343a5 --- /dev/null +++ b/drivers/staging/ks7010/eap.h @@ -0,0 +1,73 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_EAP_H +#define _KS7010_EAP_H + +/* + * FIXME does the kernel already define these? + */ + +/** + * enum protocol_id - Ethernet frame protocol identity. + * @PROTO_ID_EAPOL: EAP over LAN (802.1X) + * @PROTO_ID_IP: Internet Protocol version 4 + * @PROTO_ID_ARP: Address resolution protocol + */ +enum protocol_id { + PROTO_ID_EAPOL = 0x888e, + PROTO_ID_IP = 0x0800, + PROTO_ID_ARP = 0x0806 +}; + +#define OUI_SIZE 3 + +/** + * struct snap_hdr - EAPOL on 802.11 SNAP header. + * @dsap: Destination Service Access Point. + * @ssap: Source Service Access Point. + * @cntl: Control, set to 0x03 for Unnumbered Information. + * @oui: Organizationally Unique Identifier. + */ +struct snap_hdr { + u8 dsap; + u8 ssap; + u8 cntl; + u8 oui[OUI_SIZE]; +} __packed; + +/** + * struct fil_eap_hdr - Firmware Interface Layer EAP header + * @da: Destination MAC address. + * @sa: Source MAC address. + * @dsap: Destination Service Access Point. + * @ssap: Source Service Access Point. + * @cntl: Control, set to 0x03 for Unnumbered Information. + * @oui: Organizationally Unique Identifier. + * @type: Ethernet protocol type. + */ +struct fil_eap_hdr { + u8 *da; + u8 *sa; + u8 dsap; + u8 ssap; + u8 cntl; + u8 oui[OUI_SIZE]; + __be16 type; +} __packed; + +#endif /* _KS7010_EAP_H */ diff --git a/drivers/staging/ks7010/fil.c b/drivers/staging/ks7010/fil.c new file mode 100644 index 0000000..b103785 --- /dev/null +++ b/drivers/staging/ks7010/fil.c @@ -0,0 +1,1294 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <crypto/hash.h> +#include <uapi/linux/wireless.h> +#include <linux/skbuff.h> + +#include "ks7010.h" +#include "sdio.h" +#include "fil.h" +#include "eap.h" +#include "fil_types.h" + +/** + * DOC: Firmware Interface Layer - Set and get variables to and from + * the device firmware. + */ + +/* + * fil_t_hdr->size has different meaning depending on receive path or + * transmit path. Keep all the logic here in one place. + */ + +static __le16 tx_frame_size_to_fil_t_hdr_size(size_t frame_size) +{ + struct fil_t_hdr fhdr; + + return cpu_to_le16((u16)(frame_size - sizeof(fhdr.size))); +} + +static size_t rx_fil_t_hdr_to_frame_size(struct fil_t_hdr *fhdr) +{ + return le16_to_cpu(fhdr->size); +} + +/** + * fil_alloc_tx_frame() - Allocate a tx frame buffer. + * @frame_size: Frame size in octets. + * @event: &struct fil_t_event + * + * Allocates an aligned frame big enough to fit @frame_size + * octets. Once fil_alloc_frame() returns we do not know how much + * memory was allocated, _tx_align() recalculates the aligned size. + * + * Sets the &struct fil_t_hdr size and event members. + */ +static void *fil_alloc_tx_frame(size_t frame_size, enum fil_t_event event) +{ + struct fil_t_hdr *fhdr; + size_t aligned_size; + + aligned_size = fil_align_size(frame_size); + + if (aligned_size > MAX_U16_VAL) { + ks_err("aligning frame overflows u16: %zu", frame_size); + return NULL; + } + + fhdr = kzalloc(aligned_size, GFP_ATOMIC); + if (!fhdr) + return NULL; + + fhdr->size = tx_frame_size_to_fil_t_hdr_size(frame_size); + fhdr->event = cpu_to_le16((u16)event); + + return fhdr; +} + +static void fil_tx(struct ks7010 *ks, void *data, size_t frame_size) +{ + int ret; + size_t data_size; + + data_size = fil_align_size(frame_size); + + ret = ks7010_hif_tx(ks, data, data_size); + if (ret) + ks_debug("failed to queue tx data"); +} + +static void fil_mib_get_req(struct ks7010 *ks, enum mib_attribute attr) +{ + struct fil_t_mib_get_req *hdr; + size_t frame_size; + + frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_MIB_GET_REQ); + if (!hdr) { + ks_debug("fil_alloc_tx_frame failed for attr: %d", (int)attr); + return; + } + + hdr->attribute = cpu_to_le32(attr); + fil_tx(ks, hdr, frame_size); +} + +static void _fil_mib_set_req(struct ks7010 *ks, + enum mib_attribute attr, + enum mib_data_type type, + u8 *data, size_t data_size) +{ + struct fil_t_mib_set_req *hdr; + size_t frame_size; + + frame_size = sizeof(*hdr) + data_size; + if (frame_size > MAX_U16_VAL) { + ks_debug("u16 overflow, attr: %d size: %d", + (int)attr, (int)frame_size); + return; + } + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_MIB_SET_REQ); + if (!hdr) { + ks_debug("fil_alloc_tx_frame failed for attr: %d", (int)attr); + return; + } + + hdr->attribute = cpu_to_le32(attr); + hdr->data_size = cpu_to_le16((u16)data_size); + hdr->data_type = cpu_to_le16(type); + memcpy(&hdr->data, data, data_size); + + fil_tx(ks, hdr, frame_size); +} + +static void +fil_mib_set_req_int(struct ks7010 *ks, enum mib_attribute attr, u32 val) +{ + __le32 v = cpu_to_le32(val); + + _fil_mib_set_req(ks, attr, FIL_T_MIB_TYPE_INT, (u8 *)&v, sizeof(v)); +} + +static void +fil_mib_set_req_bool(struct ks7010 *ks, enum mib_attribute attr, bool val) +{ + __le32 v = cpu_to_le32((u32)val); + + _fil_mib_set_req(ks, attr, FIL_T_MIB_TYPE_BOOL, (u8 *)&v, sizeof(v)); +} + +static void fil_mib_set_req_ostring(struct ks7010 *ks, enum mib_attribute attr, + u8 *data, size_t data_size) +{ + _fil_mib_set_req(ks, attr, FIL_T_MIB_TYPE_OSTRING, data, data_size); +} + +static void fil_simple_req(struct ks7010 *ks, enum fil_t_event event) +{ + struct fil_t_hdr *hdr; + size_t frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, event); + if (!hdr) + return; + + fil_tx(ks, hdr, frame_size); +} + +void ks7010_fil_start(struct ks7010 *ks, enum fil_nw_type nw_type) +{ + struct fil_t_start_req *hdr; + size_t frame_size = sizeof(*hdr); + + if (nw_type != NW_TYPE_INFRA) { + ks_debug("driver supports infrastructure networks only"); + return; + } + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_START_REQ); + if (!hdr) + return; + + hdr->nw_type = cpu_to_le16((u16)nw_type); + + fil_tx(ks, hdr, frame_size); +} + +void ks7010_fil_stop(struct ks7010 *ks) +{ + fil_simple_req(ks, FIL_T_STOP_REQ); +} + +void ks7010_fil_sleep(struct ks7010 *ks) +{ + fil_simple_req(ks, FIL_T_SLEEP_REQ); +} + +void +ks7010_fil_mic_failure(struct ks7010 *ks, struct fil_mic_failure *req) +{ + struct fil_t_mic_failure_req *hdr; + size_t frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_MIC_FAILURE_REQ); + if (!hdr) + return; + + hdr->count = cpu_to_le16(req->count); + hdr->timer = cpu_to_le16(req->timer); + + fil_tx(ks, hdr, frame_size); +} + +void ks7010_fil_set_power_mgmt(struct ks7010 *ks, struct fil_power_mgmt *req) +{ + struct fil_t_power_mgmt_req *hdr; + size_t frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_POWER_MGMT_REQ); + if (!hdr) + return; + + if (req->ps_enable) + hdr->mode = cpu_to_le32(FIL_T_POWER_MGMT_MODE_SAVE); + else + hdr->mode = cpu_to_le32(FIL_T_POWER_MGMT_MODE_ACTIVE); + + if (req->wake_up) + hdr->wake_up = cpu_to_le32(FIL_T_POWER_MGMT_WAKE_UP_TRUE); + else + hdr->wake_up = cpu_to_le32(FIL_T_POWER_MGMT_WAKE_UP_FALSE); + + if (req->receive_dtims) + hdr->receive_dtims = + cpu_to_le32(FIL_T_POWER_MGMT_RECEIVE_DTIMS_TRUE); + else + hdr->receive_dtims = + cpu_to_le32(FIL_T_POWER_MGMT_RECEIVE_DTIMS_FALSE); + + fil_tx(ks, hdr, frame_size); +} + +static bool _set_infra_req_is_valid(struct fil_set_infra *req) +{ + if (req->ssid_size > FIL_T_SSID_MAX_SIZE) { + ks_debug("ssid size to big: %zu", req->ssid_size); + return false; + } + + if (req->channels_size > FIL_T_CHANNELS_MAX_SIZE) { + ks_debug("channels size to big: %zu", req->channels_size); + return false; + } + + if (req->rates_size > FIL_T_INFRA_SET_REQ_RATES_MAX_SIZE) { + ks_debug("rates size to big: %zu", req->rates_size); + return false; + } + + return true; +} + +void ks7010_fil_set_infra(struct ks7010 *ks, struct fil_set_infra *req) +{ + struct fil_t_infra_set_req *hdr; + struct _infra_set_req *ptr; + size_t frame_size = sizeof(*hdr); + + if (!_set_infra_req_is_valid(req)) + return; + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_INFRA_SET_REQ); + if (!hdr) + return; + + ptr = &hdr->req; + + ptr->phy_type = cpu_to_le16((u16)req->phy_type); + ptr->cts_mode = cpu_to_le16((u16)req->cts_mode); + ptr->scan_type = cpu_to_le16((u16)req->scan_type); + ptr->auth_type = cpu_to_le16((u16)req->auth_type); + + ptr->capability = cpu_to_le16(req->capability); + ptr->beacon_lost_count = cpu_to_le16(req->beacon_lost_count); + + memcpy(&ptr->rates.body[0], &req->rates, req->rates_size); + ptr->rates.size = req->rates_size; + + memcpy(&ptr->ssid.body[0], req->ssid, req->ssid_size); + ptr->ssid.size = req->ssid_size; + + memcpy(&ptr->channels.body[0], req->channels, req->channels_size); + ptr->channels.size = req->channels_size; + + fil_tx(ks, hdr, frame_size); +} + +void ks7010_fil_set_infra_bssid( + struct ks7010 *ks, struct fil_set_infra *req, u8 *bssid) +{ + struct fil_t_infra_set2_req *hdr; + struct _infra_set_req *ptr; + size_t frame_size = sizeof(*hdr); + + if (!_set_infra_req_is_valid(req)) + return; + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_INFRA_SET2_REQ); + if (!hdr) + return; + + ptr = &hdr->req; + + ptr->phy_type = cpu_to_le16((u16)req->phy_type); + ptr->cts_mode = cpu_to_le16((u16)req->cts_mode); + ptr->scan_type = cpu_to_le16((u16)req->scan_type); + ptr->auth_type = cpu_to_le16((u16)req->auth_type); + + ptr->capability = cpu_to_le16(req->capability); + ptr->beacon_lost_count = cpu_to_le16(req->beacon_lost_count); + + memcpy(&ptr->rates.body[0], &req->rates, req->rates_size); + ptr->rates.size = req->rates_size; + + memcpy(&ptr->ssid.body[0], req->ssid, req->ssid_size); + ptr->ssid.size = req->ssid_size; + + memcpy(&ptr->channels.body[0], req->channels, req->channels_size); + ptr->channels.size = req->channels_size; + + memcpy(hdr->bssid, bssid, ETH_ALEN); + + fil_tx(ks, hdr, frame_size); +} + +void ks7010_fil_set_mac_addr(struct ks7010 *ks, u8 *addr) +{ + fil_mib_set_req_ostring(ks, LOCAL_CURRENT_ADDRESS, addr, ETH_ALEN); +} + +#define FIL_T_MCAST_MAX_NUM_ADDRS 32 + +/** + * ks7010_fil_set_mcast_addr() - Set multicast address list. + * @ks: The ks7010 device. + * @addresses: Consecutive Ethernet addresses. + * @num_addresses: Number of addresses in @addresses. + */ +void ks7010_fil_set_mcast_addresses( + struct ks7010 *ks, u8 *addresses, int num_addresses) +{ + size_t size; + + if (num_addresses > FIL_T_MCAST_MAX_NUM_ADDRS) { + ks_debug("to many mcast addresses: %d", num_addresses); + return; + } + + size = num_addresses * ETH_ALEN; + fil_mib_set_req_ostring(ks, LOCAL_MULTICAST_ADDRESS, addresses, size); +} + +void ks7010_fil_mcast_filter_enable(struct ks7010 *ks, bool enable) +{ + fil_mib_set_req_bool(ks, LOCAL_MULTICAST_FILTER, enable); +} + +void ks7010_fil_privacy_invoked(struct ks7010 *ks, bool enable) +{ + fil_mib_set_req_bool(ks, DOT11_PRIVACY_INVOKED, enable); +} + +void ks7010_fil_set_default_key_index(struct ks7010 *ks, int idx) +{ + fil_mib_set_req_int(ks, MIB_DEFAULT_KEY_INDEX, idx); +} + +void ks7010_fil_set_key_1(struct ks7010 *ks, u8 *key, size_t key_size) +{ + fil_mib_set_req_ostring(ks, MIB_KEY_VALUE_1, key, key_size); +} + +void ks7010_fil_set_key_2(struct ks7010 *ks, u8 *key, size_t key_size) +{ + fil_mib_set_req_ostring(ks, MIB_KEY_VALUE_2, key, key_size); +} + +void ks7010_fil_set_key_3(struct ks7010 *ks, u8 *key, size_t key_size) +{ + fil_mib_set_req_ostring(ks, MIB_KEY_VALUE_3, key, key_size); +} + +void ks7010_fil_set_key_4(struct ks7010 *ks, u8 *key, size_t key_size) +{ + fil_mib_set_req_ostring(ks, MIB_KEY_VALUE_4, key, key_size); +} + +void ks7010_fil_wpa_enable(struct ks7010 *ks, bool enable) +{ + fil_mib_set_req_bool(ks, MIB_WPA_ENABLE, enable); +} + +void ks7010_fil_set_wpa_mode(struct ks7010 *ks, enum fil_wpa_mode mode) +{ + struct { + __le32 mode; + __le16 capability; + } __packed mct; + + mct.mode = cpu_to_le32((u32)mode); + mct.capability = 0; + + fil_mib_set_req_ostring(ks, MIB_WPA_MODE, (u8 *)&mct, sizeof(mct)); +} + +void ks7010_fil_set_wpa_ucast_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size) +{ + fil_mib_set_req_ostring(ks, MIB_WPA_CONFIG_UCAST_SUITE, + cipher, cipher_size); +} + +void ks7010_fil_set_wpa_mcast_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size) +{ + fil_mib_set_req_ostring(ks, MIB_WPA_CONFIG_MCAST_SUITE, + cipher, cipher_size); +} + +void ks7010_fil_set_wpa_key_mgmt_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size) +{ + fil_mib_set_req_ostring(ks, MIB_WPA_CONFIG_AUTH_SUITE, + cipher, cipher_size); +} + +void ks7010_fil_set_ptk_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size) +{ + fil_mib_set_req_ostring(ks, MIB_PTK_TSC, seq, seq_size); +} + +void ks7010_fil_set_gtk_1_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size) +{ + fil_mib_set_req_ostring(ks, MIB_GTK_1_TSC, seq, seq_size); +} + +void ks7010_fil_set_gtk_2_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size) +{ + fil_mib_set_req_ostring(ks, MIB_GTK_2_TSC, seq, seq_size); +} + +void ks7010_set_pmk(struct ks7010 *ks) +{ + /* TODO */ +} + +void ks7010_fil_set_region(struct ks7010 *ks, u32 region) +{ + fil_mib_set_req_int(ks, LOCAL_REGION, region); +} + +void ks7010_fil_set_rts_thresh(struct ks7010 *ks, u32 thresh) +{ + fil_mib_set_req_int(ks, DOT11_RTS_THRESHOLD, thresh); +} + +void ks7010_fil_set_frag_thresh(struct ks7010 *ks, u32 thresh) +{ + fil_mib_set_req_int(ks, DOT11_FRAGMENTATION_THRESHOLD, thresh); +} + +void ks7010_fil_set_gain(struct ks7010 *ks, struct fil_gain *gain) +{ + fil_mib_set_req_ostring(ks, LOCAL_GAIN, (u8 *)gain, sizeof(*gain)); +} + +void ks7010_fil_get_mac_addr(struct ks7010 *ks) +{ + fil_mib_get_req(ks, DOT11_MAC_ADDRESS); +} + +void ks7010_fil_get_fw_version(struct ks7010 *ks) +{ + fil_mib_get_req(ks, MIB_FIRMWARE_VERSION); +} + +void ks7010_fil_get_eeprom_cksum(struct ks7010 *ks) +{ + fil_mib_get_req(ks, LOCAL_EEPROM_SUM); +} + +void ks7010_fil_get_rts_thresh(struct ks7010 *ks) +{ + fil_mib_get_req(ks, DOT11_RTS_THRESHOLD); +} + +void ks7010_fil_get_frag_thresh(struct ks7010 *ks) +{ + fil_mib_get_req(ks, DOT11_FRAGMENTATION_THRESHOLD); +} + +void ks7010_fil_get_gain(struct ks7010 *ks) +{ + fil_mib_get_req(ks, LOCAL_GAIN); +} + +/** + * ks7010_fil_get_phy_info() - Get PHY information. + * @ks: The ks7010 device. + * @timer: 0 for no timer. + */ +void ks7010_fil_get_phy_info(struct ks7010 *ks, u16 timer) +{ + struct fil_t_phy_info_req *hdr; + size_t frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_PHY_INFO_REQ); + if (!hdr) + return; + + if (timer) { + hdr->type = cpu_to_le16((u16)FIL_T_PHY_INFO_TYPE_TIME); + hdr->time = cpu_to_le16(timer); + } else { + hdr->type = cpu_to_le16((u16)FIL_T_PHY_INFO_TYPE_NORMAL); + hdr->time = 0; + } + + fil_tx(ks, hdr, frame_size); +} + +static bool _scan_req_is_valid(struct fil_scan *req) +{ + if (req->ssid_size > FIL_T_SSID_MAX_SIZE) { + ks_debug("ssid size to big: %zu", req->ssid_size); + return false; + } + + if (req->channels_size > FIL_T_CHANNELS_MAX_SIZE) { + ks_debug("channels size to big: %zu", req->channels_size); + return false; + } + + return true; +} + +void ks7010_fil_scan(struct ks7010 *ks, struct fil_scan *req) +{ + struct fil_t_scan_req *hdr; + size_t frame_size = sizeof(*hdr); + + hdr = fil_alloc_tx_frame(frame_size, FIL_T_SCAN_REQ); + if (!hdr) + return; + + if (!_scan_req_is_valid(req)) + return; + + hdr->ch_time_min = cpu_to_le32((u32)FIL_T_DEFAULT_CH_TIME_MIN); + hdr->ch_time_max = cpu_to_le32((u32)FIL_T_DEFAULT_CH_TIME_MAX); + + memcpy(hdr->channels.body, req->channels, req->channels_size); + hdr->channels.size = req->channels_size; + + if (req->scan_type == FIL_SCAN_TYPE_ACTIVE) { + if (req->ssid_size) { + size_t size = req->ssid_size; + + if (size > FIL_T_SSID_MAX_SIZE) { + ks_debug("ssid too long, truncating"); + size = FIL_T_SSID_MAX_SIZE; + } + + memcpy(hdr->ssid.body, req->ssid, size); + hdr->ssid.size = (u8)size; + hdr->scan_type = FIL_SCAN_TYPE_ACTIVE; + } else { + ks_debug("no ssid, falling back to passive scan"); + hdr->scan_type = FIL_SCAN_TYPE_PASSIVE; + } + } else { + hdr->scan_type = FIL_SCAN_TYPE_PASSIVE; + } + + fil_tx(ks, hdr, frame_size); +} + +static void _fil_mib_set_conf(struct ks7010 *ks, u32 attribute) +{ + struct fil_ops *fil_ops = ks->fil_ops; + void (*callback)(struct ks7010 *ks); + + switch (attribute) { + case LOCAL_CURRENT_ADDRESS: + callback = fil_ops->set_mac_addr_conf; + break; + + case LOCAL_MULTICAST_ADDRESS: + callback = fil_ops->set_mcast_addresses_conf; + break; + + case LOCAL_MULTICAST_FILTER: + callback = fil_ops->mcast_filter_enable_conf; + break; + + case DOT11_PRIVACY_INVOKED: + callback = fil_ops->privacy_invoked_conf; + break; + + case MIB_DEFAULT_KEY_INDEX: + callback = fil_ops->set_default_key_index_conf; + break; + + case MIB_KEY_VALUE_1: + callback = fil_ops->set_key_1_conf; + break; + + case MIB_KEY_VALUE_2: + callback = fil_ops->set_key_2_conf; + break; + + case MIB_KEY_VALUE_3: + callback = fil_ops->set_key_3_conf; + break; + + case MIB_KEY_VALUE_4: + callback = fil_ops->set_key_4_conf; + break; + + case MIB_WPA_ENABLE: + callback = fil_ops->set_wpa_enable_conf; + break; + + case MIB_WPA_MODE: + callback = fil_ops->set_wpa_mode_conf; + break; + + case MIB_WPA_CONFIG_MCAST_SUITE: + callback = fil_ops->set_wpa_mcast_suite_conf; + break; + + case MIB_WPA_CONFIG_UCAST_SUITE: + callback = fil_ops->set_wpa_ucast_suite_conf; + break; + + case MIB_WPA_CONFIG_AUTH_SUITE: + callback = fil_ops->set_wpa_key_mgmt_suite_conf; + break; + + case MIB_PTK_TSC: + callback = fil_ops->set_ptk_tsc_conf; + break; + + case MIB_GTK_1_TSC: + callback = fil_ops->set_gtk_1_tsc_conf; + break; + + case MIB_GTK_2_TSC: + callback = fil_ops->set_gtk_2_tsc_conf; + break; + + case LOCAL_PMK: + callback = fil_ops->set_pmk_conf; + break; + + case LOCAL_REGION: + callback = fil_ops->set_region_conf; + break; + + case DOT11_RTS_THRESHOLD: + callback = fil_ops->set_rts_thresh_conf; + break; + + case DOT11_FRAGMENTATION_THRESHOLD: + callback = fil_ops->set_frag_thresh_conf; + break; + + case LOCAL_GAIN: + callback = fil_ops->set_gain_conf; + break; + + default: + ks_debug("unknown attribute %d", attribute); + callback = NULL; + break; + } + + if (callback) + callback(ks); +} + +static void fil_mib_set_conf(struct ks7010 *ks, struct fil_t_mib_set_conf *hdr) +{ + u32 status, attribute; + + status = le32_to_cpu(hdr->status); + attribute = le32_to_cpu(hdr->attribute); + + switch (status) { + case MIB_STATUS_INVALID: + ks_debug("invalid status for attribute %d", attribute); + break; + + case MIB_STATUS_READ_ONLY: + ks_debug("read only status for attribute %d", attribute); + break; + + case MIB_STATUS_WRITE_ONLY: + ks_debug("write only status for attribute %d", attribute); + break; + + case MIB_STATUS_SUCCESS: + _fil_mib_set_conf(ks, attribute); + + default: + ks_debug("unknown status for attribute %d", attribute); + break; + } +} + +static bool _mib_get_conf_attribute_and_type_is_valid(u32 attribute, u16 type) +{ + /* check the firmware behavior, confirm attributes match types ? */ + return 0; +} + +static void +_fil_mib_get_conf(struct ks7010 *ks, u32 attribute, u8 *data, u16 data_size) +{ + struct fil_ops *fil_ops = ks->fil_ops; + void (*callback)(struct ks7010 *ks, u8 *data, u16 data_size); + + switch (attribute) { + case DOT11_MAC_ADDRESS: + callback = fil_ops->get_mac_addr_conf; + break; + + case MIB_FIRMWARE_VERSION: + callback = fil_ops->get_fw_version_conf; + break; + + case LOCAL_EEPROM_SUM: + callback = fil_ops->get_eeprom_cksum_conf; + break; + + case DOT11_RTS_THRESHOLD: + callback = fil_ops->get_rts_thresh_conf; + break; + + case DOT11_FRAGMENTATION_THRESHOLD: + callback = fil_ops->get_frag_thresh_conf; + break; + + case LOCAL_GAIN: + callback = fil_ops->get_gain_conf; + break; + + default: + ks_debug("unknown status for attribute %d", attribute); + callback = NULL; + } + + if (callback) + callback(ks, data, data_size); +} + +static void fil_mib_get_conf(struct ks7010 *ks, struct fil_t_mib_get_conf *hdr) +{ + u32 status, attribute; + u16 data_size, type; + + status = le32_to_cpu(hdr->status); + attribute = le32_to_cpu(hdr->attribute); + data_size = le16_to_cpu(hdr->data_size); + type = le16_to_cpu(hdr->data_type); + + if (!_mib_get_conf_attribute_and_type_is_valid(attribute, type)) + return; + + switch (status) { + case MIB_STATUS_INVALID: + ks_debug("invalid status for attribute %d", attribute); + break; + + case MIB_STATUS_READ_ONLY: + ks_debug("read only status for attribute %d", attribute); + break; + + case MIB_STATUS_WRITE_ONLY: + ks_debug("write only status for attribute %d", attribute); + break; + + case MIB_STATUS_SUCCESS: + _fil_mib_get_conf(ks, attribute, hdr->data, data_size); + + default: + ks_debug("unknown status for attribute %d", attribute); + break; + } +} + +static bool _result_code_is_valid(u16 result_code) +{ + if (result_code != RESULT_SUCCESS && + result_code != RESULT_INVALID_PARAMETERS && + result_code != RESULT_NOT_SUPPORTED) { + ks_debug("unknown result_code"); + return false; + } + + return true; +} + +static void fil_result_code_conf(struct ks7010 *ks, u16 event, + struct fil_t_result_code_conf *hdr) +{ + struct fil_ops *fil_ops = ks->fil_ops; + u16 result_code = le16_to_cpu(hdr->result_code); + void (*callback)(struct ks7010 *ks, enum fil_result_code result); + + if (!_result_code_is_valid(result_code)) + return; + + switch (event) { + case FIL_T_START_CONF: + callback = fil_ops->start_conf; + break; + + case FIL_T_STOP_CONF: + callback = fil_ops->stop_conf; + break; + + case FIL_T_SLEEP_CONF: + callback = fil_ops->sleep_conf; + break; + + case FIL_T_MIC_FAILURE_CONF: + callback = fil_ops->mic_failure_conf; + break; + + case FIL_T_POWER_MGMT_CONF: + callback = fil_ops->set_power_mgmt_conf; + break; + + case FIL_T_INFRA_SET_CONF: + callback = fil_ops->set_infra_conf; + break; + + case FIL_T_INFRA_SET2_CONF: + callback = fil_ops->set_infra_bssid_conf; + break; + + default: + ks_debug("invalid event: %04X\n", event); + callback = NULL; + break; + } + + if (callback) + callback(ks, result_code); +} + +static void fil_phy_info_ind(struct ks7010 *ks, struct fil_t_phy_info_ind *le) +{ + struct fil_phy_info cpu; + + cpu.rssi = le->rssi; + cpu.signal = le->signal; + cpu.noise = le->noise; + cpu.link_speed = le->link_speed; + cpu.tx_frame = le32_to_cpu(le->tx_frame); + cpu.rx_frame = le32_to_cpu(le->rx_frame); + cpu.rx_error = le32_to_cpu(le->tx_error); + cpu.rx_error = le32_to_cpu(le->rx_error); + + ks_debug("PHY information indication received\n" + "\tRSSI: %u\n\tSignal: %u\n\tNoise: %u\n" + "\tLink Speed: %ux500Kbps\n" + "\tTransmitted Frame Count: %u\n\tReceived Frame Count: %u\n" + "\tTx Failed Count: %u\n\tFCS Error Count: %u\n", + cpu.rssi, cpu.signal, cpu.noise, cpu.link_speed, + cpu.tx_frame, cpu.rx_frame, cpu.tx_error, cpu.rx_error); + + if (ks->fil_ops->get_phy_info_ind) + ks->fil_ops->get_phy_info_ind(ks, &cpu); +} + +static void fil_phy_info_conf(struct ks7010 *ks, struct fil_t_hdr *fhdr) +{ + size_t frame_size; + + ks_debug("Firmware appears to treat phy_info_conf the same as phy_info_ind?"); + + frame_size = rx_fil_t_hdr_to_frame_size(fhdr); + if (frame_size < sizeof(struct fil_t_phy_info_ind)) { + ks_debug("received frame size is too small"); + return; + } + + ks_debug("passing fhdr to fil_phy_info_ind()"); + fil_phy_info_ind(ks, (struct fil_t_phy_info_ind *)fhdr); +} + +/* + * struct fil_scan_conf contains a 'reserved' member, keep it separate + * from the other result_code headers for documentation purposes + */ +static void fil_scan_conf(struct ks7010 *ks, struct fil_t_scan_conf *hdr) +{ + u16 result_code; + void (*callback)(struct ks7010 *ks, enum fil_result_code result); + + callback = ks->fil_ops->scan_conf; + + result_code = le16_to_cpu(hdr->result_code); + if (!_result_code_is_valid(result_code)) + return; + + if (callback) + callback(ks, result_code); +} + +static void fil_scan_ind(struct ks7010 *ks, struct fil_t_scan_ind *le) +{ + struct fil_ops *fil_ops = ks->fil_ops; + struct fil_scan_ind *cpu; + size_t size; + + if (!fil_ops->scan_ind) { + ks_debug("fil_ops->scan_ind is NULL"); + return; + } + + cpu = kzalloc(sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return; + + ether_addr_copy(cpu->bssid, le->bssid); + + cpu->rssi = le->rssi; + cpu->signal = le->signal; + cpu->noise = le->noise; + cpu->channel = le->channel; + + cpu->beacon_period = le16_to_cpu(le->beacon_period); + cpu->capability = le16_to_cpu(le->capability); + + if (le->frame_type == FIL_T_FRAME_TYPE_PROBE_RESP) { + cpu->type = FRAME_TYPE_PROBE_RESP; + + } else if (le->frame_type == FIL_T_FRAME_TYPE_BEACON) { + cpu->type = FRAME_TYPE_BEACON; + + } else { + ks_debug("frame type is not a scan indication frame"); + return; + } + + size = le16_to_cpu(le->body_size); + memcpy(cpu->body, le->body, size); + cpu->body_size = size; + + fil_ops->scan_ind(ks, cpu); +} + +static void +_conn_ind_copy_ie(struct fil_conn_ind *cpu, struct fil_t_conn_ind *le) +{ + size_t size; + + size = le->ies.size < IE_MAX_SIZE ? le->ies.size : IE_MAX_SIZE; + memcpy(cpu->ie, le->ies.body, size); + cpu->ie_size = size; +} + +static void fil_conn_ind(struct ks7010 *ks, struct fil_t_conn_ind *le) +{ + struct fil_ops *fil_ops = ks->fil_ops; + struct fil_conn_ind cpu; + u16 conn_code; + size_t size; + + if (!fil_ops->conn_ind) { + ks_debug("fil_ops->conn_ind is NULL"); + return; + } + + conn_code = le16_to_cpu(le->conn_code); + if (conn_code != CONN_CODE_CONNECT && + conn_code != CONN_CODE_DISCONNECT) { + ks_debug("conn_code invalid"); + return; + } + cpu.code = conn_code; + + ether_addr_copy(cpu.bssid, le->bssid); + + cpu.rssi = le->rssi; + cpu.signal = le->signal; + cpu.noise = le->noise; + cpu.channel = le->ds.channel; + + cpu.beacon_period = le16_to_cpu(le->beacon_period); + cpu.capability = le16_to_cpu(le->capability); + + size = le->rates.size; + memcpy(cpu.rates, le->rates.body, size); + cpu.rates_size = size; + + if (le->ext_rates.size > 0) { + size_t size, available; + u8 *ptr; + + available = KS7010_RATES_MAX_SIZE - cpu.rates_size; + size = le->ext_rates.size; + if (size > available) { + ks_debug("ext rates don't all fit"); + size = available; + } + + ptr = &cpu.rates[cpu.rates_size]; + memcpy(ptr, le->ext_rates.body, size); + cpu.rates_size += size; + } + + if (le->wpa_mode == FIL_WPA_MODE_WPA) { + cpu.element_id = ELEMENT_ID_WPA; + _conn_ind_copy_ie(&cpu, le); + } + + if (le->wpa_mode == FIL_WPA_MODE_RSN) { + cpu.element_id = ELEMENT_ID_RSN; + _conn_ind_copy_ie(&cpu, le); + } + + fil_ops->conn_ind(ks, &cpu); +} + +static void fil_assoc_ind(struct ks7010 *ks, struct fil_t_assoc_ind *le) +{ + struct fil_ops *fil_ops = ks->fil_ops; + struct fil_assoc_ind cpu; + u8 type; + + if (!fil_ops->assoc_ind) { + ks_debug("fil_ops->assoc_ind is NULL"); + return; + } + + memset(&cpu, 0, sizeof(cpu)); + + type = le->req.type; + if (type != FIL_T_FRAME_TYPE_ASSOC_REQ && + type != FIL_T_FRAME_TYPE_REASSOC_REQ) { + ks_debug("assoc req frame type is invalid"); + return; + } + cpu.req.type = type; + + cpu.req.capability = le16_to_cpu(le->req.capability); + cpu.req.listen_interval = le16_to_cpu(le->req.listen_interval); + ether_addr_copy(cpu.req.ap_addr, le->req.ap_addr); + cpu.req.ie_size = (size_t)le16_to_cpu(le->req.ie_size); + cpu.req.ie = le->ies; + + type = le->resp.type; + if (type != FIL_T_FRAME_TYPE_ASSOC_RESP && + type != FIL_T_FRAME_TYPE_REASSOC_RESP) { + ks_debug("assoc resp frame type is invalid"); + return; + } + cpu.resp.type = type; + + cpu.resp.capability = le16_to_cpu(le->resp.capability); + cpu.resp.status = le16_to_cpu(le->resp.status); + cpu.resp.assoc_id = le16_to_cpu(le->resp.assoc_id); + cpu.resp.ie_size = (size_t)le16_to_cpu(le->resp.ie_size); + + cpu.resp.ie = le->ies + cpu.req.ie_size; + + fil_ops->assoc_ind(ks, &cpu); +} + +static void fil_data_ind(struct ks7010 *ks, struct fil_t_data_ind *le) +{ + struct fil_ops *fil_ops = ks->fil_ops; + u16 auth_type; + int key_index; + size_t frame_size; + size_t data_size; + u8 *data; + + if (!fil_ops->data_ind) { + ks_debug("fil_ops->data_ind is NULL"); + return; + } + + auth_type = le16_to_cpu(le->auth_type); + + if (auth_type != AUTH_TYPE_PTK && + auth_type != AUTH_TYPE_GTK1 && + auth_type != AUTH_TYPE_GTK2) { + ks_debug("auth type is invalid"); + return; + } + + key_index = auth_type - 1; + frame_size = le16_to_cpu(le->fhdr.size); + data_size = frame_size - sizeof(*le); + data = le->data; + + fil_ops->data_ind(ks, key_index, data, data_size); +} + +static void fil_event_check(struct ks7010 *ks, struct fil_t_hdr *fhdr) +{ + u16 event = le16_to_cpu(fhdr->event); + + switch (event) { + case FIL_T_START_CONF: + case FIL_T_STOP_CONF: + case FIL_T_SLEEP_CONF: + case FIL_T_MIC_FAILURE_CONF: + case FIL_T_POWER_MGMT_CONF: + case FIL_T_INFRA_SET_CONF: + case FIL_T_INFRA_SET2_CONF: + fil_result_code_conf(ks, event, + (struct fil_t_result_code_conf *)fhdr); + break; + + case FIL_T_MIB_SET_CONF: + fil_mib_set_conf(ks, (struct fil_t_mib_set_conf *)fhdr); + break; + + case FIL_T_MIB_GET_CONF: + fil_mib_get_conf(ks, (struct fil_t_mib_get_conf *)fhdr); + break; + + case FIL_T_PHY_INFO_CONF: + fil_phy_info_conf(ks, fhdr); + break; + + case FIL_T_PHY_INFO_IND: + fil_phy_info_ind(ks, (struct fil_t_phy_info_ind *)fhdr); + break; + + case FIL_T_SCAN_CONF: + fil_scan_conf(ks, (struct fil_t_scan_conf *)fhdr); + break; + + case FIL_T_SCAN_IND: + fil_scan_ind(ks, (struct fil_t_scan_ind *)fhdr); + break; + + case FIL_T_CONNECT_IND: + fil_conn_ind(ks, (struct fil_t_conn_ind *)fhdr); + break; + + case FIL_T_ASSOC_IND: + fil_assoc_ind(ks, (struct fil_t_assoc_ind *)fhdr); + break; + + case FIL_T_DATA_IND: + fil_data_ind(ks, (struct fil_t_data_ind *)fhdr); + break; + + default: + ks_debug("undefined MIB event: %04X\n", event); + break; + } +} + +static const struct snap_hdr SNAP = { + .dsap = 0xAA, + .ssap = 0xAA, + .cntl = 0x03 + /* OUI is all zero */ +}; + +/** + * ks7010_fil_tx() - Build FIL tx frame. + * @ks: The ks7010 device. + * @skb: sk_buff from networking stack. + * @type: Type of frame to build, &enum fil_tx_type. + * @txd: Return argument for frame data (and size). + */ +int ks7010_fil_tx(struct ks7010 *ks, struct sk_buff *skb, + enum fil_tx_type type, struct tx_data *txd) +{ + struct ethhdr *eh; + struct fil_eap_hdr *fh; + struct fil_t_data_req *hdr; + size_t max_frame_size; + size_t frame_size; + u16 proto; + size_t size; + u8 *src_ptr, *dst_ptr; + int i; + + memset(txd, 0, sizeof(*txd)); + + if (skb->len < ETH_HLEN) + return -EINVAL; + + /* hdr->size must be updated after the frame is built */ + max_frame_size = + sizeof(*hdr) + (sizeof(*fh) - sizeof(eh)) + skb->len; + + hdr = fil_alloc_tx_frame(max_frame_size, FIL_T_DATA_REQ); + if (!hdr) + return -ENOMEM; + + eh = (struct ethhdr *)skb->data; + proto = ntohs(eh->h_proto); + frame_size = 0; + + if (proto >= ETH_P_802_3_MIN) { + fh = (struct fil_eap_hdr *)hdr->data; + + ether_addr_copy(fh->da, eh->h_dest); + ether_addr_copy(fh->sa, eh->h_source); + + fh->dsap = SNAP.dsap; + fh->ssap = SNAP.ssap; + fh->cntl = SNAP.cntl; + for (i = 0; i < OUI_SIZE; i++) + fh->oui[i] = 0; + fh->type = eh->h_proto; + + frame_size += sizeof(*fh); + dst_ptr = (u8 *)(fh + 1); + } else { /* DIX */ + dst_ptr = hdr->data; + + ether_addr_copy(dst_ptr, eh->h_dest); + frame_size += ETH_ALEN; + dst_ptr += ETH_ALEN; + + ether_addr_copy(dst_ptr, eh->h_source); + frame_size += ETH_ALEN; + dst_ptr += ETH_ALEN; + } + + src_ptr = (u8 *)(eh + 1); + size = skb->len - sizeof(*eh); + memcpy(dst_ptr, src_ptr, size); + frame_size += size; + + if (type == TX_TYPE_AUTH) + hdr->type = cpu_to_le16((u16)FIL_T_DATA_REQ_TYPE_AUTH); + else + hdr->type = cpu_to_le16((u16)FIL_T_DATA_REQ_TYPE_DATA); + + /* update hif_hdr size now we know the final packet size */ + hdr->fhdr.size = tx_frame_size_to_fil_t_hdr_size(frame_size); + + /* pass frame data back */ + txd->datap = (u8 *)hdr; + txd->size = fil_align_size(frame_size); + + return 0; +} + +/** + * ks7010_fil_rx() - FIL response to an rx event. + * @ks: The ks7010 device. + * @data: The rx data. + * @data_size: Size of data. + * + * Called by the rx interrupt bottom half tasklet to respond to an rx event. + */ +int ks7010_fil_rx(struct ks7010 *ks, u8 *data, size_t data_size) +{ + struct fil_t_hdr *fhdr; + u16 size; + + fhdr = (struct fil_t_hdr *)data; + size = le16_to_cpu(fhdr->size); + + if (data_size != size) { + ks_debug("rx size mismatch"); + return -EINVAL; + } + + fil_event_check(ks, fhdr); + + return 0; +} diff --git a/drivers/staging/ks7010/fil.h b/drivers/staging/ks7010/fil.h new file mode 100644 index 0000000..c4e03dd --- /dev/null +++ b/drivers/staging/ks7010/fil.h @@ -0,0 +1,559 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_FIL_H +#define _KS7010_FIL_H + +#include <linux/compiler.h> +#include <linux/skbuff.h> +#include <linux/ieee80211.h> +#include <linux/skbuff.h> + +#include "common.h" + +/** + * fil_nw_type - Network type + * @NW_TYPE_INFRA: Infrastructure networks. + * @NW_TYPE_ADHOC: Not implemented. + */ +enum fil_nw_type { + NW_TYPE_INFRA, + /* No other network types implemented yet */ + NW_TYPE_ADHOC +}; + +/** + * enum fil_wpa_mode - Wi-Fi Protected Access modes. + * @FIL_WPA_MODE_NONE: WPA not enabled. + * @FIL_WPA_MODE_WPA: WPA version 1. + * @FIL_WPA_MODE_RSN: WPA version 2. + */ +enum fil_wpa_mode { + FIL_WPA_MODE_NONE = 0, + FIL_WPA_MODE_WPA, + FIL_WPA_MODE_RSN +}; + +/** + * enum fil_scan_type - Scan type. + * @FIL_SCAN_TYPE_ACTIVE: Use probe request frames it identify networks. + * @FIL_SCAN_TYPE_PASSIVE: Identify networks by listening for beacons. + */ +enum fil_scan_type { + FIL_SCAN_TYPE_ACTIVE = 0, + FIL_SCAN_TYPE_PASSIVE +}; + +/** + * struct fil_scan - Data required to initiate a scan. + * @scan_type: &enum fil_scan_type + * @ssid: SSID to scan. + * @ssid_size: Size of SSID. + * @channels: List of channels to scan. + * @channels_size: Size (number) of channels in list. + */ +struct fil_scan { + enum fil_scan_type scan_type; + u8 *ssid; + size_t ssid_size; + u8 *channels; + size_t channels_size; +}; + +/* FIXME 802.11g is backward compatible with b? */ +enum fil_phy_type { + FIL_PYH_TYPE_11B_ONLY = 0, + FIL_PYH_TYPE_11G_ONLY, + FIL_PYH_TYPE_11BG_COMPATIBLE, +}; + +/** + * enum fil_cts_mode - Clear to send mode + * @FIL_CTS_MODE_FALSE: TODO document this + * @FIL_CTS_MODE_TRUE: TODO document this + */ +enum fil_cts_mode { + FIL_CTS_MODE_FALSE = 0, + FIL_CTS_MODE_TRUE +}; + +/** + * enum fil_dot11_auth_type - 802.11 Authentication. + * @FIL_DOT11_AUTH_TYPE_OPEN_SYSTEM: Open system authentication. + * @FIL_DOT11_AUTH_TYPE_SHARED_KEY: Shared key authentication. + */ +enum fil_dot11_auth_type { + FIL_DOT11_AUTH_TYPE_OPEN_SYSTEM = 0, + FIL_DOT11_AUTH_TYPE_SHARED_KEY +}; + +/** + * enum fil_bss_capability_flags - Basic service set capabilities. + * @BSS_CAP_ESS: Extended service set (mutually exclusive with IBSS). + * @BSS_CAP_IBSS: Independent service set (mutually exclusive with ESS). + * @BSS_CAP_CF_POLABLE: Contention free polling bits. + * @BSS_CAP_CF_POLL_REQ: Contention free polling bits. + * @BSS_CAP_PRIVACY: Privacy, bit set indicates WEP required. + * @BSS_CAP_SHORT_PREAMBLE: Bit on for short preamble. 802.11g always + * uses short preamble. + * @BSS_CAP_PBCC: Packet binary convolution coding modulation scheme. + * @BSS_CAP_CHANNEL_AGILITY: Bit on for channel agility. + * @BSS_CAP_SHORT_SLOT_TIME: Short slot time (802.11g). + * @BSS_CAP_DSSS_OFDM: DSSS-OFDM frame construction (802.11g). + */ +enum fil_bss_capability_flags { + BSS_CAP_ESS = 0, + BSS_CAP_IBSS = 1, + BSS_CAP_CF_POLABLE = 2, + BSS_CAP_CF_POLL_REQ = 3, + BSS_CAP_PRIVACY = 4, + BSS_CAP_SHORT_PREAMBLE = 5, + BSS_CAP_PBCC = 6, + BSS_CAP_CHANNEL_AGILITY = 7, + BSS_CAP_SHORT_SLOT_TIME = 10, + BSS_CAP_DSSS_OFDM = 13 +}; + +/** + * struct fil_set_infra - Data required to set network type to infrastructure. + * @phy_type: &enum fil_phy_type + * @cts_mode: &enum fil_cts_mode + * @scan_type: &enum fil_scan_type + * @auth_type: &enum fil_dot11_auth_type + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @beacon_lost_count: TODO document this + * @rates: Operational rates list. + * @rates_size: Size of rates list. + * @ssid: Service set identifier. + * @ssid_size: Size of SSID. + * @channels: Channel list. + * @channels_size: Size of channel list. + * @bssid: Basic service set identifier. + */ +struct fil_set_infra { + enum fil_phy_type phy_type; + enum fil_cts_mode cts_mode; + enum fil_scan_type scan_type; + enum fil_dot11_auth_type auth_type; + + u16 capability; + u16 beacon_lost_count; + + u8 *rates; + size_t rates_size; + + u8 *ssid; + size_t ssid_size; + + u8 *channels; + size_t channels_size; + + u8 *bssid; +}; + +/** + * struct fil_power_mgmt - Data for device power management. + * @ps_enable: Enable power save. + * @wake_up: TODO verify what this does (see comment in fil_types.h). + * @receive_dtims: Periodically wake up to receive DTIM's. + */ +struct fil_power_mgmt { + bool ps_enable; + bool wake_up; + bool receive_dtims; +}; + +/** + * struct fil_gain - TODO document this + */ +struct fil_gain { + u8 tx_mode; + u8 rx_mode; + u8 tx_gain; + u8 rx_gain; +}; + +/** + * struct fil_t_mic_failure_req - Michael MIC failure event frame. + * @fhdr: &struct fil_t_hdr + * @count: Notify firmware that this is failure number @count. + * @timer: Number of jiffies since the last failure. + * + * Michael Message Integrity Check must be done by the driver, in the + * event of a failure use this frame type to notify the firmware of + * the failure. + */ +struct fil_mic_failure { + u16 count; + u16 timer; +}; + +/* TODO document fil_phy_info (same as fil_t_phy_info_ind) */ + +/** + * struct fil_phy_info - PHY information. + * @rssi: Received signal strength indication. + * @signal: + * @noise: + * @link_speed: + * @tx_frame: + * @rx_frame: + * @tx_error: + * @rx_error: + */ +struct fil_phy_info { + u8 rssi; + u8 signal; + u8 noise; + u8 link_speed; + u32 tx_frame; + u32 rx_frame; + u32 tx_error; + u32 rx_error; +}; + +/** + * enum frame_type - Scan response frame type. + * @FRAME_TYPE_PROBE_RESP: Frame returned in response to a probe + * request (active scan). + * @FRAME_TYPE_BEACON: Frame beacon type. + */ +enum frame_type { + FRAME_TYPE_PROBE_RESP, + FRAME_TYPE_BEACON +}; + +#define FIL_AP_INFO_MAX_SIZE 1024 + +/** + * struct fil_scan_ind - Data received from firmware after scan completes. + * @bssid: Basic service set identifier. + * @rssi: Received signal strength indication. + * @signal: TODO document this + * @noise: TODO document this + * @channel: Channel for scanned network. + * @beacon_period: Beacon period (interval) in time units. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @type: Probe response or beacon, &enum frame_type. + * @body_size: Size of @body in octets. + * @body: Scan indication data, made up of consecutive &struct fil_ap_info. + */ +struct fil_scan_ind { + u8 bssid[ETH_ALEN]; + u8 rssi; + u8 signal; + u8 noise; + u8 channel; + u16 beacon_period; + u16 capability; + enum frame_type type; + + size_t body_size; + u8 body[FIL_AP_INFO_MAX_SIZE]; +}; + +/** + * struct fil_ap_info - Information element. + * @element_id: Information element identifier. + * @data_size: Size if IE + * @data: IE data. + */ +struct fil_ap_info { + u8 element_id; + u8 data_size; + u8 data[0]; +}; + +/* + * FIXME these are constants define by 802.11, does the kernel + * define these already? + */ +enum element_id { + ELEMENT_ID_RSN = 0x30, + ELEMENT_ID_WPA = 0xdd +}; + +/** + * enum conn_code - Connection code type. + * @CONN_CODE_CONNECT: Connection. + * @CONN_CODE_DISCONNECT: Disconnection. + */ +enum conn_code { + CONN_CODE_CONNECT = 0, + CONN_CODE_DISCONNECT, +}; + +#define KS7010_RATES_MAX_SIZE 16 +#define KS7010_IE_MAX_SIZE 128 + +/** + * struct fil_conn_ind - Data received from firmware on connection. + * @bssid: Basic service set identifier. + * @rssi: Received signal strength indication. + * @signal: TODO document this + * @noise: TODO document this + * @channel: Network channel. + * @beacon_period: Beacon period (interval) in time units. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @rates_size: Size of rate set. + * @rates: List of rates supported by connected network. + * @element_id: IE identifier. + * @ie_size: Size of data in IE's. + * @ie: Information elements. + */ +struct fil_conn_ind { + enum conn_code code; + u8 bssid[ETH_ALEN]; + u8 rssi; + u8 signal; + u8 noise; + u8 channel; + + u16 beacon_period; + u16 capability; + + u8 rates_size; + u8 rates[KS7010_RATES_MAX_SIZE]; + + enum element_id element_id; + size_t ie_size; + u8 ie[KS7010_IE_MAX_SIZE]; +}; + +/** + * enum assoc_type - + * @ASSOC_TYPE_ASSOC: Association type. + * @ASSOC_TYPE_REASSOC: Re-association type. + */ +enum assoc_type { + ASSOC_TYPE_ASSOC, + ASSOC_TYPE_REASSOC +}; + +/** + * struct fil_assoc_ind_req_info - Association request information. + * @type: &enum assoc_type + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @listen_interval: Listen interval. + * @ap_addr: Current access point MAC address. + * @ie_size: Number of octets in IE. + * @ie: Information elements. + */ +struct fil_assoc_ind_req_info { + enum assoc_type type; + u16 capability; + u16 listen_interval; + u8 ap_addr[ETH_ALEN]; + size_t ie_size; + u8 *ie; +}; + +/** + * struct fil_assoc_ind_resp_info - Association response information. + * @type: &enum assoc_type + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @status: TODO unknown. + * @assoc_id: Association identifier. + * @ie_size: Number of octets in IE. + * @ie: Information elements. + */ +struct fil_assoc_ind_resp_info { + enum assoc_type type; + u16 capability; + u16 status; + u16 assoc_id; + size_t ie_size; + u8 *ie; +}; + +/** + * struct fil_assoc_ind - Data received from firmware on association. + * @req: &struct fil_assoc_ind_req + * @resp: &struct fil_assoc_ind_resp + */ +struct fil_assoc_ind { + struct fil_assoc_ind_req_info req; + struct fil_assoc_ind_resp_info resp; +}; + +/** + * enum fil_tx_type - Tx frame type. + * @TX_TYPE_AUTH: Authentication frame type. + * @TX_TYPE_DATA: Data frame type. + */ +enum fil_tx_type { + TX_TYPE_AUTH, + TX_TYPE_DATA +}; + +/** + * struct fil_tx_data - Data required to initiate a transmission. + * @da: Destination MAC address. + * @sa: Source MAC address. + * @proto: Ethernet protocol. + * @add_snap_hdr_to_frame: True if frame should include LLC and SNAP headers. + * @add_protocol_to_frame: True if frame should include the protocol. + * @type: Authentication/data frame, &enum fil_tx_type. + * @data: Frame data. + * @data_size: Frame data size. + * @skb: Pointer to the sk_buff passed down from networking stack. + */ +struct fil_tx_data { + u8 *da; + u8 *sa; + u16 proto; + enum fil_tx_type type; + u8 *data; + size_t data_size; + struct sk_buff *skb; +}; + +/** + * enum fil_result_code - FIL result code. + * @RESULT_SUCCESS: Firmware request successful. + * @RESULT_INVALID_PARAMETERS: Firmware request failed, invalid parameters. + * @RESULT_NOT_SUPPORTED: Request not supported by firmware. + */ +enum fil_result_code { + RESULT_SUCCESS = 0, + RESULT_INVALID_PARAMETERS, + RESULT_NOT_SUPPORTED, +}; + +/** + * struct fil_ops - Firmware Interface Layer callbacks. + * @start_conf: Confirmation of ks7010_fil_start(). + */ +struct fil_ops { + void (*start_conf)(struct ks7010 *ks, enum fil_result_code result); + void (*stop_conf)(struct ks7010 *ks, enum fil_result_code result); + void (*sleep_conf)(struct ks7010 *ks, enum fil_result_code result); + void (*mic_failure_conf)(struct ks7010 *ks, + enum fil_result_code result); + void (*set_power_mgmt_conf)(struct ks7010 *ks, + enum fil_result_code result); + void (*set_infra_conf)(struct ks7010 *ks, enum fil_result_code result); + void (*set_infra_bssid_conf)(struct ks7010 *ks, + enum fil_result_code result); + + void (*set_mac_addr_conf)(struct ks7010 *ks); + void (*set_mcast_addresses_conf)(struct ks7010 *ks); + void (*mcast_filter_enable_conf)(struct ks7010 *ks); + void (*privacy_invoked_conf)(struct ks7010 *ks); + void (*set_default_key_index_conf)(struct ks7010 *ks); + void (*set_key_1_conf)(struct ks7010 *ks); + void (*set_key_2_conf)(struct ks7010 *ks); + void (*set_key_3_conf)(struct ks7010 *ks); + void (*set_key_4_conf)(struct ks7010 *ks); + void (*set_wpa_enable_conf)(struct ks7010 *ks); + void (*set_wpa_mode_conf)(struct ks7010 *ks); + void (*set_wpa_ucast_suite_conf)(struct ks7010 *ks); + void (*set_wpa_mcast_suite_conf)(struct ks7010 *ks); + void (*set_wpa_key_mgmt_suite_conf)(struct ks7010 *ks); + void (*set_ptk_tsc_conf)(struct ks7010 *ks); + void (*set_gtk_1_tsc_conf)(struct ks7010 *ks); + void (*set_gtk_2_tsc_conf)(struct ks7010 *ks); + void (*set_pmk_conf)(struct ks7010 *ks); /* TODO */ + void (*set_region_conf)(struct ks7010 *ks); + void (*set_rts_thresh_conf)(struct ks7010 *ks); + void (*set_frag_thresh_conf)(struct ks7010 *ks); + void (*set_gain_conf)(struct ks7010 *ks); + + void (*get_mac_addr_conf)(struct ks7010 *ks, u8 *data, u16 size); + void (*get_fw_version_conf)(struct ks7010 *ks, u8 *data, u16 size); + void (*get_eeprom_cksum_conf)(struct ks7010 *ks, u8 *data, u16 size); + void (*get_rts_thresh_conf)(struct ks7010 *ks, u8 *data, u16 size); + void (*get_frag_thresh_conf)(struct ks7010 *ks, u8 *data, u16 size); + void (*get_gain_conf)(struct ks7010 *ks, u8 *data, u16 size); + + void (*get_phy_info_ind)(struct ks7010 *ks, struct fil_phy_info *ind); + + void (*scan_conf)(struct ks7010 *ks, enum fil_result_code result); + + void (*scan_ind)(struct ks7010 *ks, struct fil_scan_ind *ind); + + /* FIXME understand how connection and association are initiated */ + void (*conn_ind)(struct ks7010 *ks, struct fil_conn_ind *ind); + void (*assoc_ind)(struct ks7010 *ks, struct fil_assoc_ind *ind); + + void (*data_ind)(struct ks7010 *ks, int key_index, + u8 *data, size_t data_size); +}; + +void ks7010_fil_start(struct ks7010 *ks, enum fil_nw_type type); +void ks7010_fil_stop(struct ks7010 *ks); +void ks7010_fil_sleep(struct ks7010 *ks); + +void ks7010_fil_mic_failure(struct ks7010 *ks, struct fil_mic_failure *req); + +void ks7010_fil_set_power_mgmt(struct ks7010 *ks, struct fil_power_mgmt *req); + +void ks7010_fil_set_infra(struct ks7010 *ks, struct fil_set_infra *req); +void ks7010_fil_set_infra_bssid(struct ks7010 *ks, + struct fil_set_infra *req, u8 *bssid); + +void ks7010_fil_set_mac_addr(struct ks7010 *ks, u8 *addr); +void ks7010_fil_set_mcast_addresses(struct ks7010 *ks, + u8 *addresses, int num_addresses); +void ks7010_fil_mcast_filter_enable(struct ks7010 *ks, bool enable); + +void ks7010_fil_privacy_invoked(struct ks7010 *ks, bool enable); +void ks7010_fil_set_default_key_index(struct ks7010 *ks, int index); + +void ks7010_fil_set_key_1(struct ks7010 *ks, u8 *key, size_t key_size); +void ks7010_fil_set_key_2(struct ks7010 *ks, u8 *key, size_t key_size); +void ks7010_fil_set_key_3(struct ks7010 *ks, u8 *key, size_t key_size); +void ks7010_fil_set_key_4(struct ks7010 *ks, u8 *key, size_t key_size); + +void ks7010_fil_wpa_enable(struct ks7010 *ks, bool enable); +void ks7010_fil_set_wpa_mode(struct ks7010 *ks, enum fil_wpa_mode mode); + +void ks7010_fil_set_wpa_ucast_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size); +void ks7010_fil_set_wpa_mcast_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size); +void ks7010_fil_set_wpa_key_mgmt_suite(struct ks7010 *ks, u8 *cipher, + size_t cipher_size); + +void ks7010_fil_set_ptk_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size); +void ks7010_fil_set_gtk_1_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size); +void ks7010_fil_set_gtk_2_tsc(struct ks7010 *ks, u8 *seq, size_t seq_size); + +void ks7010_set_pmk(struct ks7010 *ks); /* TODO */ + +void ks7010_fil_set_region(struct ks7010 *ks, u32 region); +void ks7010_fil_set_rts_thresh(struct ks7010 *ks, u32 thresh); +void ks7010_fil_set_frag_thresh(struct ks7010 *ks, u32 thresh); +void ks7010_fil_set_gain(struct ks7010 *ks, struct fil_gain *gain); + +void ks7010_fil_get_mac_addr(struct ks7010 *ks); +void ks7010_fil_get_fw_version(struct ks7010 *ks); +void ks7010_fil_get_eeprom_cksum(struct ks7010 *ks); + +void ks7010_fil_get_rts_thresh(struct ks7010 *ks); +void ks7010_fil_get_frag_thresh(struct ks7010 *ks); +void ks7010_fil_get_gain(struct ks7010 *ks); + +void ks7010_fil_get_phy_info(struct ks7010 *ks, u16 timer); +void ks7010_fil_scan(struct ks7010 *ks, struct fil_scan *req); + +int ks7010_fil_tx(struct ks7010 *ks, struct sk_buff *skb, + enum fil_tx_type type, struct tx_data *txd); + +int ks7010_fil_rx(struct ks7010 *ks, u8 *data, size_t data_size); + +#endif /* _KS7010_FIL_H */ diff --git a/drivers/staging/ks7010/fil_types.h b/drivers/staging/ks7010/fil_types.h new file mode 100644 index 0000000..6e77a2b --- /dev/null +++ b/drivers/staging/ks7010/fil_types.h @@ -0,0 +1,851 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * DOC: Internal types for the Firmware Interface Layer. + */ + +#define KS7010_SDIO_ALIGN 32 + +/** + * fil_align_size() - Device alignment. + * @size: size to align. + */ +static inline size_t fil_align_size(size_t size) +{ + /* FIXME can we use kernel macro ALIGN here */ + if (size % KS7010_SDIO_ALIGN) + return size + KS7010_SDIO_ALIGN - (size % KS7010_SDIO_ALIGN); + + return size; +} + +/** + * struct fil_t_hdr - Firmware Interface Layer header. + * + * @size: Value is tx/rx dependent. + * @event: &enum fil_t_event + * + * Do not access size manually, use helper functions. + * tx_fil_hdr_to_frame_size() + * tx_frame_size_to_fil_hdr_size() + * rx_fil_hdr_to_frame_size() + * rx_frame_size_to_fil_hdr_size() + */ +struct fil_t_hdr { + __le16 size; + __le16 event; +} __packed; + +/** + * enum fil_t_event - Host interface events + * + * Events include; + * - get/set requests, i.e commands to the target. + * - confirmation and indication events. + * + * @FIL_T_MIB_SET_REQ: Management Information Base set request. + * @FIL_T_MIB_GET_REQ: Management Information Base get request. + */ +enum fil_t_event { + FIL_T_DATA_REQ = 0xE001, + FIL_T_MIB_GET_REQ = 0xE002, + FIL_T_MIB_SET_REQ = 0xE003, + FIL_T_POWER_MGMT_REQ = 0xE004, + FIL_T_START_REQ = 0xE005, + FIL_T_STOP_REQ = 0xE006, + /* FIL_T_PS_ADH_SET_REQ = 0xE007, */ + FIL_T_INFRA_SET_REQ = 0xE008, + /* FIL_T_ADH_SET_REQ = 0xE009, */ + /* FIL_T_ADH_SET2_REQ = 0xE010, */ + /* FIL_T_AP_SET_REQ = 0xE00A, */ + FIL_T_MIC_FAILURE_REQ = 0xE00B, + FIL_T_SCAN_REQ = 0xE00C, + FIL_T_PHY_INFO_REQ = 0xE00D, + FIL_T_SLEEP_REQ = 0xE00E, + FIL_T_INFRA_SET2_REQ = 0xE00F, + + FIL_T_REQ_MAX = 0xE010, + + FIL_T_DATA_IND = 0xE801, + FIL_T_MIB_GET_CONF = 0xE802, + FIL_T_MIB_SET_CONF = 0xE803, + FIL_T_POWER_MGMT_CONF = 0xE804, + FIL_T_START_CONF = 0xE805, + FIL_T_CONNECT_IND = 0xE806, + FIL_T_STOP_CONF = 0xE807, + /* FIL_T_PS_ADH_SET_CONF= 0xE808, */ + FIL_T_INFRA_SET_CONF = 0xE809, + /* FIL_T_ADH_SET_CONF = 0xE80A, */ + /* FIL_T_AP_SET_CONF = 0xE80B, */ + FIL_T_ASSOC_IND = 0xE80C, + FIL_T_MIC_FAILURE_CONF = 0xE80D, + FIL_T_SCAN_CONF = 0xE80E, + FIL_T_PHY_INFO_CONF = 0xE80F, + FIL_T_SLEEP_CONF = 0xE810, + FIL_T_PHY_INFO_IND = 0xE811, + FIL_T_SCAN_IND = 0xE812, + FIL_T_INFRA_SET2_CONF = 0xE813, + /* FIL_T_ADH_SET2_CONF = 0xE814, */ +}; + +/** + * struct fil_t_mib_get_req - Management Information Base get request frame. + * @fhdr: &struct fil_t_hdr. + * @attribute: &enum mib_attribute + */ +struct fil_t_mib_get_req { + struct fil_t_hdr fhdr; + __le32 attribute; +} __packed; + +/** + * struct fil_t_mib_set_req - Management Information Base set request frame. + * @fhdr: &struct fil_t_hdr. + * @attribute: &enum mib_attribute + * @data_size: Size of data in octets. + * @type: &enum mib_data_type. + * @data: MIB request data. + */ +struct fil_t_mib_set_req { + struct fil_t_hdr fhdr; + __le32 attribute; + __le16 data_size; + __le16 data_type; + u8 data[0]; +} __packed; + +/** + * enum mib_attribute - Management Information Base attribute. + * + * Attribute value used for accessing and updating the + * Management Information Base, set/get req/ind. + * + * R is read only. + * W is write only. + * R/W is read and write. + * + * @DOT11_MAC_ADDRESS: MAC Address (R) + * @MIB_FIRMWARE_VERSION: FirmWare Version (R) + * @LOCAL_EEPROM_SUM: EEPROM checksum information (R) + * + * @LOCAL_CURRENT_ADDRESS: MAC Address change (W) + * + * @LOCAL_MULTICAST_ADDRESS: Multicast address (W) + * @LOCAL_MULTICAST_FILTER: Multicast filter enable/disable (W) + * + * @DOT11_PRIVACY_INVOKED: Use encryption (WEP/WPA/RSN) + * + * @MIB_DEFAULT_KEY_INDEX: WEP key index or WPA txkey (W) + * @MIB_KEY_VALUE_1: WEP Key 1 or TKIP/CCMP PTK (W) + * @MIB_KEY_VALUE_2: WEP Key 2 or TKIP/CCMP GTK 1 (W) + * @MIB_KEY_VALUE_3: WEP Key 3 or TKIP/CCMP GTK 2 (W) + * @MIB_KEY_VALUE_4: WEP Key 4 (not currently used for TKIP/CCMP) (W) + + * @MIB_WPA_ENABLE: WPA/RSN enable/disable (W) + * @MIB_WPA_MODE: WPA or RSN (W) + * @MIB_WPA_CONFIG_UCAST_SUITE: Pairwise key cipher suite (W) + * @MIB_WPA_CONFIG_MCAST_SUITE: Group key cipher suite (W) + * @MIB_WPA_CONFIG_AUTH_SUITE: Authentication key management suite (W) + * @MIB_PTK_TSC: PTK sequence counter (W) + * @MIB_GTK_1_TSC: GTK 1 sequence counter (W) + * @MIB_GTK_2_TSC: GTK 2 sequence counter (W) + * + * @LOCAL_PMK: Pairwise Master Key cache (W) + * + * @LOCAL_REGION: Region setting (W) + * + * @DOT11_RTS_THRESHOLD: Request To Send Threshold (R/W) + * @DOT11_FRAGMENTATION_THRESHOLD: Fragment Threshold (R/W) + * @LOCAL_GAIN: Carrier sense threshold for demo ato show (R/W) + * + * @DOT11_WEP_LIST: + * @DOT11_DESIRED_SSID: + * @DOT11_CURRENT_CHANNEL: + * @DOT11_OPERATION_RATE_SET: + * @LOCAL_AP_SEARCH_INTEAVAL: + * @LOCAL_SEARCHED_AP_LIST: + * @LOCAL_LINK_AP_STATUS: + * @LOCAL_PACKET_STATISTICS: + * @LOCAL_AP_SCAN_LIST_TYPE_SET: + * @DOT11_RSN_CONFIG_VERSION: + * @LOCAL_RSN_CONFIG_ALL: + * @DOT11_GMK3_TSC: + */ +enum mib_attribute { + DOT11_MAC_ADDRESS = 0x21010100, + MIB_FIRMWARE_VERSION = 0x31024100, + LOCAL_EEPROM_SUM = 0xF10E0100, + + LOCAL_CURRENT_ADDRESS = 0xF1050100, + + LOCAL_MULTICAST_ADDRESS = 0xF1060100, + LOCAL_MULTICAST_FILTER = 0xF1060200, + + DOT11_PRIVACY_INVOKED = 0x15010100, + MIB_DEFAULT_KEY_INDEX = 0x15020100, + + MIB_KEY_VALUE_1 = 0x13020101, + MIB_KEY_VALUE_2 = 0x13020102, + MIB_KEY_VALUE_3 = 0x13020103, + MIB_KEY_VALUE_4 = 0x13020104, + + MIB_WPA_ENABLE = 0x15070100, + MIB_WPA_MODE = 0x56010100, + MIB_WPA_CONFIG_UCAST_SUITE = 0x52020100, + MIB_WPA_CONFIG_MCAST_SUITE = 0x51040100, + MIB_WPA_CONFIG_AUTH_SUITE = 0x53020100, + + MIB_PTK_TSC = 0x55010100, + MIB_GTK_1_TSC = 0x55010101, + MIB_GTK_2_TSC = 0x55010102, + + LOCAL_PMK = 0x58010100, + + LOCAL_REGION = 0xF10A0100, + + DOT11_RTS_THRESHOLD = 0x21020100, + DOT11_FRAGMENTATION_THRESHOLD = 0x21050100, + LOCAL_GAIN = 0xF10D0100, + + /* unused */ + DOT11_WEP_LIST = 0x13020100, + DOT11_RSN_CONFIG_VERSION = 0x51020100, + LOCAL_RSN_CONFIG_ALL = 0x5F010100, + DOT11_DESIRED_SSID = 0x11090100, + DOT11_CURRENT_CHANNEL = 0x45010100, + DOT11_OPERATION_RATE_SET = 0x11110100, + LOCAL_AP_SEARCH_INTEAVAL = 0xF1010100, + LOCAL_SEARCHED_AP_LIST = 0xF1030100, + LOCAL_LINK_AP_STATUS = 0xF1040100, + LOCAL_PACKET_STATISTICS = 0xF1020100, + LOCAL_AP_SCAN_LIST_TYPE_SET = 0xF1030200, + DOT11_GMK3_TSC = 0x55010103 +}; + +/** + * enum mib_type - Message Information Base data type. + * @FIL_T_MIB_TYPE_NULL: Null type + * @FIL_T_MIB_TYPE_INT: Integer type + * @FIL_T_MIB_TYPE_BOOL: Boolean type + * @FIL_T_MIB_TYPE_COUNT32: unused + * @FIL_T_MIB_TYPE_OSTRING: Memory chunk + */ +enum mib_data_type { + FIL_T_MIB_TYPE_NULL = 0, + FIL_T_MIB_TYPE_INT, + FIL_T_MIB_TYPE_BOOL, + FIL_T_MIB_TYPE_COUNT32, + FIL_T_MIB_TYPE_OSTRING, +}; + +/** + * struct fil_t_phy_info_req - PHY information request frame. + * @fhdr: &struct fil_t_hdr + * @type: &enum fil_t_phy_info_type + * @time: unit 100ms + */ +struct fil_t_phy_info_req { + struct fil_t_hdr fhdr; + __le16 type; + __le16 time; +} __packed; + +/** + * enum fil_t_phy_info_type - TODO document this enum + * @FIL_T_PHY_INFO_TYPE_NORMAL: + * @FIL_T_PHY_INFO_TYPE_TIME: + */ +enum fil_t_phy_info_type { + FIL_T_PHY_INFO_TYPE_NORMAL = 0, + FIL_T_PHY_INFO_TYPE_TIME, +}; + +/** + * struct fil_t_start_req - Start request frame. + * @fhdr: &struct fil_t_hdr + * @nw_type: &enum fil_t_nw_type + */ +struct fil_t_start_req { + struct fil_t_hdr fhdr; + __le16 nw_type; +} __packed; + +/** + * enum fil_t_nw_type - Network type. + * @FIL_T_NW_TYPE_PSEUDO_ADHOC: Pseudo adhoc mode. + * @FIL_T_NW_TYPE_INFRASTRUCTURE: Infrastructure mode. + * @FIL_T_NW_TYPE_AP: Access point mode, not supported. + * @FIL_T_NW_TYPE_ADHOC: Adhoc mode. + */ +enum fil_t_nw_type { + FIL_T_NW_TYPE_PSEUDO_ADHOC = 0, + FIL_T_NW_TYPE_INFRASTRUCTURE, + FIL_T_NW_TYPE_AP, + FIL_T_NW_TYPE_ADHOC +}; + +/** + * struct fil_t_power_mgmt_req - Power management request frame. + * @fhdr: &struct fil_t_hdr + * @mode: enum fil_t_power_mgmt_mode + * @wake_up: enum fil_t_power_mgmt_wake_up + * @receive_dtims: enum fil_t_power_mgmt_receive_dtims + */ +struct fil_t_power_mgmt_req { + struct fil_t_hdr fhdr; + __le32 mode; + __le32 wake_up; + __le32 receive_dtims; +} __packed; + +/** + * enum fil_t_power_mgmt_mode - Power management mode. + * @FIL_T_POWER_MGMT_MODE_ACTIVE: Disable power management, device + * may not sleep. + * @FIL_T_POWER_MGMT_MODE_SAVE: Enable power management, used for + * 'sleep' mode and 'deep sleep' mode. + */ +enum fil_t_power_mgmt_mode { + FIL_T_POWER_MGMT_MODE_ACTIVE = 1, + FIL_T_POWER_MGMT_MODE_SAVE +}; + +/** + * enum fil_t_power_mgmt_wake_up - Wake up the device if it is asleep. + * @FIL_T_POWER_MGMT_WAKE_UP_FALSE: + * @FIL_T_POWER_MGMT_WAKE_UP_TRUE: + * + * Variable is unused in original Renesas open source driver, we have + * no indication of its purpose except the name. + * + * TODO test device and verify variables usage. + */ +enum fil_t_power_mgmt_wake_up { + FIL_T_POWER_MGMT_WAKE_UP_FALSE = 0, + FIL_T_POWER_MGMT_WAKE_UP_TRUE +}; + +/** + * enum fil_t_power_mgmt_receive_dtims - Receive DTIM's + * @FIL_T_POWER_MGMT_RECEIVE_DTIMS_FALSE: Do not wake up to receive DTIM. + * @FIL_T_POWER_MGMT_RECEIVE_DTIMS_TRUE: Wake up periodically to receive DTIM. + */ +enum fil_t_power_mgmt_receive_dtims { + FIL_T_POWER_MGMT_RECEIVE_DTIMS_FALSE = 0, + FIL_T_POWER_MGMT_RECEIVE_DTIMS_TRUE +}; + +#define FIL_T_CHANNELS_MAX_SIZE 14 + +/** + * struct fil_t_channels - Channel list + * @size: Size of list, i.e number of channels. + * @body: List data. + * @pad: Unused, structure padding. + * + * Each channel number is a single octet. + */ +struct fil_t_channels { + u8 size; + u8 body[FIL_T_CHANNELS_MAX_SIZE]; + u8 pad; +} __packed; + +#define FIL_T_SSID_MAX_SIZE 32 + +/** + * struct fil_t_ssid - Service Set Identity + * @size: Size of SSID in octets. + * @body: SSID data. + * @pad: Unused, structure padding. + */ +struct fil_t_ssid { + u8 size; + u8 body[FIL_T_SSID_MAX_SIZE]; + u8 pad; +} __packed; + +/** + * enum fil_t_default_channel_time - Default channel times. + * @FIL_T_DEFAULT_CH_TIME_MIN: Default minimum time. + * @FIL_T_DEFAULT_CH_TIME_MAX: Default maximum time. + */ +enum fil_t_default_channel_time { + FIL_T_DEFAULT_CH_TIME_MIN = 110, + FIL_T_DEFAULT_CH_TIME_MAX = 130 +}; + +/** + * struct fil_t_scan_req - Scan request frame. + * @fhdr: &struct fil_t_hdr + * @scan_type: &enum fil_scan_type + * @pad: Unused, structure padding. + * @ch_time_min: Minimum scan time per channel in time units. + * @ch_time_max: Maximum scan time per channel in time units. + * @channels: List of channels to scan, &struct fil_t_channels. + * @ssid: SSID used during scan, &struct fil_t_ssid. + */ +struct fil_t_scan_req { + struct fil_t_hdr fhdr; + u8 scan_type; + u8 pad[3]; + __le32 ch_time_min; + __le32 ch_time_max; + struct fil_t_channels channels; + struct fil_t_ssid ssid; +} __packed; + +#define FIL_T_INFRA_SET_REQ_RATES_MAX_SIZE 16 + +/** + * struct fil_t_rates - List of rates. + * @size: Size of list, i.e number of rates. + * @body: List data. + * @pad: Unused, structure padding. + * + * Each rate number is a single octet. + */ +struct fil_t_rates { + u8 size; + u8 body[FIL_T_INFRA_SET_REQ_RATES_MAX_SIZE]; + u8 pad; +} __packed; + +/** + * struct _infra_set_req - Network type infrastructure request frame. + * @fhdr: &struct fil_t_hdr + * @phy_type: &enum fil_phy_type + * @cts_mode: &enum cts_mode + * @rates: Supported data rates, &struct fil_t_rates + * @ssid: SSID, &struct fil_t_ssid + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @beacon_lost_count: TODO document this + * @auth_type: &enum fil_dot11_auth_type + * @channels: &struct fil_t_channels + * @scan_type: &enum fil_scan_type + */ +struct _infra_set_req { + struct fil_t_hdr fhdr; + __le16 phy_type; + __le16 cts_mode; + struct fil_t_rates rates; + struct fil_t_ssid ssid; + __le16 capability; + __le16 beacon_lost_count; + __le16 auth_type; + struct fil_t_channels channels; + __le16 scan_type; +} __packed; + +/** + * struct fil_t_infra_set_req - Set BSS mode without specifying the BSSID + * @req: &struct _infra_set_req + */ +struct fil_t_infra_set_req { + struct _infra_set_req req; +} __packed; + +/** + * struct fil_t_infra_set_req - Set BSS mode specifying the BSSID + * @req: &struct _infra_set_req + * @bssid: BSSID to use for request. + */ +struct fil_t_infra_set2_req { + struct _infra_set_req req; + u8 bssid[ETH_ALEN]; +} __packed; + +/** + * struct fil_t_mic_failure_req - Michael MIC failure event frame. + * @fhdr: &struct fil_t_hdr + * @count: Notify firmware that this is failure number @count. + * @timer: Number of jiffies since the last failure. + * + * Michael Message Integrity Check must be done by the driver, in the + * event of a failure use this frame type to notify the firmware of + * the failure. + */ +struct fil_t_mic_failure_req { + struct fil_t_hdr fhdr; + __le16 count; + __le16 timer; +} __packed; + +/** + * struct fil_t_data_req - Tx data and auth frames. + * @fhdr: &struct fil_t_hdr + * @type: &enum data_req_type. + * @reserved: Unused, reserved. + * @data: Upper layer data. + * + * Frame used when building tx frames out of sk_buff passed down from + * networking stack, used for data frames and authentication frames. + */ +struct fil_t_data_req { + struct fil_t_hdr fhdr; + __le16 type; + __le16 reserved; + u8 data[0]; +} __packed; + +/** + * enum fil_data_req_type - Tx frame. + * @FIL_DATA_REQ_TYPE_DATA: Data requests frame. + * @FIL_DATA_REQ_TYTE_AUTH: Data authentication frame. + */ +enum fil_t_data_req_type { + FIL_T_DATA_REQ_TYPE_DATA = 0x0000, + FIL_T_DATA_REQ_TYPE_AUTH +}; + +/** + * struct fil_t_data_ind - Rx frame. + * @fhdr: &struct fil_t_hdr + * @auth_type: &struct data_ind_auth_type. + * @reserved: Unused, reserved. + * @data: Rx data. + */ +struct fil_t_data_ind { + struct fil_t_hdr fhdr; + __le16 auth_type; + __le16 reserved; + u8 data[0]; +} __packed; + +/** + * enum data_ind_auth_type - Key used for encryption. + * @AUTH_TYPE_PTK: Pairwise Transient Key + * @AUTH_TYPE_GTK1: Group Transient Key 1 + * @AUTH_TYPE_GTK2: Group Transient Key 2 + */ +enum data_ind_auth_type { + AUTH_TYPE_PTK = 0x0001, + AUTH_TYPE_GTK1, + AUTH_TYPE_GTK2 +}; + +/** + * struct fil_t_mib_set_conf - 'MIB set' confirmation frame. + * @fhdr: &struct fil_t_hdr + * @status: &enum mib_status + * @attribute: &enum mib_attribute + */ +struct fil_t_mib_set_conf { + struct fil_t_hdr fhdr; + __le32 status; + __le32 attribute; +} __packed; + +/** + * struct fil_t_mib_get_conf - 'MIB get' confirmation frame. + * @fhdr: &struct fil_t_hdr + * @status: &enum mib_status + * @attribute: &enum mib_attribute + * @data_size: Size of @data in octets. + * @data_type: &enum mib_data_type + */ +struct fil_t_mib_get_conf { + struct fil_t_hdr fhdr; + __le32 status; + __le32 attribute; + __le16 data_size; + __le16 data_type; + u8 data[0]; +} __packed; + +/** + * enum mib_status - Result status of a MIB get/set request. + * @MIB_STATUS_SUCCESS: Request successful. + * @MIB_STATUS_INVALID: Request invalid. + * @MIB_STATUS_READ_ONLY: Request failed, attribute is read only. + * @MIB_STATUS_WRITE_ONLY: Request failed, attribute is write only. + */ +enum mib_status { + MIB_STATUS_SUCCESS = 0, + MIB_STATUS_INVALID, + MIB_STATUS_READ_ONLY, + MIB_STATUS_WRITE_ONLY, +}; + +/** + * struct fil_t_result_code_conf - Generic confirmation frame. + * @fhdr: &struct fil_t_hdr + * @relust_code: &struct fil_t_result_code + */ +struct fil_t_result_code_conf { + struct fil_t_hdr fhdr; + __le16 result_code; +} __packed; + +/* TODO document struct fil_t_phy_info_ind */ + +/** + * struct fil_t_phy_info_ind - PHY information frame. + * @fhdr: &struct fil_t_hdr + * @rssi: Received signal strength indication. + * @signal: + * @noise: + * @link_speed: + * @tx_frame: + * @rx_frame: + * @tx_error: + * @rx_error: + */ +struct fil_t_phy_info_ind { + struct fil_t_hdr fhdr; + u8 rssi; + u8 signal; + u8 noise; + u8 link_speed; + __le32 tx_frame; + __le32 rx_frame; + __le32 tx_error; + __le32 rx_error; +} __packed; + +/** + * struct fil_t_scan_conf - Scan confirmation frame. + * @fhdr: &struct fil_t_hdr + * @relust_code: &struct fil_t_result_code + * @reserved: Unused, reserved. + */ +struct fil_t_scan_conf { + struct fil_t_hdr fhdr; + __le16 result_code; + __le16 reserved; +} __packed; + +/* TODO document struct fil_t_phy_info_ind */ + +#define FIL_T_AP_INFO_MAX_SIZE + +/** + * struct fil_t_scan_ind - Scan result information frame. + * @fhdr: &struct fil_t_hdr + * @bssid: Basic service set identifier. + * @rssi: Received signal strength indication. + * @signal: + * @noise: + * @pad0: Unused, structure padding. + * @beacon_period: Beacon period (interval) in time units. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @frame_type: &enum fil_t_scan_ind_frame_type + * @channel: Channel to use. + * @body_size: Size of @body in octets. + * @body: Scan indication data, made up of consecutive &struct fil_ap_info. + */ +struct fil_t_scan_ind { + struct fil_t_hdr fhdr; + u8 bssid[ETH_ALEN]; + u8 rssi; + u8 signal; + u8 noise; + u8 pad0; + __le16 beacon_period; + __le16 capability; + u8 frame_type; + u8 channel; + __le16 body_size; + u8 body[FIL_AP_INFO_MAX_SIZE]; +} __packed; + +/** + * enum fil_t_scan_ind_frame_type - FIL scan frame type. + * @FIL_T_FRAME_TYPE_PROBE_RESP: Probe response frame type. + * @FIL_T_FRAME_TYPE_BEACON: Beacon frame type. + */ +enum fil_t_scan_ind_frame_type { + FIL_T_FRAME_TYPE_PROBE_RESP = 0x50, + FIL_T_FRAME_TYPE_BEACON = 0x80 +}; + +#define FIL_T_IE_MAX_SIZE 128 +#define FIL_T_CONN_IND_RATES_MAX_SIZE 8 + +/** + * struct fil_t_conn_ind - Connection event indication frame. + * @fhdr: &struct fil_t_hdr + * @conn_code: &struct fil_conn_code + * @bssid: Basic service set identifier. + * @rssi: Received signal strength indication. + * @signal: TODO document this + * @noise: TODO document this + * @pad0: Unused, structure padding. + * @beacon_period: Beacon period (interval) in time units. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @rates: List of supported data rates. + * @fh: Frequency hopping parameters. + * @ds: Direct sequence parameters. + * @cf: Contention free parameters. + * @ibss: Adhoc network parameters. + * @erp: Extended rate PHY parameters. + * @pad1: Unused, structure padding. + * @ext_rates: Extended rates list. + * @dtim_period: Delivery traffic indication map period. + * @wpa_mode: &struct fil_wpa_mode. + * @ies: Information elements + */ +struct fil_t_conn_ind { + struct fil_t_hdr fhdr; + __le16 conn_code; + u8 bssid[ETH_ALEN]; + u8 rssi; + u8 signal; + u8 noise; + u8 pad0; + __le16 beacon_period; + __le16 capability; + + struct { + u8 size; + u8 body[FIL_T_CONN_IND_RATES_MAX_SIZE]; + u8 pad; + } __packed rates; + + struct { + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; + } __packed fh; + + struct { + u8 channel; + } __packed ds; + + struct { + u8 count; + u8 period; + __le16 max_duration; + __le16 dur_remaining; + } __packed cf; + + struct { + __le16 atim_window; + } __packed ibss; + + struct { + u8 info; + } __packed erp; + + u8 pad1; + + struct { + u8 size; + u8 body[FIL_T_CONN_IND_RATES_MAX_SIZE]; + u8 pad; + } __packed ext_rates; + + u8 dtim_period; + u8 wpa_mode; + + struct { + u8 size; + u8 body[FIL_T_IE_MAX_SIZE]; + } __packed ies; +} __packed; + +/** + * struct fil_t_assoc_ind_req_info - Association event request information. + * @type: &enum fil_t_assoc_req_frame_type + * @pad: Unused, structure padding. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @listen_interval: Management frame listen interval. + * @ap_addr: Current access point MAC address. + * @ie_size: Number of octets in the request portion of the + * information elements data. + */ +struct fil_t_assoc_ind_req_info { + u8 type; + u8 pad; + __le16 capability; + __le16 listen_interval; + u8 ap_addr[ETH_ALEN]; + __le16 ie_size; +} __packed; + +/** + * enum fil_t_assoc_req_frame_type - Association request frame type. + * @FIL_T_FRAME_TYPE_ASSOC_REQ: Association request frame type. + * @FIL_T_FRAME_TYPE_REASSOC_REQ: Re-association request frame type. + */ +enum fil_t_assoc_req_frame_type { + FIL_T_FRAME_TYPE_ASSOC_REQ = 0x00, + FIL_T_FRAME_TYPE_REASSOC_REQ = 0x20 +}; + +/** + * struct fil_t_assoc_ind_resp_info - Association event response information. + * @type: &enum fil_t_assoc_resp_frame_type + * @pad: Unused, structure padding. + * @capability: Network capability flags, &enum fil_bss_capability_flags. + * @status: No known information. Most likely this is a subset of + * the 802.11 fixed-length management frame 'status' field. + * @assoc_id: Management frame association identifier. + * @ie_size: Number of octets in the request portion of the + * information elements data. + */ +struct fil_t_assoc_ind_resp_info { + u8 type; + u8 pad; + __le16 capability; + __le16 status; + __le16 assoc_id; + __le16 ie_size; +} __packed; + +/** + * enum fil_t_assoc_resp_frame_type - Association response frame type. + * @FIL_T_FRAME_TYPE_ASSOC_RESP: Association response frame type. + * @FIL_T_FRAME_TYPE_REASSOC_RESP: Re-association response frame type. + */ +enum fil_t_assoc_resp_frame_type { + FIL_T_FRAME_TYPE_ASSOC_RESP = 0x10, + FIL_T_FRAME_TYPE_REASSOC_RESP = 0x30 +}; + +/** + * struct fil_t_assoc_ind - y + * @fhdr: &struct fil_t_hdr + * @req: &struct fil_t_assoc_ind_req_info + * @resp: &struct fil_t_assoc_ind_resp_info + * @ies: Consecutive information elements, @req IE's followed by @resp IE's. + */ +struct fil_t_assoc_ind { + struct fil_t_hdr fhdr; + struct fil_t_assoc_ind_req_info req; + struct fil_t_assoc_ind_resp_info resp; + u8 ies[0]; + /* followed by (req->ie_size + resp->ie_size) octets of data */ +} __packed; + +/** + * struct fil_eth_hdr - Firmware Interface Layer Ethernet frame header. + * @h_dest: Destination MAC address. + * @h_source: Source MAC address. + * @snap: SNAP header. + * @h_proto: Protocol ID. + * @data: Upper layer data. + */ +struct fil_eth_hdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + struct snap_hdr snap; + __be16 h_proto; + u8 data[0]; +}; diff --git a/drivers/staging/ks7010/hif.c b/drivers/staging/ks7010/hif.c new file mode 100644 index 0000000..508c94d --- /dev/null +++ b/drivers/staging/ks7010/hif.c @@ -0,0 +1,505 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/kernel.h> +#include <crypto/hash.h> +#include <uapi/linux/wireless.h> + +#include "ks7010.h" +#include "hif.h" +#include "fil.h" +#include "cfg80211.h" + +/** + * DOC: Host Interface Layer - Provides abstraction layer on top of + * Firmware Interface Layer. When interfacing with the device FIL + * provides the mechanism, HIF provides the policy. + */ + +static bool _ptk_available(struct ks7010 *ks) +{ + return ks->vif->wpa_keys[PTK_IDX].key_size > 0; +} + +static bool _gtks_available(struct ks7010 *ks) +{ + if (ks->vif->wpa_keys[GTK1_IDX].key_size > 0 && + ks->vif->wpa_keys[GTK2_IDX].key_size > 0) + return true; + + return false; +} + +/** + * ks7010_hif_tx_start() - HIF initiate transmission. + * @ks: The ks7010 device. + * @skb: sk_buff from networking stack. + * @txd: Passed to FIL as return argument. + */ +int +ks7010_hif_tx_start(struct ks7010 *ks, struct sk_buff *skb, struct tx_data *txd) +{ + struct ethhdr *eth; + u16 proto; + enum fil_tx_type type = TX_TYPE_DATA; + bool have_ptk; + bool have_gtks; + bool wpa; + int ret; + + eth = (struct ethhdr *)skb->data; + proto = ntohs(eth->h_proto); + + have_ptk = _ptk_available(ks); + have_gtks = _gtks_available(ks); + + wpa = ks->vif->wpa_enabled && have_ptk; + if (wpa) { + if (proto == ETH_P_PAE && !have_gtks) + type = TX_TYPE_AUTH; + else + ;/* TODO handle TKIP and CCMP */ + + } else if (proto == ETH_P_PAE) { + type = TX_TYPE_AUTH; + } else { + type = TX_TYPE_DATA; + } + + ret = ks7010_fil_tx(ks, skb, type, txd); + if (ret) + return ret; + + return 0; +} + +/** + * ks7010_hif_tx() - Transmit a frame from FIL. + * @ks: The ks7010 device. + * @data: The tx data. + * @data_size: Size of data. + * + * Called by the FIL to send a tx frame to the device. + */ +int ks7010_hif_tx(struct ks7010 *ks, u8 *data, size_t data_size) +{ + return ks7010_tx_enqueue(ks, data, data_size); +} + +/** + * ks7010_hif_rx() - HIF response to an rx event. + * @ks: The ks7010 device. + * @data: The rx data. + * @data_size: Size of data. + */ +void ks7010_hif_rx(struct ks7010 *ks, u8 *data, size_t data_size) +{ + ks7010_fil_rx(ks, data, data_size); +} + +/** + * ks7010_hif_get_mac_addr() - Get the MAC address. + * @ks: The ks7010 device. + */ +void ks7010_hif_get_mac_addr(struct ks7010 *ks) +{ + ks7010_fil_get_mac_addr(ks); +} + +static void hif_get_mac_addr_conf(struct ks7010 *ks, u8 *data, u16 size) +{ + if (size != ETH_ALEN) { + ks_debug("MAC address size error"); + return; + } + + ether_addr_copy(ks->mac_addr, data); + ks->mac_addr_valid = true; +} + +/** + * ks7010_hif_get_fw_version() - Get the firmware version. + * @ks: The ks7010 device. + */ +void ks7010_hif_get_fw_version(struct ks7010 *ks) +{ + ks7010_fil_get_fw_version(ks); +} + +static void hif_get_fw_version_conf(struct ks7010 *ks, u8 *data, u16 size) +{ + if (size > ETHTOOL_FWVERS_LEN) { + ks_debug("firmware version too big"); + return; + } + + memcpy(ks->fw_version, data, size); + ks->fw_version_len = size; +} + +/** + * ks7010_hif_set_rts_thresh() - Set the RTS threshold. + * @ks: The ks7010 device. + * @thresh: The new request to send threshold. + */ +void ks7010_hif_set_rts_thresh(struct ks7010 *ks, u32 thresh) +{ + if (thresh > IEEE80211_MAX_RTS_THRESHOLD) { + ks_debug("threshold maximum exceeded, not setting threshold"); + return; + } + + ks7010_fil_set_rts_thresh(ks, thresh); +} + +static void hif_get_rts_thresh_conf(struct ks7010 *ks, u8 *data, u16 size) +{ + /* TODO convert data to threshold value */ + ks_debug("firmware returned %d bytes", size); +} + +/** + * ks7010_hif_set_frag_thresh() - Set the fragmentation threshold. + * @ks: The ks7010 device. + * @thresh: The new fragmentation threshold. + */ +void ks7010_hif_set_frag_thresh(struct ks7010 *ks, u32 thresh) +{ + if (thresh > IEEE80211_MAX_FRAG_THRESHOLD) { + ks_debug("threshold maximum exceeded, not setting threshold"); + return; + } + + ks7010_fil_set_frag_thresh(ks, thresh); +} + +static void hif_get_frag_thresh_conf(struct ks7010 *ks, u8 *data, u16 size) +{ + /* TODO convert data to threshold value */ + ks_debug("firmware returned %d bytes", size); +} + +/** + * ks7010_hif_connect() - Initiate network connection. + * @ks: The ks7010 device. + */ +int ks7010_hif_connect(struct ks7010 *ks) +{ + /* TODO interface connect with firmware */ + return 0; +} + +/** + * ks7010_hif_reconnect() - Initiate network re-connection. + * @ks: The ks7010 device. + */ +int ks7010_hif_reconnect(struct ks7010 *ks) +{ + /* TODO interface re-connect with firmware */ + return 0; +} + +/** + * ks7010_hif_disconnect() - Initiate network disconnection. + * @ks: The ks7010 device. + */ +int ks7010_hif_disconnect(struct ks7010 *ks) +{ + struct ks7010_vif *vif = ks->vif; + + if (!(test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags))) + return -ENOTCONN; + + /* TODO interface disconnect with firmware */ + + /* The connected flag will be cleared in + * disconnect event notification. + */ + clear_bit(CONNECT_PEND, &vif->flags); + + return 0; +} + +/** + * hif_conn_ind() - Network connection indication. + * @ks: The ks7010 device. + * @ind: Data returned by firmware for connection event. + */ +static void hif_conn_ind(struct ks7010 *ks, struct fil_conn_ind *ind) +{ + struct ks7010_vif *vif = ks->vif; + + if (ind->code == CONN_CODE_DISCONNECT) { + ks_debug("connection event: disconnected"); + + clear_bit(CONNECTED, &vif->flags); + return; + } + ks_debug("connection event: connected"); + + /* TODO handle connection event */ + + spin_lock(&vif->if_lock); + set_bit(CONNECTED, &vif->flags); + spin_unlock(&vif->if_lock); +} + +static void _add_key(struct ks7010 *ks, int idx, u8 *key_val, size_t key_size) +{ + void (*fil_set_key_fn)(struct ks7010 *ks, u8 *key, size_t key_size); + + switch (idx) { + case 0: + fil_set_key_fn = ks7010_fil_set_key_1; + break; + + case 1: + fil_set_key_fn = ks7010_fil_set_key_2; + break; + + case 2: + fil_set_key_fn = ks7010_fil_set_key_3; + break; + + case 3: + fil_set_key_fn = ks7010_fil_set_key_4; + break; + + default: + ks_debug("key index out of range: %d", idx); + return; + } + + fil_set_key_fn(ks, key_val, key_size); +} + +/** + * ks7010_hif_add_wep_key() - Add WEP key to device. + * @ks: The ks7010 device. + * @key_index: Index of key to add. + */ +int ks7010_hif_add_wep_key(struct ks7010 *ks, int key_index) +{ + struct ks7010_vif *vif = ks->vif; + struct ks7010_wep_key *key; + + if (key_index > KS7010_MAX_WEP_KEY_INDEX) { + ks_debug("key index %d out of bounds\n", key_index); + return -EINVAL; + } + + key = &vif->wep_keys[key_index]; + _add_key(ks, key_index, key->key_val, key->key_size); + + return 0; +} + +/** + * ks7010_hif_add_wpa_key() - Add WPA key to device. + * @ks: The ks7010 device. + * @idx: Index of key to add. + */ +int ks7010_hif_add_wpa_key(struct ks7010 *ks, int key_index) +{ + /* TODO interface add_wpa_key with the firmware */ + return 0; +} + +/** + * ks7010_hif_set_default_key() - Set the default key index to use. + * @ks: The ks7010 device. + * @idx: New default index. + */ +int ks7010_hif_set_default_key(struct ks7010 *ks, int idx) +{ + struct ks7010_vif *vif = ks->vif; + int max_idx; + + max_idx = max(KS7010_MAX_WEP_KEY_INDEX, KS7010_MAX_WPA_KEY_INDEX); + if (idx > max_idx) { + ks_debug("key index out of bounds: %d", idx); + return -EINVAL; + } + + /* FIXME same variable for WEP index and WPA default tx key? */ + vif->def_txkey_index = idx; + ks7010_fil_set_default_key_index(ks, idx); + + return 0; +} + +static enum fil_scan_type hif_to_fil_scan_type(enum hif_bss_scan_type type) +{ + if (type == BSS_SCAN_ACTIVE) + return FIL_SCAN_TYPE_ACTIVE; + + return FIL_SCAN_TYPE_PASSIVE; +} + +/** + * ks7010_hif_scan() - Initiate a network scan. + * @ks: The ks7010 device. + * @type: Scan type, &enum hif_bss_scan_type. + * @channels: Channels to scan, &struct hif_channels. + * @ssid: SSID to scan, &struct hif_ssid. + */ +void ks7010_hif_scan(struct ks7010 *ks, enum hif_bss_scan_type type, + struct hif_channels *channels, struct hif_ssid *ssid) +{ + struct fil_scan req; + + memset(&req, 0, sizeof(req)); + + req.scan_type = hif_to_fil_scan_type(type); + req.ssid = ssid->buf; + req.ssid_size = ssid->size; + req.channels = channels->list; + req.channels_size = channels->size; + + ks7010_fil_scan(ks, &req); +} + +/** + * ks7010_hif_set_power_mgmt_active() - Disable power save. + * @ks: The ks7010 device. + */ +void ks7010_hif_set_power_mgmt_active(struct ks7010 *ks) +{ + struct fil_power_mgmt req; + + req.ps_enable = false; + req.wake_up = true; + req.receive_dtims = true; + + ks7010_fil_set_power_mgmt(ks, &req); +} + +/** + * ks7010_hif_set_power_mgmt_sleep() - Enable power save, sleep. + * @ks: The ks7010 device. + * + * Power save sleep mode. Wake periodically to receive DTIM's. + */ +void ks7010_hif_set_power_mgmt_sleep(struct ks7010 *ks) +{ + struct fil_power_mgmt req; + + req.ps_enable = true; + req.wake_up = false; + req.receive_dtims = true; + + ks7010_fil_set_power_mgmt(ks, &req); +} + +/** + * ks7010_hif_set_power_mgmt_deep_sleep() - Enable power save, deep sleep. + * @ks: The ks7010 device. + * + * Power save deep sleep mode. Do not wake to receive DTIM's. + */ +void ks7010_hif_set_power_mgmt_deep_sleep(struct ks7010 *ks) +{ + struct fil_power_mgmt req; + + req.ps_enable = true; + req.wake_up = false; + req.receive_dtims = false; + + ks7010_fil_set_power_mgmt(ks, &req); +} + +static void +hif_result_debug_msg(const char *fn_name, enum fil_result_code result) +{ + switch (result) { + case RESULT_SUCCESS: + ks_debug("%s result 'success'", fn_name); + break; + + case RESULT_INVALID_PARAMETERS: + ks_debug("%s result 'invalid parameters'", fn_name); + break; + + case RESULT_NOT_SUPPORTED: + ks_debug("%s result 'not supported'", fn_name); + break; + } +} + +enum scan_event { + SCAN_ABORTED = true, + SCAN_COMPLETED = false +}; + +static void hif_scan_conf(struct ks7010 *ks, enum fil_result_code result) +{ + hif_result_debug_msg("scan conf", result); + + if (result != RESULT_SUCCESS) + ks7010_cfg80211_scan_aborted(ks); +} + +static void hif_scan_ind(struct ks7010 *ks, struct fil_scan_ind *ind) +{ + /* TODO handle scan indication */ + + ks7010_cfg80211_scan_complete(ks); +} + +static void hif_data_ind(struct ks7010 *ks, int key_index, + u8 *data, size_t data_size) +{ + /* TODO handle data indication */ +} + +/* + * FIXME currently all the callbacks are running in software interrupt + * context, called by the rx bottom half tasklet. Is this correct? + */ + +static struct fil_ops fil_ops = { + .get_fw_version_conf = hif_get_fw_version_conf, + .get_mac_addr_conf = hif_get_mac_addr_conf, + .get_rts_thresh_conf = hif_get_rts_thresh_conf, + .get_frag_thresh_conf = hif_get_frag_thresh_conf, + .scan_conf = hif_scan_conf, + .scan_ind = hif_scan_ind, + .data_ind = hif_data_ind, + .conn_ind = hif_conn_ind, +}; + +void ks7010_hif_init(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); +} + +void ks7010_hif_cleanup(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); +} + +void ks7010_hif_create(struct ks7010 *ks) +{ + ks->fil_ops = &fil_ops; +} + +void ks7010_hif_destroy(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); +} + diff --git a/drivers/staging/ks7010/hif.h b/drivers/staging/ks7010/hif.h new file mode 100644 index 0000000..932146a --- /dev/null +++ b/drivers/staging/ks7010/hif.h @@ -0,0 +1,202 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_HIF_H +#define _KS7010_HIF_H + +#include <linux/compiler.h> +#include <linux/skbuff.h> +#include <linux/ieee80211.h> + +#include "common.h" + +#define HIF_MAX_CHANNELS 14 +#define HIF_SSID_MAX_LEN 32 + +#define AP_INFO_RATE_MAX_SIZE 8 +#define RATE_SET_MAX_SIZE 16 + +#define BASIC_RATE 0x80 +#define RATE_MASK 0x7F +#define TX_RATE_AUTO 0xff + +#define PTK_IDX 0 +#define GTK1_IDX 1 +#define GTK2_IDX 2 + +/** + * enum hif_network_type - Network type. + * @INFRA_NETWORK: Infrastructure network. + * @ADHOC_NETWORK: Not implemented. + */ +enum hif_network_type { + INFRA_NETWORK = 0x01, + ADHOC_NETWORK = 0x02, +}; + +/** + * enum hif_dot11_auth_type - 802.11 Authentication. + * @DOT11_AUTH_OPEN: Open system authentication. + * @DOT11_AUTH_SHARED: Shared key authentication. + */ +enum hif_dot11_auth_mode { + DOT11_AUTH_OPEN = 0x01, + DOT11_AUTH_SHARED = 0x02, +}; + +/** + * enum hif_auth_mode - Authentication modes. + * @AUTH_NONE: Used for WEP and no authentication + * @AUTH_WPA: Wi-Fi Protected Access version 1. + * @AUTH_WPA2: Wi-Fi Protected Access version 2. + * @AUTH_WPA_PSK: Wi-Fi Protected Access version 1 pre-shared key. + * @AUTH_WPA2_PSK: Wi-Fi Protected Access version 2 pre-shared key. + */ +enum hif_auth_mode { + AUTH_NONE = 0x01, + AUTH_WPA = 0x02, + AUTH_WPA2 = 0x04, + AUTH_WPA_PSK = 0x08, + AUTH_WPA2_PSK = 0x10, +}; + +/** + * enum hif_crypt_type - Cryptography protocol. + * @CRYPT_NONE: No cryptography used. + * @CRYPT_WEP: Wired Equivalent Protocol. + * @CRYPT_TKIP: Temporal Key Integrity Protocol (WPA). + * @CRYPT_AES: Advanced Encryption Standard (RSN). + */ +enum hif_crypt_type { + CRYPT_NONE = 0x01, + CRYPT_WEP = 0x02, + CRYPT_TKIP = 0x04, + CRYPT_AES = 0x08, +}; + +/** + * enum hif_preamble_type - Used by PHY to synchronize transmiter and receiver. + * @PREAMBLE_LONG: Long preamble. + * @PREAMBLE_SHORT: Short preamble. + */ +enum hif_preamble_type { + PREAMBLE_LONG, + PREAMBLE_SHORT +}; + +/** + * enum hif_bss_scan_type - Scan type. + * @BSS_SCAN_ACTIVE: Use probe request frames it identify networks. + * @BSS_SCAN_PASSIVE: Identify networks by listening for beacons. + */ +enum hif_bss_scan_type { + BSS_SCAN_ACTIVE = 0, + BSS_SCAN_PASSIVE +}; + +/** + * enum hif_nw_phy_type - set_request + * (pseudo_adhoc, adhoc, and infrastructure) + * @PHY_MODE_11B_ONLY: 802.11b + * @PHY_MODE_11G_ONLY: 802.11g + * + * FIXME remove this (802.11g is backward compatible with b)? + * @PHY_MODE_11BG_COMPATIBLE_MODE: + */ +enum hif_nw_phy_type { + PHY_MODE_11B_ONLY = 0, + PHY_MODE_11G_ONLY, + PHY_MODE_11BG_COMPATIBLE +}; + +/** + * enum hif_nw_cts_mode - Clear to send mode. + * @CTS_MODE_FALSE: false. + * @CTS_MODE_TRUE: true. + */ +enum hif_nw_cts_mode { + CTS_MODE_FALSE = 0, + CTS_MODE_TRUE +}; + +/** + * struct hif_channels - Channel list. + * @list: List of channels, each channel is one octet. + * @size: The size of the list. + */ +struct hif_channels { + u8 list[HIF_MAX_CHANNELS]; + size_t size; +}; + +/** + * struct hif_ssid - Service set identifier. + * @buf: Buffer holding the SSID. + * @size: Size of SSID. + */ +struct hif_ssid { + char buf[HIF_SSID_MAX_LEN]; + size_t size; +}; + +/** + * enum hif_power_mgmt_type + * @POWER_MGMT_ATTIVE: Initiate request to activate device. + * @POWER_MGMT_DEEP_SLEEP: Initiate sleep request, do not receive DTIM's. + * @POWER_MGMT_SLEEP: Initiate sleep request, receive DTIM's. + */ +enum hif_power_mgmt_type { + POWER_MGMT_ACTIVE, + POWER_MGMT_DEEP_SLEEP, + POWER_MGMT_SLEEP +}; + +struct tx_data; /* used as return argument */ + +int ks7010_hif_tx_start(struct ks7010 *ks, struct sk_buff *skb, + struct tx_data *txd); +int ks7010_hif_tx(struct ks7010 *ks, u8 *data, size_t data_size); +void ks7010_hif_rx(struct ks7010 *ks, u8 *data, size_t data_size); + +void ks7010_hif_get_mac_addr(struct ks7010 *ks); +void ks7010_hif_get_fw_version(struct ks7010 *ks); + +void ks7010_hif_set_rts_thresh(struct ks7010 *ks, u32 thresh); +void ks7010_hif_set_frag_thresh(struct ks7010 *ks, u32 thresh); + +int ks7010_hif_connect(struct ks7010 *ks); +int ks7010_hif_reconnect(struct ks7010 *ks); +int ks7010_hif_disconnect(struct ks7010 *ks); + +int ks7010_hif_add_wep_key(struct ks7010 *ks, int idx); +int ks7010_hif_add_wpa_key(struct ks7010 *ks, int key_index); +int ks7010_hif_set_default_key(struct ks7010 *ks, int idx); + +void ks7010_hif_scan(struct ks7010 *ks, enum hif_bss_scan_type type, + struct hif_channels *channels, struct hif_ssid *ssid); + +void ks7010_hif_set_power_mgmt_active(struct ks7010 *ks); +void ks7010_hif_set_power_mgmt_sleep(struct ks7010 *ks); +void ks7010_hif_set_power_mgmt_deep_sleep(struct ks7010 *ks); + +void ks7010_hif_init(struct ks7010 *ks); +void ks7010_hif_cleanup(struct ks7010 *ks); + +void ks7010_hif_create(struct ks7010 *ks); +void ks7010_hif_destroy(struct ks7010 *ks); + +#endif /* _KS7010_HIF_H */ diff --git a/drivers/staging/ks7010/ks7010.h b/drivers/staging/ks7010/ks7010.h new file mode 100644 index 0000000..6fdec3f --- /dev/null +++ b/drivers/staging/ks7010/ks7010.h @@ -0,0 +1,309 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_H +#define _KS7010_H + +#include <net/cfg80211.h> +#include <linux/list.h> +#include <linux/if_ether.h> +#include <linux/interrupt.h> +#include <linux/wireless.h> +#include <linux/skbuff.h> +#include <crypto/hash.h> +#include <linux/kfifo.h> + +#include "common.h" +#include "hif.h" + +#define DRIVER_PREFIX "ks7010: " + +#define ks_err(fmt, arg...) \ + pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg) + +#define ks_info(fmt, arg...) \ + pr_info(DRIVER_PREFIX "INFO " fmt "\n", ##arg) + +#define ks_warn(fmt, arg...) \ + pr_warn(DRIVER_PREFIX "WARNING " fmt "\n", ##arg) + +#define ks_debug(fmt, arg...) \ + pr_debug(DRIVER_PREFIX "%s: " fmt "\n", __func__, ##arg) + +#define KS7010_ROM_FILE "ks7010sd.rom" + +/** + * enum ks7010_state - ks7010 device state. + * @KS7010_STATE_OFF: Device is off. + * @KS7010_STATE_READY: Device ready. + */ +enum ks7010_state { + KS7010_STATE_OFF, + KS7010_STATE_READY +}; + +/* SDIO function private data */ +struct ks7010_sdio; + +#define KS7010_TX_QUEUE_SIZE 1024 /* must be a power of 2 */ +#define KS7010_RX_QUEUE_SIZE 32 /* must be a power of 2 */ +#define RX_DATA_MAX_SIZE (2 + 2 + 2347 + 1) + +/** + * struct tx_data - Transmit path data. + * @data: The data. + * @data_size: Size of the data, in octets. + */ +struct tx_data { + u8 *datap; + size_t size; +}; + +/** + * struct tx_queue - Transmit path queue. + * @buf: Buffer used to hold the queue. + * @head: Head of the queue. + * @tail: Tail of the queue. + * + * Tx queue uses a circular buffer. Single producer is enforced by + * networking layer, single consumer is enforced due to consumer + * being called from the interrupt handler. No further queue locking + * is required. + */ +struct tx_queue { + struct tx_data buf[KS7010_TX_QUEUE_SIZE]; + int head; + int tail; + spinlock_t producer_lock; /* enforce single producer */ + spinlock_t consumer_lock; /* enforce single consumer */ +}; + +/** + * struct rx_data - Receive path data. + * @data: The data. + * @data_size: Size of the data, in octets. + */ +struct rx_data { + u8 data[RX_DATA_MAX_SIZE]; + size_t data_size; +}; + +/** + * struct rx_queue - Receive path queue. + * @buf: Buffer used to hold the queue. + * @head: Head of the queue. + * @tail: Tail of the queue. + * + * Rx queue uses a circular buffer. Rx queue data is produced during + * interrupt handling, no further locking is required. Single consumer + * must be enforced by the driver. + */ +struct rx_queue { + struct rx_data buf[KS7010_RX_QUEUE_SIZE]; + int head; + int tail; + spinlock_t producer_lock; /* enforce single producer */ + spinlock_t consumer_lock; /* enforce single consumer */ +}; + +/* vif flags info */ +/** + * enum ks7010_vif_state - VIF flags + * @CONNECTED: Connected to a network. + * @CONNECT_PEND: Network connection initiated. + * @WLAN_ENABLED: + */ +enum ks7010_vif_state { + CONNECTED, + CONNECT_PEND, + WLAN_ENABLED, +}; + +#define KS7010_WEP_KEY_MAX_SIZE 64 + +struct ks7010_wep_key { + u8 key_size; + u8 key_val[KS7010_WEP_KEY_MAX_SIZE]; +}; + +#define KS7010_KEY_SEQ_MAX_SIZE 8 +#define KS7010_MIC_KEY_SIZE 8 + +struct ks7010_wpa_key { + u8 key_val[WLAN_MAX_KEY_LEN]; + u8 key_size; + + u8 seq[KS7010_KEY_SEQ_MAX_SIZE]; + u8 seq_size; + + u32 cipher; + + u8 tx_mic_key[KS7010_MIC_KEY_SIZE]; + u8 rx_mic_key[KS7010_MIC_KEY_SIZE]; +}; + +#define KS7010_NUM_WEP_KEYS 4 +#define KS7010_MAX_WEP_KEY_INDEX 3 + +#define KS7010_NUM_WPA_KEYS 3 /* ptk, gtk1, gtk2 */ +#define KS7010_MAX_WPA_KEY_INDEX 2 + +/** + * ks7010_vif - Virtual interface (net_device private data). + * @ndev: Pointer to the net_device for this VIF. + */ +struct ks7010_vif { + struct net_device *ndev; + struct wireless_dev wdev; + + struct ks7010 *ks; + + /* Protect VIF flags */ + spinlock_t if_lock; /* TODO use this lock when needed */ + unsigned long flags; + + u8 req_bssid[ETH_ALEN]; + + size_t ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + + enum hif_network_type nw_type; + enum hif_dot11_auth_mode dot11_auth_mode; + enum hif_auth_mode auth_mode; + + enum hif_crypt_type pairwise_crypto; + size_t pairwise_crypto_size; + enum hif_crypt_type group_crypto; + size_t group_crypto_size; + + enum hif_bss_scan_type scan_type; + struct cfg80211_scan_request *scan_req; + + u8 tx_rate; + enum hif_preamble_type preamble; + + u16 beacon_lost_count; + u32 rts_thresh; + u32 frag_thresh; + u16 ch_hint; + + enum hif_nw_phy_type phy_type; + enum hif_nw_cts_mode cts_mode; + + enum hif_power_mgmt_type power_mgmt; + + u8 bssid[ETH_ALEN] __aligned(2); + + bool privacy_invoked; + struct ks7010_wep_key wep_keys[KS7010_NUM_WEP_KEYS]; + + bool wpa_enabled; + struct ks7010_wpa_key wpa_keys[KS7010_NUM_WPA_KEYS]; + + int def_txkey_index; +}; + +/** + * struct ks7010 - The ks7010 device. + * @priv: Pointer to the SDIO private data. + * @vif: The virtual interface (driver supports single VIF only). + * @state: The device state, &enum ks7010_state. + * @wiphy: The device wiphy. + * @dev: Pointer to the device embedded within the SDIO func. + * @fil_ops: Firmware interface layer operations. + * @mac_addr: Device MAC address. + * @mac_addr_valid: True if @mac_addr is valid. + */ +struct ks7010 { + struct ks7010_sdio *priv; + struct ks7010_vif *vif; + + enum ks7010_state state; + + struct wiphy *wiphy; + bool wiphy_registered; + + struct device *dev; + + struct fil_ops *fil_ops; + + u8 mac_addr[ETH_ALEN] __aligned(2); + bool mac_addr_valid; + + /* firmware */ + u8 *fw; + size_t fw_size; + + char fw_version[ETHTOOL_FWVERS_LEN]; + size_t fw_version_len; + + /* tx and rx */ + struct tasklet_struct rx_bh_task; + + struct tx_queue txq; + struct rx_queue rxq; + + /* TKIP */ + struct crypto_shash *tx_tfm_mic; + struct crypto_shash *rx_tfm_mic; + + /* TODO add stat updates to driver */ + struct net_device_stats nstats; + struct iw_statistics wstats; + spinlock_t stats_lock; /* protect stats */ +}; + +static inline struct ks7010_vif *ks7010_wdev_to_vif(struct wireless_dev *wdev) +{ + return container_of(wdev, struct ks7010_vif, wdev); +} + +static inline struct ks7010 *ks7010_ndev_to_ks(struct net_device *ndev) +{ + return ((struct ks7010_vif *)netdev_priv(ndev))->ks; +} + +/* main.c */ +bool ks7010_is_asleep(struct ks7010 *ks); +void ks7010_request_wakeup(struct ks7010 *ks); +void ks7010_request_sleep(struct ks7010 *ks); + +void ks7010_init_netdev(struct net_device *ndev); +int ks7010_init_hw(struct ks7010 *ks); + +int ks7010_init(struct ks7010 *ks); +void ks7010_cleanup(struct ks7010 *ks); + +struct ks7010 *ks7010_create(struct device *dev); +void ks7010_destroy(struct ks7010 *ks); + +/* tx.c */ +int ks7010_tx_start(struct sk_buff *skb, struct net_device *ndev); + +int ks7010_tx_enqueue(struct ks7010 *ks, u8 *data, size_t data_size); +void ks7010_tx_hw(struct ks7010 *ks); + +int ks7010_tx_init(struct ks7010 *ks); +void ks7010_tx_cleanup(struct ks7010 *ks); + +/* rx.c */ +void ks7010_rx(struct ks7010 *ks, u16 size); + +int ks7010_rx_init(struct ks7010 *ks); +void ks7010_rx_cleanup(struct ks7010 *ks); + +#endif /* _KS7010_H */ diff --git a/drivers/staging/ks7010/main.c b/drivers/staging/ks7010/main.c new file mode 100644 index 0000000..7b045e2 --- /dev/null +++ b/drivers/staging/ks7010/main.c @@ -0,0 +1,337 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/firmware.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio_func.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/inetdevice.h> + +#include "ks7010.h" +#include "sdio.h" +#include "cfg80211.h" + +/** + * ks7010_is_asleep() - True if the device is asleep. + * @ks: The ks7010 device. + */ +bool ks7010_is_asleep(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); + return false; +} + +/** + * ks7010_request_wakeup() - Request the device to enter active mode. + * @ks: The ks7010 device. + */ +void ks7010_request_wakeup(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); +} + +/** + * ks7010_request_sleep() - Request the device to enter sleep mode. + * @ks: The ks7010 device. + */ +void ks7010_request_sleep(struct ks7010 *ks) +{ + ks_debug("not implemented yet"); +} + +static int ks7010_open(struct net_device *ndev) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + + set_bit(WLAN_ENABLED, &vif->flags); + + if (test_bit(CONNECTED, &vif->flags)) { + netif_carrier_on(ndev); + netif_wake_queue(ndev); + } else { + netif_carrier_off(ndev); + } + + return 0; +} + +static int ks7010_close(struct net_device *ndev) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + + netif_stop_queue(ndev); + netif_carrier_off(vif->ndev); + + ks7010_cfg80211_stop(vif); + + clear_bit(WLAN_ENABLED, &vif->flags); + + return 0; +} + +static int +ks7010_set_features(struct net_device *dev, netdev_features_t features) +{ + ks_debug("not implemented yet"); + return 0; +} + +static void ks7010_set_multicast_list(struct net_device *dev) +{ + ks_debug("not implemented yet"); +} + +static const struct net_device_ops ks7010_netdev_ops = { + .ndo_open = ks7010_open, + .ndo_stop = ks7010_close, + .ndo_start_xmit = ks7010_tx_start, + .ndo_set_features = ks7010_set_features, + .ndo_set_rx_mode = ks7010_set_multicast_list, +}; + +static const unsigned char dummy_addr[] = { + 0x00, 0x0b, 0xe3, 0x00, 0x00, 0x00 +}; + +#define KS7010_TX_TIMEOUT (3 * HZ) + +void ks7010_init_netdev(struct net_device *ndev) +{ + struct ks7010_vif *vif = netdev_priv(ndev); + struct ks7010 *ks = vif->ks; + + ndev->netdev_ops = &ks7010_netdev_ops; + ndev->destructor = free_netdev; + ndev->watchdog_timeo = KS7010_TX_TIMEOUT; + + ks->mac_addr_valid = false; + ether_addr_copy(ks->mac_addr, dummy_addr); + ether_addr_copy(ndev->dev_addr, dummy_addr); +} + +static int ks7010_fetch_fw(struct ks7010 *ks) +{ + const struct firmware *fw_entry = NULL; + const u8 *data; + size_t size; + int ret; + + if (ks->fw) + return 0; + + if (!ks->dev) { + ks_debug("no valid pointer to dev"); + return -ENODEV; + } + + ret = request_firmware(&fw_entry, KS7010_ROM_FILE, ks->dev); + if (ret) { + ks_debug("request_firmware() failed"); + return ret; + } + + data = fw_entry->data; + size = fw_entry->size; + + /* TODO firmware sanity checks */ + + ks->fw = kmemdup(data, size, GFP_KERNEL); + if (!ks->fw) { + ret = -ENOMEM; + goto release_firmware; + } + ks->fw_size = size; + + ret = 0; + +release_firmware: + release_firmware(fw_entry); + + return ret; +} + +enum fw_check_type { + FW_CHECK_RUNNING_SINGLE, + FW_CHECK_RUNNING_REPEAT +}; + +#define FW_CHECK_NUM_REPEATS 50 +#define FW_CHECK_DELAY 10 + +static bool +ks7010_fw_is_running(struct ks7010 *ks, enum fw_check_type check_type) +{ + int nchecks; + int i; + + nchecks = 1; + if (check_type == FW_CHECK_RUNNING_REPEAT) + nchecks = FW_CHECK_NUM_REPEATS; + + for (i = 0; i < nchecks; i++) { + if (ks7010_sdio_fw_is_running(ks)) + return true; + + if (i < nchecks - 1) + mdelay(FW_CHECK_DELAY); + } + + return false; +} + +static int _upload_fw(struct ks7010 *ks) +{ + int ret; + + if (ks7010_fw_is_running(ks, FW_CHECK_RUNNING_SINGLE)) { + ks_debug("firmware already running"); + return 0; + } + + if (!ks->fw) { + ret = ks7010_fetch_fw(ks); + if (ret) { + ks_debug("failed to fetch firmware"); + return ret; + } + } + + ret = ks7010_sdio_upload_fw(ks, ks->fw, ks->fw_size); + if (ret) { + ks_debug("failed to upload firmware"); + goto err_free_fw; + } + + if (!ks7010_fw_is_running(ks, FW_CHECK_RUNNING_REPEAT)) { + ks_debug("firmware failed to start"); + ret = -EIO; + goto err_free_fw; + } + + return 0; + +err_free_fw: + kfree(ks->fw); + + return ret; +} + +int ks7010_init(struct ks7010 *ks) +{ + struct wireless_dev *wdev; + int ret; + + spin_lock_init(&ks->stats_lock); + + ret = ks7010_tx_init(ks); + if (ret) { + ks_err("failed to tx init"); + return ret; + } + + ret = ks7010_rx_init(ks); + if (ret) { + ks_err("failed to rx init"); + goto err_tx_cleanup; + } + + ret = ks7010_cfg80211_init(ks); + if (ret) { + ks_err("failed to configure cfg80211"); + goto err_rx_cleanup; + } + + ret = _upload_fw(ks); + if (ret) { + ks_err("failed to upload firmware: %d", ret); + goto err_cfg80211_cleanup; + } + + rtnl_lock(); + wdev = ks7010_cfg80211_add_interface(ks, "wlan%d", NET_NAME_ENUM, + INFRA_NETWORK); + rtnl_unlock(); + if (IS_ERR(wdev)) { + ret = PTR_ERR(wdev); + ks_err("failed to add interface\n"); + goto err_free_fw; + } + + ks_debug("%s: name=%s dev=0x%p, ks=0x%p\n", + __func__, wdev->netdev->name, wdev->netdev, ks); + + return 0; + +err_free_fw: + kfree(ks->fw); +err_cfg80211_cleanup: + ks7010_cfg80211_cleanup(ks); +err_rx_cleanup: + ks7010_rx_cleanup(ks); +err_tx_cleanup: + ks7010_tx_cleanup(ks); + + return ret; +} + +/** + * ks7010_cleanup() - Undoes ks7010_init() + * @ks: The ks7010 device. + */ +void ks7010_cleanup(struct ks7010 *ks) +{ + rtnl_lock(); + ks7010_cfg80211_rm_interface(ks); + rtnl_unlock(); + + kfree(ks->fw); + + ks7010_cfg80211_cleanup(ks); + ks7010_rx_cleanup(ks); + ks7010_tx_cleanup(ks); +} + +/* FIXME what about the device embedded in the net_device? */ + +/** + * ks7010 *ks7010_create() - Create the ks7010 device. + * @dev: The device embedded within the SDIO function. + */ +struct ks7010 *ks7010_create(struct device *dev) +{ + struct ks7010 *ks; + + ks = ks7010_cfg80211_create(); + if (!ks) + return NULL; + + ks->dev = dev; + ks->state = KS7010_STATE_OFF; + + return ks; +} + +/** + * ks7010_destroy() - Destroy the ks7010 device. + * @ks: The ks7010 device. + */ +void ks7010_destroy(struct ks7010 *ks) +{ + ks->dev = NULL; + ks7010_cfg80211_destroy(ks); +} diff --git a/drivers/staging/ks7010/rx.c b/drivers/staging/ks7010/rx.c new file mode 100644 index 0000000..2714822 --- /dev/null +++ b/drivers/staging/ks7010/rx.c @@ -0,0 +1,130 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/circ_buf.h> + +#include "ks7010.h" +#include "hif.h" +#include "sdio.h" + +/** + * ks7010_rx() - Copy rx data from device. + * @ks: The ks7010 device. + * @size: Number of octets to copy. + * + * Reads rx data from the ks7010 device to the driver. Called in + * interrupt context by @ks7010_sdio_interrupt(). + */ +void ks7010_rx(struct ks7010 *ks, u16 size) +{ + struct rx_queue *q = &ks->rxq; + struct rx_data *rxd; + int head, tail; + unsigned long flags; + int ret = 0; + + if (size == 0 || size > RX_DATA_MAX_SIZE) { + ks_debug("rx data size invalid %d\n", size); + ret = -EINVAL; + goto idle; + } + + spin_lock_irqsave(&q->producer_lock, flags); + + head = q->head; + tail = READ_ONCE(q->tail); + + if (CIRC_SPACE(head, tail, KS7010_RX_QUEUE_SIZE) < 1) + goto unlock; + + rxd = &q->buf[head]; + + ret = ks7010_sdio_rx_read(ks, rxd->data, size); + + /* Finish reading descriptor before incrementing tail. */ + smp_store_release(&q->head, (head + 1) & (KS7010_RX_QUEUE_SIZE - 1)); + +unlock: + spin_unlock_irqrestore(&q->producer_lock, flags); + +idle: + ks7010_sdio_set_read_status_idle(ks); + if (ret == 0) + tasklet_schedule(&ks->rx_bh_task); +} + +static void ks7010_rx_bh_task(unsigned long dev) +{ + struct ks7010 *ks = (struct ks7010 *)dev; + struct rx_queue *q = &ks->rxq; + struct rx_data *rxd; + int head, tail; + int pending; + + /* FIXME do we need a consumer lock when only one tasklet can + * run at a time? + */ + + spin_lock_bh(&q->consumer_lock); + + /* Read index before reading contents at that index. */ + head = smp_load_acquire(&q->head); + tail = q->tail; + + pending = CIRC_CNT(head, tail, KS7010_RX_QUEUE_SIZE); + if (pending == 0) + goto unlock; + + rxd = &q->buf[tail]; + ks7010_hif_rx(ks, rxd->data, rxd->data_size); + + /* Finish reading descriptor before incrementing tail. */ + smp_store_release(&q->tail, (tail + 1) & (KS7010_RX_QUEUE_SIZE - 1)); + + if (pending > 1) + tasklet_schedule(&ks->rx_bh_task); + +unlock: + spin_unlock_bh(&q->consumer_lock); +} + +/** + * ks7010_rx_init() - Rx initialization function. + * @ks: The ks7010 device. + */ +int ks7010_rx_init(struct ks7010 *ks) +{ + struct rx_queue *q = &ks->rxq; + + tasklet_init(&ks->rx_bh_task, ks7010_rx_bh_task, (unsigned long)ks); + + spin_lock_init(&q->producer_lock); + spin_lock_init(&q->consumer_lock); + + return 0; +} + +/** + * ks7010_rx_cleanup() - Rx cleanup function. + * @ks: The ks7010 device. + */ +void ks7010_rx_cleanup(struct ks7010 *ks) +{ + tasklet_kill(&ks->rx_bh_task); +} diff --git a/drivers/staging/ks7010/sdio.c b/drivers/staging/ks7010/sdio.c new file mode 100644 index 0000000..108b920 --- /dev/null +++ b/drivers/staging/ks7010/sdio.c @@ -0,0 +1,691 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sd.h> +#include <linux/firmware.h> + +#include "ks7010.h" +#include "sdio.h" + +/* SDIO KeyStream vendor and device */ +#define SDIO_VENDOR_ID_KS_CODE_A 0x005b +#define SDIO_VENDOR_ID_KS_CODE_B 0x0023 + +/* Older sources suggest earlier versions were named 7910 or 79xx */ +#define SDIO_DEVICE_ID_KS_7010 0x7910 + +#define KS7010_IO_BLOCK_SIZE 512 + +/* read status register */ +#define READ_STATUS_ADDR 0x000000 +#define READ_STATUS_BUSY 0 +#define READ_STATUS_IDLE 1 + +/* read index register */ +#define READ_INDEX_ADDR 0x000004 + +/* read data size register */ +#define READ_DATA_SIZE_ADDR 0x000008 + +/* write index register */ +#define WRITE_INDEX_ADDR 0x000010 + +/* write status register */ +#define WRITE_STATUS_ADDR 0x00000C +#define WRITE_STATUS_BUSY 0 +#define WRITE_STATUS_IDLE 1 + +/* [write status] / [read data size] register + * Used for network packets less than 2048 bytes data. + */ +#define WSTATUS_RSIZE_ADDR 0x000014 +#define WSTATUS_MASK 0x80 +#define RSIZE_MASK 0x7F + +/* ARM to SD interrupt enable */ +#define INT_ENABLE_ADDR 0x000020 +#define INT_DISABLE 0 + +/* ARM to SD interrupt pending */ +#define INT_PENDING_ADDR 0x000024 +#define INT_CLEAR 0xFF + +/* General Communication Register A */ +#define GCR_A_ADDR 0x000028 +enum gen_com_reg_a { + GCR_A_INIT = 0, + GCR_A_REMAP, + GCR_A_RUN +}; + +/* General Communication Register B */ +#define GCR_B_ADDR 0x00002C +enum gen_com_reg_b { + GCR_B_ACTIVE = 0, + GCR_B_SLEEP +}; + +#define INT_GCR_B BIT(7) +#define INT_GCR_A BIT(6) +#define INT_WRITE_STATUS BIT(5) +#define INT_WRITE_INDEX BIT(4) +#define INT_WRITE_SIZE BIT(3) +#define INT_READ_STATUS BIT(2) +#define INT_READ_INDEX BIT(1) +#define INT_READ_SIZE BIT(0) + +/* wake up register */ +#define WAKEUP_ADDR 0x008018 +#define WAKEUP_REQ 0x5a + +/* AHB Data Window 0x010000-0x01FFFF */ +#define DATA_WINDOW_ADDR 0x010000 +#define DATA_WINDOW_SIZE (64 * 1024) + +#define KS7010_IRAM_ADDR 0x06000000 + +/** + * enum ks7010_sdio_state - SDIO device state. + * @SDIO_DISABLED: SDIO function is disabled. + * @SDIO_ENABLED: SDIO function is enabled. + */ +enum ks7010_sdio_state { + SDIO_DISABLED, + SDIO_ENABLED +}; + +/** + * struct ks7010_sdio - SDIO device private data. + * @func: The SDIO function device. + * @ks: The ks7010 device. + * @id: The SDIO device identifier. + * @state: The SDIO device state, &enum ks7010_sdio_sate. + * @fw: Firmware for the device. + * @fw_size: Size of the firmware. + * @fw_version: Firmware version string. + * @fw_version_len: Length of firmware version string. + */ +struct ks7010_sdio { + struct sdio_func *func; + struct ks7010 *ks; + + const struct sdio_device_id *id; + enum ks7010_sdio_state state; +}; + +static struct sdio_func *ks_to_func(struct ks7010 *ks) +{ + struct ks7010_sdio *ks_sdio = ks->priv; + + if (ks_sdio->state != SDIO_ENABLED) { + ks_debug("sdio_func is not ready"); + return NULL; + } + + return ks_sdio->func; +} + +/** + * ks7010_sdio_readb() - Read a single byte from SDIO device. + * @ks: The ks7010 device. + * @addr: SDIO device address to read from. + * @byte: Pointer to store byte read. + */ +static int ks7010_sdio_readb(struct ks7010 *ks, int addr, u8 *byte) +{ + struct sdio_func *func = ks_to_func(ks); + int ret; + + sdio_claim_host(func); + *byte = sdio_readb(func, addr, &ret); + if (ret) + ks_debug("sdio read byte failed %d", ret); + sdio_release_host(func); + + return ret; +} + +/** + * ks7010_sdio_read() - Read data from SDIO device. + * @ks: The ks7010 device. + * @dst: Destination buffer to read data into. + * @addr: SDIO device address to read from. + * @count: Number of bytes to read. + */ +static int ks7010_sdio_read(struct ks7010 *ks, u8 *dst, unsigned int addr, + size_t count) +{ + struct sdio_func *func = ks_to_func(ks); + int ret; + + sdio_claim_host(func); + ret = sdio_memcpy_fromio(func, dst, addr, count); + if (ret) { + ks_debug("sdio read failed (%d) from addr: %X count: %zu", + ret, addr, count); + } + + sdio_release_host(func); + + return ret; +} + +/** + * ks7010_sdio_writeb() - Write a single byte to SDIO device. + * @ks: The ks7010 device. + * @addr: SDIO device address to write to. + * @byte: Byte to write. + */ +static int ks7010_sdio_writeb(struct ks7010 *ks, int addr, u8 byte) +{ + struct sdio_func *func = ks_to_func(ks); + int ret; + + sdio_claim_host(func); + sdio_writeb(func, byte, addr, &ret); + if (ret) + ks_debug("sdio write byte failed %d", ret); + sdio_release_host(func); + + return ret; +} + +/** + * ks7010_sdio_write() - Write data to SDIO device. + * @ks: The ks7010 device. + * @addr: SDIO device address to write to. + * @buf: Source data buffer. + * @len: Number of bytes to write. + */ +static int ks7010_sdio_write(struct ks7010 *ks, int addr, void *buf, size_t len) +{ + struct sdio_func *func = ks_to_func(ks); + int ret; + + sdio_claim_host(func); + ret = sdio_memcpy_toio(func, addr, buf, len); + if (ret) + ks_debug("sdio write failed %d", ret); + sdio_release_host(func); + + return ret; +} + +#define ALL_BITS_CLEAR 0x00 + +/** + * ks7010_sdio_read_trx_status_byte() - Tx/rx status information. + * @ks: The ks7010 device. + * + * Reads the appropriate registers on the device, returns information + * encoded in device specific format. Extract status information using + * @ks7010_sdio_can_tx() and TODO add rx documentation. + */ +u8 ks7010_sdio_read_trx_status_byte(struct ks7010 *ks) +{ + int ret; + u8 status; + + ret = ks7010_sdio_readb(ks, WSTATUS_RSIZE_ADDR, &status); + if (ret) + return ALL_BITS_CLEAR; + + return status; +} + +/** + * ks7010_sdio_can_tx() - True if device is ready to transmit. + * @ks: The ks7010 device. + * @trx_status_byte: Byte returned by @ks7010_sdio_read_trx_status() + */ +bool ks7010_sdio_can_tx(struct ks7010 *ks, u8 trx_status_byte) +{ + return trx_status_byte & WSTATUS_MASK; +} + +/** + * ks7010_sdio_set_read_status_idle() - Set the device read status to idle. + * @ks: The ks7010 device. + * + * Called after rx frame has been read from the device. + */ +void ks7010_sdio_set_read_status_idle(struct ks7010 *ks) +{ + ks7010_sdio_writeb(ks, READ_STATUS_ADDR, READ_STATUS_IDLE); +} + +/** + * ks7010_sdio_tx() - Write tx data to the device. + * @ks: The ks7010 device. + * @data: The data to write. + * @size: Write size, must be aligned. + */ +int ks7010_sdio_tx(struct ks7010 *ks, u8 *data, size_t size) +{ + int ret; + + ret = ks7010_sdio_write(ks, DATA_WINDOW_SIZE, data, size); + if (ret) + return ret; + + ret = ks7010_sdio_writeb(ks, WRITE_STATUS_ADDR, WRITE_STATUS_BUSY); + if (ret) + return ret; + + return 0; +} + +/** + * ks7010_sdio_rx_read() - Read rx data from the device. + * @ks: The ks7010 device. + * @buf: Read destination buf. + * @size: Number of octets to read, must be aligned. + */ +int ks7010_sdio_rx_read(struct ks7010 *ks, u8 *buf, size_t size) +{ + int ret; + + ret = ks7010_sdio_read(ks, buf, DATA_WINDOW_ADDR, size); + if (ret) + return ret; + + return 0; +} + +static int ks7010_sdio_enable_interrupts(struct ks7010 *ks) +{ + struct sdio_func *func = ks_to_func(ks); + u8 byte; + int ret; + + sdio_claim_host(func); + + ret = ks7010_sdio_writeb(ks, INT_PENDING_ADDR, INT_CLEAR); + if (ret) + goto out; + + byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS); + ret = ks7010_sdio_writeb(ks, INT_ENABLE_ADDR, byte); +out: + sdio_release_host(func); + return ret; +} + +/** + * ks7010_sdio_interrupt() - Interrupt handler for device. + * @func: The SDIO function. + */ +static void ks7010_sdio_interrupt(struct sdio_func *func) +{ + struct ks7010_sdio *ks_sdio; + struct ks7010 *ks; + u8 status, byte; + int ret; + + ks_sdio = sdio_get_drvdata(func); + ks = ks_sdio->ks; + + ret = ks7010_sdio_readb(ks, INT_PENDING_ADDR, &status); + if (ret) + return; + + /* TODO check if device just woke up */ + + do { + ret = ks7010_sdio_readb(ks, WSTATUS_RSIZE_ADDR, &byte); + if (ret) + return; + + /* rx frame arrival */ + if (byte & RSIZE_MASK) { + u16 size = (byte & RSIZE_MASK) << 4; + + ks7010_rx(ks, size); + } + + /* tx frame transmit complete */ + if (byte & WSTATUS_MASK) + ks7010_tx_hw(ks); + + /* FIXME why is this done in a loop? */ + } while (byte & RSIZE_MASK); +} + +static int ks7010_sdio_update_index(struct ks7010 *ks, u32 index) +{ + /* FIXME SDIO code should not have to know about FIL endianness */ + __le32 di; + int ret; + + di = cpu_to_le32(index); + + ret = ks7010_sdio_write(ks, WRITE_INDEX_ADDR, &di, sizeof(di)); + if (ret) + return -EIO; + + ret = ks7010_sdio_write(ks, READ_INDEX_ADDR, &di, sizeof(di)); + if (ret) + return -EIO; + + return 0; +} + +/** + * ks7010_sdio_fw_is_running() - True if firmware is running. + * @ks: The ks7010 device. + */ +bool ks7010_sdio_fw_is_running(struct ks7010 *ks) +{ + int ret; + u8 byte; + + ret = ks7010_sdio_readb(ks, GCR_A_ADDR, &byte); + if (ret) + return false; + + if (byte == GCR_A_RUN) + return true; + + return false; +} + +/** + * ks7010_sdio_upload_fw() - Upload firmware. + * @ks: The ks7010 device. + * @fw: Pointer to the firmware data. + * @fw_size: Size of firmware. + */ +int ks7010_sdio_upload_fw(struct ks7010 *ks, u8 *fw, size_t fw_size) +{ + size_t remaining; + int offset; + u8 *buf; + int ret; + + buf = kmalloc(DATA_WINDOW_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + remaining = fw_size; + offset = 0; + + ks_debug("attempting to upload %zu bytes of firmware", remaining); + + while (remaining > 0) { + int trf = 0; + + if (remaining > DATA_WINDOW_SIZE) { + trf = DATA_WINDOW_SIZE; + remaining -= DATA_WINDOW_SIZE; + } else { + trf = remaining; + remaining = 0; + } + + ret = ks7010_sdio_update_index(ks, KS7010_IRAM_ADDR + offset); + if (ret) + goto free_buf; + + /* upload firmware chunk */ + ret = ks7010_sdio_write(ks, DATA_WINDOW_ADDR, fw + offset, trf); + if (ret) + goto free_buf; + + ks_debug("wrote %d bytes to device address: %X with offset %X", + trf, DATA_WINDOW_ADDR, offset); + + /* verify chunk transfer */ + ret = ks7010_sdio_read(ks, buf, DATA_WINDOW_ADDR, trf); + if (ret) + goto free_buf; + + if (memcmp(buf, fw + offset, trf) != 0) { + ks_debug("fw upload failed: data compare error"); + ret = -EIO; + goto free_buf; + } + + offset += trf; + } + + ret = ks7010_sdio_writeb(ks, GCR_A_ADDR, GCR_A_REMAP); + if (ret) + goto free_buf; + + ret = 0; + +free_buf: + kfree(buf); + return ret; +} + +/* called before ks7010 device is initialized */ +static int ks7010_sdio_init(struct ks7010_sdio *ks_sdio, + const struct sdio_device_id *id) +{ + struct sdio_func *func = ks_sdio->func; + int ret = -ENODEV; + + ks_sdio->id = id; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) + goto err_release; + + sdio_writeb(func, INT_DISABLE, INT_ENABLE_ADDR, &ret); + if (ret) { + ret = -EIO; + goto err_disable_func; + } + + sdio_writeb(func, INT_CLEAR, INT_PENDING_ADDR, &ret); + if (ret) { + ret = -EIO; + goto err_disable_func; + } + + ret = sdio_claim_irq(func, ks7010_sdio_interrupt); + if (ret) + goto err_disable_func; + + sdio_release_host(func); + + ks_sdio->state = SDIO_ENABLED; + + return 0; + +err_release: + sdio_release_host(func); +err_disable_func: + sdio_disable_func(func); + + return ret; +} + +static void ks7010_sdio_cleanup(struct ks7010 *ks) +{ + struct sdio_func *func = ks_to_func(ks); + + sdio_claim_host(func); + + sdio_release_irq(func); + sdio_disable_func(func); + + sdio_release_host(func); +} + +static int ks7010_sdio_config(struct ks7010 *ks) +{ + struct sdio_func *func = ks_to_func(ks); + int ret; + + sdio_claim_host(func); + + /* give us some time to enable, in ms */ + func->enable_timeout = 100; + + ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE); + if (ret) { + ks_debug("set sdio block size %d failed: %d)\n", + KS7010_IO_BLOCK_SIZE, ret); + goto out; + } + +out: + sdio_release_host(func); + + return ret; +} + +static int ks7010_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct ks7010_sdio *ks_sdio; + struct ks7010 *ks; + int ret; + + ks_debug("sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x", + func->num, func->vendor, func->device, + func->max_blksize, func->cur_blksize); + + ks_sdio = kzalloc(sizeof(*ks_sdio), GFP_KERNEL); + if (!ks_sdio) + return -ENOMEM; + + ks_sdio->state = SDIO_DISABLED; + + ks_sdio->func = func; + sdio_set_drvdata(func, ks_sdio); + + ret = ks7010_sdio_init(ks_sdio, id); + if (ret) { + ks_debug("failed to init ks_sdio: %d", ret); + goto err_sdio_free; + } + + ks = ks7010_create(&func->dev); + if (!ks) { + ret = -ENOMEM; + goto err_sdio_cleanup; + } + + ks_sdio->ks = ks; + ks->priv = ks_sdio; + + ret = ks7010_sdio_config(ks); + if (ret) { + ks_debug("failed to config ks_sdio: %d", ret); + goto err_ks_destroy; + } + + ret = ks7010_init(ks); + if (ret) { + ks_debug("failed to init ks7010"); + goto err_ks_destroy; + } + + ret = ks7010_sdio_enable_interrupts(ks); + if (ret) { + ks_debug("failed to enable interrupts"); + goto err_ks_cleanup; + } + + ks->state = KS7010_STATE_READY; + ks_info("SDIO device successfully probed"); + + return 0; + +err_ks_cleanup: + ks7010_cleanup(ks); +err_ks_destroy: + ks7010_destroy(ks); +err_sdio_cleanup: + ks7010_sdio_cleanup(ks); +err_sdio_free: + kfree(ks_sdio); + + return ret; +} + +static void ks7010_sdio_remove(struct sdio_func *func) +{ + struct ks7010_sdio *ks_sdio = sdio_get_drvdata(func); + struct ks7010 *ks = ks_sdio->ks; + + ks_debug("sdio removed func %d vendor 0x%x device 0x%x", + func->num, func->vendor, func->device); + + ks7010_destroy(ks); + + ks7010_sdio_cleanup(ks); + + sdio_set_drvdata(func, NULL); + kfree(ks_sdio); + + ks_info("SDIO device removed"); +} + +static const struct sdio_device_id ks7010_sdio_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)}, + { /* all zero */ } +}; +MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids); + +static struct sdio_driver ks7010_sdio_driver = { + .name = "ks7010_sdio", + .id_table = ks7010_sdio_ids, + .probe = ks7010_sdio_probe, + .remove = ks7010_sdio_remove, +}; + +static int __init ks7010_sdio_module_init(void) +{ + int ret; + + ret = sdio_register_driver(&ks7010_sdio_driver); + if (ret) + ks_debug("failed to register sdio driver: %d", ret); + + ks_info("module loaded"); + ks_debug("debugging output enabled"); + + return ret; +} + +static void __exit ks7010_sdio_module_exit(void) +{ + sdio_unregister_driver(&ks7010_sdio_driver); + ks_info("module unloaded"); +} + +module_init(ks7010_sdio_module_init); +module_exit(ks7010_sdio_module_exit); + +MODULE_AUTHOR("Tobin C. Harding"); +MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream"); +MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/ks7010/sdio.h b/drivers/staging/ks7010/sdio.h new file mode 100644 index 0000000..76b9ebe --- /dev/null +++ b/drivers/staging/ks7010/sdio.h @@ -0,0 +1,37 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _KS7010_SDIO_H +#define _KS7010_SDIO_H + +#include <linux/firmware.h> + +#include "common.h" + +int ks7010_sdio_tx(struct ks7010 *ks, u8 *data, size_t size); +bool ks7010_sdio_fw_is_running(struct ks7010 *ks); + +u8 ks7010_sdio_read_trx_status_byte(struct ks7010 *ks); +bool ks7010_sdio_can_tx(struct ks7010 *ks, u8 trx_status_byte); + +int ks7010_sdio_rx_read(struct ks7010 *ks, u8 *buf, size_t size); +void ks7010_sdio_set_read_status_idle(struct ks7010 *ks); + +int ks7010_sdio_upload_fw(struct ks7010 *ks, u8 *fw, size_t fw_size); +bool ks7010_sdio_fw_is_running(struct ks7010 *ks); + +#endif /* _KS7010_SDIO_H */ diff --git a/drivers/staging/ks7010/tx.c b/drivers/staging/ks7010/tx.c new file mode 100644 index 0000000..bfba1af --- /dev/null +++ b/drivers/staging/ks7010/tx.c @@ -0,0 +1,170 @@ +/* + * Driver for KeyStream wireless LAN cards. + * + * Copyright (C) 2005-2008 KeyStream Corp. + * Copyright (C) 2009 Renesas Technology Corp. + * Copyright (C) 2017 Tobin C. Harding. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/workqueue.h> +#include <linux/circ_buf.h> + +#include "ks7010.h" +#include "sdio.h" +#include "hif.h" + +/** + * ks7010_tx_start() - Start transmission. + * @ndev: The net_device associated with this socket buffer. + * @skb: socket buffer passed down from the networking stack. + * + * Called by the networking stack (tx queue producer). + */ +int ks7010_tx_start(struct sk_buff *skb, struct net_device *ndev) +{ + struct ks7010 *ks = ks7010_ndev_to_ks(ndev); + struct tx_data txd; + int ret; + + memset(&txd, 0, sizeof(txd)); + + ks_debug("%s: skb=0x%p, data=0x%p, len=0x%x\n", __func__, + skb, skb->data, skb->len); + + if (eth_skb_pad(skb)) + return NETDEV_TX_OK; + + ret = ks7010_hif_tx_start(ks, skb, &txd); + if (ret) + goto out; + + ret = ks7010_tx_enqueue(ks, txd.datap, txd.size); + if (ret) { + kfree(txd.datap); + goto out; + } + + netdev_sent_queue(ndev, skb->len); + +out: + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/** + * ks7010_tx_enqueue() - Enqueue tx data in the tx buffer. + * @ks: The ks7010 device. + * @data: The tx data. + * @data_size: Size of data. + */ +int ks7010_tx_enqueue(struct ks7010 *ks, u8 *data, size_t data_size) +{ + struct tx_queue *q = &ks->txq; + struct tx_data *txd; + int head, tail; + unsigned long flags; + int ret; + + spin_lock_irqsave(&q->producer_lock, flags); + + head = q->head; + tail = READ_ONCE(q->tail); + + if (CIRC_SPACE(head, tail, KS7010_TX_QUEUE_SIZE) < 1) { + ret = -EOVERFLOW; + goto unlock; + } + + txd = &q->buf[head]; + + txd->datap = data; + txd->size = data_size; + + /* Finish reading descriptor before incrementing tail. */ + smp_store_release(&q->head, (head + 1) & (KS7010_RX_QUEUE_SIZE - 1)); + + ret = 0; +unlock: + spin_unlock_irqrestore(&q->producer_lock, flags); + + return ret; +} + +/** + * ks7010_tx_hw() - Send tx packet to the device. + * @ks: The ks7010 device. + * + * Called in interrupt context. Txq consumer, empty queue and send to device. + */ +void ks7010_tx_hw(struct ks7010 *ks) +{ + struct tx_queue *q = &ks->txq; + struct tx_data *txd; + int head, tail; + unsigned long flags; + bool tx_succeeded; + int ret; + + spin_lock_irqsave(&q->consumer_lock, flags); + + /* Read index before reading contents at that index. */ + head = smp_load_acquire(&q->head); + tail = q->tail; + + if (CIRC_CNT(head, tail, KS7010_TX_QUEUE_SIZE) < 1) + goto unlock; + + txd = &q->buf[tail]; + + ret = ks7010_sdio_tx(ks, txd->datap, txd->size); + if (ret) { + WARN_ONCE(ret, "tx write failed, leaving data in queue"); + tx_succeeded = false; + goto unlock; + } + + tx_succeeded = true; + kfree(txd->datap); + + /* Finish reading descriptor before incrementing tail. */ + smp_store_release(&q->tail, (tail + 1) & (KS7010_TX_QUEUE_SIZE - 1)); + +unlock: + spin_unlock_irqrestore(&q->consumer_lock, flags); + + /* TODO update stats (based on tx_succeeded) */ +} + +/** + * ks7010_tx_init() - Initialize transmit path. + * @ks: The ks7010 device. + */ +int ks7010_tx_init(struct ks7010 *ks) +{ + struct tx_queue *q = &ks->txq; + + spin_lock_init(&q->producer_lock); + spin_lock_init(&q->consumer_lock); + + return 0; +} + +/** + * ks7010_tx_cleanup() - Cleanup the transmit path. + * @ks: The ks7010 device. + */ +void ks7010_tx_cleanup(struct ks7010 *ks) +{ +} -- 2.7.4 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel