From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- drivers/staging/wlan-ng/Kconfig | 13 + drivers/staging/wlan-ng/Makefile | 8 + drivers/staging/wlan-ng/README | 8 + drivers/staging/wlan-ng/TODO | 16 + drivers/staging/wlan-ng/cfg80211.c | 718 +++++ drivers/staging/wlan-ng/hfa384x.h | 1236 +++++++++ drivers/staging/wlan-ng/hfa384x_usb.c | 3880 ++++++++++++++++++++++++++++ drivers/staging/wlan-ng/p80211conv.c | 643 +++++ drivers/staging/wlan-ng/p80211conv.h | 141 + drivers/staging/wlan-ng/p80211hdr.h | 189 ++ drivers/staging/wlan-ng/p80211ioctl.h | 69 + drivers/staging/wlan-ng/p80211metadef.h | 227 ++ drivers/staging/wlan-ng/p80211metastruct.h | 236 ++ drivers/staging/wlan-ng/p80211mgmt.h | 199 ++ drivers/staging/wlan-ng/p80211msg.h | 39 + drivers/staging/wlan-ng/p80211netdev.c | 988 +++++++ drivers/staging/wlan-ng/p80211netdev.h | 215 ++ drivers/staging/wlan-ng/p80211req.c | 223 ++ drivers/staging/wlan-ng/p80211req.h | 33 + drivers/staging/wlan-ng/p80211types.h | 292 +++ drivers/staging/wlan-ng/p80211wep.c | 207 ++ drivers/staging/wlan-ng/prism2fw.c | 1213 +++++++++ drivers/staging/wlan-ng/prism2mgmt.c | 1315 ++++++++++ drivers/staging/wlan-ng/prism2mgmt.h | 99 + drivers/staging/wlan-ng/prism2mib.c | 742 ++++++ drivers/staging/wlan-ng/prism2sta.c | 1984 ++++++++++++++ drivers/staging/wlan-ng/prism2usb.c | 299 +++ 27 files changed, 15232 insertions(+) create mode 100644 drivers/staging/wlan-ng/Kconfig create mode 100644 drivers/staging/wlan-ng/Makefile create mode 100644 drivers/staging/wlan-ng/README create mode 100644 drivers/staging/wlan-ng/TODO create mode 100644 drivers/staging/wlan-ng/cfg80211.c create mode 100644 drivers/staging/wlan-ng/hfa384x.h create mode 100644 drivers/staging/wlan-ng/hfa384x_usb.c create mode 100644 drivers/staging/wlan-ng/p80211conv.c create mode 100644 drivers/staging/wlan-ng/p80211conv.h create mode 100644 drivers/staging/wlan-ng/p80211hdr.h create mode 100644 drivers/staging/wlan-ng/p80211ioctl.h create mode 100644 drivers/staging/wlan-ng/p80211metadef.h create mode 100644 drivers/staging/wlan-ng/p80211metastruct.h create mode 100644 drivers/staging/wlan-ng/p80211mgmt.h create mode 100644 drivers/staging/wlan-ng/p80211msg.h create mode 100644 drivers/staging/wlan-ng/p80211netdev.c create mode 100644 drivers/staging/wlan-ng/p80211netdev.h create mode 100644 drivers/staging/wlan-ng/p80211req.c create mode 100644 drivers/staging/wlan-ng/p80211req.h create mode 100644 drivers/staging/wlan-ng/p80211types.h create mode 100644 drivers/staging/wlan-ng/p80211wep.c create mode 100644 drivers/staging/wlan-ng/prism2fw.c create mode 100644 drivers/staging/wlan-ng/prism2mgmt.c create mode 100644 drivers/staging/wlan-ng/prism2mgmt.h create mode 100644 drivers/staging/wlan-ng/prism2mib.c create mode 100644 drivers/staging/wlan-ng/prism2sta.c create mode 100644 drivers/staging/wlan-ng/prism2usb.c (limited to 'drivers/staging/wlan-ng') diff --git a/drivers/staging/wlan-ng/Kconfig b/drivers/staging/wlan-ng/Kconfig new file mode 100644 index 0000000000..082c16a316 --- /dev/null +++ b/drivers/staging/wlan-ng/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +config PRISM2_USB + tristate "Prism2.5/3 USB driver" + depends on WLAN && USB && CFG80211 + select WIRELESS_EXT + select WEXT_PRIV + select CRC32 + help + This is the wlan-ng prism 2.5/3 USB driver for a wide range of + old USB wireless devices. + + To compile this driver as a module, choose M here: the module + will be called prism2_usb. diff --git a/drivers/staging/wlan-ng/Makefile b/drivers/staging/wlan-ng/Makefile new file mode 100644 index 0000000000..1d24b0f86e --- /dev/null +++ b/drivers/staging/wlan-ng/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PRISM2_USB) += prism2_usb.o + +prism2_usb-y := prism2usb.o \ + p80211conv.o \ + p80211req.o \ + p80211wep.o \ + p80211netdev.o diff --git a/drivers/staging/wlan-ng/README b/drivers/staging/wlan-ng/README new file mode 100644 index 0000000000..d0621f827c --- /dev/null +++ b/drivers/staging/wlan-ng/README @@ -0,0 +1,8 @@ +TODO: + - checkpatch.pl cleanups + - sparse warnings + - move to use the in-kernel wireless stack + +Please send any patches or complaints about this driver to Greg +Kroah-Hartman and don't bother the upstream wireless +kernel developers about it, they want nothing to do with it. diff --git a/drivers/staging/wlan-ng/TODO b/drivers/staging/wlan-ng/TODO new file mode 100644 index 0000000000..ab9d5d145b --- /dev/null +++ b/drivers/staging/wlan-ng/TODO @@ -0,0 +1,16 @@ +To-do list: + +* Correct the coding style according to Linux guidelines; please read the document + at https://www.kernel.org/doc/html/latest/process/coding-style.html. +* Remove unnecessary debugging/printing macros; for those that are still needed + use the proper kernel API (pr_debug(), dev_dbg(), netdev_dbg()). +* Remove dead code such as unusued functions, variables, fields, etc.. +* Use in-kernel API and remove unnecessary wrappers where possible. +* Fix bugs due to code that sleeps in atomic context. +* Remove the HAL layer and migrate its functionality into the relevant parts of + the driver. +* Switch to use LIB80211. +* Switch to use MAC80211. +* Switch to use CFG80211. +* Improve the error handling of various functions, particularly those that use + existing kernel APIs. diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c new file mode 100644 index 0000000000..471bb31017 --- /dev/null +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: GPL-2.0 +/* cfg80211 Interface for prism2_usb module */ +#include "hfa384x.h" +#include "prism2mgmt.h" + +/* Prism2 channel/frequency/bitrate declarations */ +static const struct ieee80211_channel prism2_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + +static const struct ieee80211_rate prism2_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20 }, + { .bitrate = 55 }, + { .bitrate = 110 } +}; + +#define PRISM2_NUM_CIPHER_SUITES 2 +static const u32 prism2_cipher_suites[PRISM2_NUM_CIPHER_SUITES] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104 +}; + +/* prism2 device private data */ +struct prism2_wiphy_private { + struct wlandevice *wlandev; + + struct ieee80211_supported_band band; + struct ieee80211_channel channels[ARRAY_SIZE(prism2_channels)]; + struct ieee80211_rate rates[ARRAY_SIZE(prism2_rates)]; + + struct cfg80211_scan_request *scan_request; +}; + +static const void * const prism2_wiphy_privid = &prism2_wiphy_privid; + +/* Helper Functions */ +static int prism2_result2err(int prism2_result) +{ + int err = 0; + + switch (prism2_result) { + case P80211ENUM_resultcode_invalid_parameters: + err = -EINVAL; + break; + case P80211ENUM_resultcode_implementation_failure: + err = -EIO; + break; + case P80211ENUM_resultcode_not_supported: + err = -EOPNOTSUPP; + break; + default: + err = 0; + break; + } + + return err; +} + +static int prism2_domibset_uint32(struct wlandevice *wlandev, + u32 did, u32 data) +{ + struct p80211msg_dot11req_mibset msg; + struct p80211item_uint32 *mibitem = + (struct p80211item_uint32 *)&msg.mibattribute.data; + + msg.msgcode = DIDMSG_DOT11REQ_MIBSET; + mibitem->did = did; + mibitem->data = data; + + return p80211req_dorequest(wlandev, (u8 *)&msg); +} + +static int prism2_domibset_pstr32(struct wlandevice *wlandev, + u32 did, u8 len, const u8 *data) +{ + struct p80211msg_dot11req_mibset msg; + struct p80211item_pstr32 *mibitem = + (struct p80211item_pstr32 *)&msg.mibattribute.data; + + msg.msgcode = DIDMSG_DOT11REQ_MIBSET; + mibitem->did = did; + mibitem->data.len = len; + memcpy(mibitem->data.data, data, len); + + return p80211req_dorequest(wlandev, (u8 *)&msg); +} + +/* The interface functions, called by the cfg80211 layer */ +static int prism2_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct wlandevice *wlandev = dev->ml_priv; + u32 data; + int result; + int err = 0; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + if (wlandev->macmode == WLAN_MACMODE_IBSS_STA) + goto exit; + wlandev->macmode = WLAN_MACMODE_IBSS_STA; + data = 0; + break; + case NL80211_IFTYPE_STATION: + if (wlandev->macmode == WLAN_MACMODE_ESS_STA) + goto exit; + wlandev->macmode = WLAN_MACMODE_ESS_STA; + data = 1; + break; + default: + netdev_warn(dev, "Operation mode: %d not support\n", type); + return -EOPNOTSUPP; + } + + /* Set Operation mode to the PORT TYPE RID */ + result = prism2_domibset_uint32(wlandev, + DIDMIB_P2_STATIC_CNFPORTTYPE, + data); + + if (result) + err = -EFAULT; + + dev->ieee80211_ptr->iftype = type; + +exit: + return err; +} + +static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr, struct key_params *params) +{ + struct wlandevice *wlandev = dev->ml_priv; + u32 did; + + if (key_index >= NUM_WEPKEYS) + return -EINVAL; + + if (params->cipher != WLAN_CIPHER_SUITE_WEP40 && + params->cipher != WLAN_CIPHER_SUITE_WEP104) { + pr_debug("Unsupported cipher suite\n"); + return -EFAULT; + } + + if (prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, + key_index)) + return -EFAULT; + + /* send key to driver */ + did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1); + + if (prism2_domibset_pstr32(wlandev, did, params->key_len, params->key)) + return -EFAULT; + return 0; +} + +static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params*)) +{ + struct wlandevice *wlandev = dev->ml_priv; + struct key_params params; + int len; + + if (key_index >= NUM_WEPKEYS) + return -EINVAL; + + len = wlandev->wep_keylens[key_index]; + memset(¶ms, 0, sizeof(params)); + + if (len == 13) + params.cipher = WLAN_CIPHER_SUITE_WEP104; + else if (len == 5) + params.cipher = WLAN_CIPHER_SUITE_WEP104; + else + return -ENOENT; + params.key_len = len; + params.key = wlandev->wep_keys[key_index]; + params.seq_len = 0; + + callback(cookie, ¶ms); + + return 0; +} + +static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct wlandevice *wlandev = dev->ml_priv; + u32 did; + int err = 0; + int result = 0; + + /* There is no direct way in the hardware (AFAIK) of removing + * a key, so we will cheat by setting the key to a bogus value + */ + + if (key_index >= NUM_WEPKEYS) + return -EINVAL; + + /* send key to driver */ + did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1); + result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000"); + + if (result) + err = -EFAULT; + + return err; +} + +static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, + int link_id, u8 key_index, bool unicast, + bool multicast) +{ + struct wlandevice *wlandev = dev->ml_priv; + + return prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, + key_index); +} + +static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct wlandevice *wlandev = dev->ml_priv; + struct p80211msg_lnxreq_commsquality quality; + int result; + + memset(sinfo, 0, sizeof(*sinfo)); + + if (!wlandev || (wlandev->msdstate != WLAN_MSD_RUNNING)) + return -EOPNOTSUPP; + + /* build request message */ + quality.msgcode = DIDMSG_LNXREQ_COMMSQUALITY; + quality.dbm.data = P80211ENUM_truth_true; + quality.dbm.status = P80211ENUM_msgitem_status_data_ok; + + /* send message to nsd */ + if (!wlandev->mlmerequest) + return -EOPNOTSUPP; + + result = wlandev->mlmerequest(wlandev, (struct p80211msg *)&quality); + + if (result == 0) { + sinfo->txrate.legacy = quality.txrate.data; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + sinfo->signal = quality.level.data; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } + + return result; +} + +static int prism2_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev; + struct prism2_wiphy_private *priv = wiphy_priv(wiphy); + struct wlandevice *wlandev; + struct p80211msg_dot11req_scan msg1; + struct p80211msg_dot11req_scan_results *msg2; + struct cfg80211_bss *bss; + struct cfg80211_scan_info info = {}; + + int result; + int err = 0; + int numbss = 0; + int i = 0; + u8 ie_buf[46]; + int ie_len; + + if (!request) + return -EINVAL; + + dev = request->wdev->netdev; + wlandev = dev->ml_priv; + + if (priv->scan_request && priv->scan_request != request) + return -EBUSY; + + if (wlandev->macmode == WLAN_MACMODE_ESS_AP) { + netdev_err(dev, "Can't scan in AP mode\n"); + return -EOPNOTSUPP; + } + + msg2 = kzalloc(sizeof(*msg2), GFP_KERNEL); + if (!msg2) + return -ENOMEM; + + priv->scan_request = request; + + memset(&msg1, 0x00, sizeof(msg1)); + msg1.msgcode = DIDMSG_DOT11REQ_SCAN; + msg1.bsstype.data = P80211ENUM_bsstype_any; + + memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data)); + msg1.bssid.data.len = 6; + + if (request->n_ssids > 0) { + msg1.scantype.data = P80211ENUM_scantype_active; + msg1.ssid.data.len = request->ssids->ssid_len; + memcpy(msg1.ssid.data.data, + request->ssids->ssid, request->ssids->ssid_len); + } else { + msg1.scantype.data = 0; + } + msg1.probedelay.data = 0; + + for (i = 0; + (i < request->n_channels) && i < ARRAY_SIZE(prism2_channels); + i++) + msg1.channellist.data.data[i] = + ieee80211_frequency_to_channel(request->channels[i]->center_freq); + msg1.channellist.data.len = request->n_channels; + + msg1.maxchanneltime.data = 250; + msg1.minchanneltime.data = 200; + + result = p80211req_dorequest(wlandev, (u8 *)&msg1); + if (result) { + err = prism2_result2err(msg1.resultcode.data); + goto exit; + } + /* Now retrieve scan results */ + numbss = msg1.numbss.data; + + for (i = 0; i < numbss; i++) { + int freq; + + msg2->msgcode = DIDMSG_DOT11REQ_SCAN_RESULTS; + msg2->bssindex.data = i; + + result = p80211req_dorequest(wlandev, (u8 *)&msg2); + if ((result != 0) || + (msg2->resultcode.data != P80211ENUM_resultcode_success)) { + break; + } + + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = msg2->ssid.data.len; + ie_len = ie_buf[1] + 2; + memcpy(&ie_buf[2], &msg2->ssid.data.data, msg2->ssid.data.len); + freq = ieee80211_channel_to_frequency(msg2->dschannel.data, + NL80211_BAND_2GHZ); + bss = cfg80211_inform_bss(wiphy, + ieee80211_get_channel(wiphy, freq), + CFG80211_BSS_FTYPE_UNKNOWN, + (const u8 *)&msg2->bssid.data.data, + msg2->timestamp.data, msg2->capinfo.data, + msg2->beaconperiod.data, + ie_buf, + ie_len, + (msg2->signal.data - 65536) * 100, /* Conversion to signed type */ + GFP_KERNEL); + + if (!bss) { + err = -ENOMEM; + goto exit; + } + + cfg80211_put_bss(wiphy, bss); + } + + if (result) + err = prism2_result2err(msg2->resultcode.data); + +exit: + info.aborted = !!(err); + cfg80211_scan_done(request, &info); + priv->scan_request = NULL; + kfree(msg2); + return err; +} + +static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct prism2_wiphy_private *priv = wiphy_priv(wiphy); + struct wlandevice *wlandev = priv->wlandev; + u32 data; + int result; + int err = 0; + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + if (wiphy->rts_threshold == -1) + data = 2347; + else + data = wiphy->rts_threshold; + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD, + data); + if (result) { + err = -EFAULT; + goto exit; + } + } + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + if (wiphy->frag_threshold == -1) + data = 2346; + else + data = wiphy->frag_threshold; + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD, + data); + if (result) { + err = -EFAULT; + goto exit; + } + } + +exit: + return err; +} + +static int prism2_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wlandevice *wlandev = dev->ml_priv; + struct ieee80211_channel *channel = sme->channel; + struct p80211msg_lnxreq_autojoin msg_join; + u32 did; + int length = sme->ssid_len; + int chan = -1; + int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) || + (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104); + int result; + int err = 0; + + /* Set the channel */ + if (channel) { + chan = ieee80211_frequency_to_channel(channel->center_freq); + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL, + chan); + if (result) + goto exit; + } + + /* Set the authorization */ + if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) || + ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep)) + msg_join.authtype.data = P80211ENUM_authalg_opensystem; + else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) || + ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep)) + msg_join.authtype.data = P80211ENUM_authalg_sharedkey; + else + netdev_warn(dev, + "Unhandled authorisation type for connect (%d)\n", + sme->auth_type); + + /* Set the encryption - we only support wep */ + if (is_wep) { + if (sme->key) { + if (sme->key_idx >= NUM_WEPKEYS) + return -EINVAL; + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, + sme->key_idx); + if (result) + goto exit; + + /* send key to driver */ + did = didmib_dot11smt_wepdefaultkeystable_key(sme->key_idx + 1); + result = prism2_domibset_pstr32(wlandev, + did, sme->key_len, + (u8 *)sme->key); + if (result) + goto exit; + } + + /* Assume we should set privacy invoked and exclude unencrypted + * We could possible use sme->privacy here, but the assumption + * seems reasonable anyways + */ + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, + P80211ENUM_truth_true); + if (result) + goto exit; + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, + P80211ENUM_truth_true); + if (result) + goto exit; + + } else { + /* Assume we should unset privacy invoked + * and exclude unencrypted + */ + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, + P80211ENUM_truth_false); + if (result) + goto exit; + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, + P80211ENUM_truth_false); + if (result) + goto exit; + } + + /* Now do the actual join. Note there is no way that I can + * see to request a specific bssid + */ + msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; + + memcpy(msg_join.ssid.data.data, sme->ssid, length); + msg_join.ssid.data.len = length; + + result = p80211req_dorequest(wlandev, (u8 *)&msg_join); + +exit: + if (result) + err = -EFAULT; + + return err; +} + +static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct wlandevice *wlandev = dev->ml_priv; + struct p80211msg_lnxreq_autojoin msg_join; + int result; + int err = 0; + + /* Do a join, with a bogus ssid. Thats the only way I can think of */ + msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN; + + memcpy(msg_join.ssid.data.data, "---", 3); + msg_join.ssid.data.len = 3; + + result = p80211req_dorequest(wlandev, (u8 *)&msg_join); + + if (result) + err = -EFAULT; + + return err; +} + +static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + return -EOPNOTSUPP; +} + +static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, int mbm) +{ + struct prism2_wiphy_private *priv = wiphy_priv(wiphy); + struct wlandevice *wlandev = priv->wlandev; + u32 data; + int result; + int err = 0; + + if (type == NL80211_TX_POWER_AUTOMATIC) + data = 30; + else + data = MBM_TO_DBM(mbm); + + result = prism2_domibset_uint32(wlandev, + DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL, + data); + + if (result) { + err = -EFAULT; + goto exit; + } + +exit: + return err; +} + +static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm) +{ + struct prism2_wiphy_private *priv = wiphy_priv(wiphy); + struct wlandevice *wlandev = priv->wlandev; + struct p80211msg_dot11req_mibget msg; + struct p80211item_uint32 *mibitem; + int result; + int err = 0; + + mibitem = (struct p80211item_uint32 *)&msg.mibattribute.data; + msg.msgcode = DIDMSG_DOT11REQ_MIBGET; + mibitem->did = DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL; + + result = p80211req_dorequest(wlandev, (u8 *)&msg); + + if (result) { + err = -EFAULT; + goto exit; + } + + *dbm = mibitem->data; + +exit: + return err; +} + +/* Interface callback functions, passing data back up to the cfg80211 layer */ +void prism2_connect_result(struct wlandevice *wlandev, u8 failed) +{ + u16 status = failed ? + WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS; + + cfg80211_connect_result(wlandev->netdev, wlandev->bssid, + NULL, 0, NULL, 0, status, GFP_KERNEL); +} + +void prism2_disconnected(struct wlandevice *wlandev) +{ + cfg80211_disconnected(wlandev->netdev, 0, NULL, + 0, false, GFP_KERNEL); +} + +void prism2_roamed(struct wlandevice *wlandev) +{ + struct cfg80211_roam_info roam_info = { + .links[0].bssid = wlandev->bssid, + }; + + cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); +} + +/* Structures for declaring wiphy interface */ +static const struct cfg80211_ops prism2_usb_cfg_ops = { + .change_virtual_intf = prism2_change_virtual_intf, + .add_key = prism2_add_key, + .get_key = prism2_get_key, + .del_key = prism2_del_key, + .set_default_key = prism2_set_default_key, + .get_station = prism2_get_station, + .scan = prism2_scan, + .set_wiphy_params = prism2_set_wiphy_params, + .connect = prism2_connect, + .disconnect = prism2_disconnect, + .join_ibss = prism2_join_ibss, + .leave_ibss = prism2_leave_ibss, + .set_tx_power = prism2_set_tx_power, + .get_tx_power = prism2_get_tx_power, +}; + +/* Functions to create/free wiphy interface */ +static struct wiphy *wlan_create_wiphy(struct device *dev, + struct wlandevice *wlandev) +{ + struct wiphy *wiphy; + struct prism2_wiphy_private *priv; + + wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv)); + if (!wiphy) + return NULL; + + priv = wiphy_priv(wiphy); + priv->wlandev = wlandev; + memcpy(priv->channels, prism2_channels, sizeof(prism2_channels)); + memcpy(priv->rates, prism2_rates, sizeof(prism2_rates)); + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(prism2_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = ARRAY_SIZE(prism2_rates); + priv->band.band = NL80211_BAND_2GHZ; + priv->band.ht_cap.ht_supported = false; + wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; + + set_wiphy_dev(wiphy, dev); + wiphy->privid = prism2_wiphy_privid; + wiphy->max_scan_ssids = 1; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES; + wiphy->cipher_suites = prism2_cipher_suites; + + if (wiphy_register(wiphy) < 0) { + wiphy_free(wiphy); + return NULL; + } + + return wiphy; +} + +static void wlan_free_wiphy(struct wiphy *wiphy) +{ + wiphy_unregister(wiphy); + wiphy_free(wiphy); +} diff --git a/drivers/staging/wlan-ng/hfa384x.h b/drivers/staging/wlan-ng/hfa384x.h new file mode 100644 index 0000000000..a4799589e4 --- /dev/null +++ b/drivers/staging/wlan-ng/hfa384x.h @@ -0,0 +1,1236 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Defines the constants and data structures for the hfa384x + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * [Implementation and usage notes] + * + * [References] + * CW10 Programmer's Manual v1.5 + * IEEE 802.11 D10.0 + * + * -------------------------------------------------------------------- + */ + +#ifndef _HFA384x_H +#define _HFA384x_H + +#define HFA384x_FIRMWARE_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) + +#include +#include + +/*--- Mins & Maxs -----------------------------------*/ +#define HFA384x_PORTID_MAX ((u16)7) +#define HFA384x_NUMPORTS_MAX ((u16)(HFA384x_PORTID_MAX + 1)) +#define HFA384x_PDR_LEN_MAX ((u16)512) /* in bytes, from EK */ +#define HFA384x_PDA_RECS_MAX ((u16)200) /* a guess */ +#define HFA384x_PDA_LEN_MAX ((u16)1024) /* in bytes, from EK*/ +#define HFA384x_SCANRESULT_MAX ((u16)31) +#define HFA384x_HSCANRESULT_MAX ((u16)31) +#define HFA384x_CHINFORESULT_MAX ((u16)16) +#define HFA384x_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */ +#define HFA384x_RIDDATA_MAXLEN HFA384x_RID_GUESSING_MAXLEN +#define HFA384x_USB_RWMEM_MAXLEN 2048 + +/*--- Support Constants -----------------------------*/ +#define HFA384x_PORTTYPE_IBSS ((u16)0) +#define HFA384x_PORTTYPE_BSS ((u16)1) +#define HFA384x_PORTTYPE_PSUEDOIBSS ((u16)3) +#define HFA384x_WEPFLAGS_PRIVINVOKED ((u16)BIT(0)) +#define HFA384x_WEPFLAGS_EXCLUDE ((u16)BIT(1)) +#define HFA384x_WEPFLAGS_DISABLE_TXCRYPT ((u16)BIT(4)) +#define HFA384x_WEPFLAGS_DISABLE_RXCRYPT ((u16)BIT(7)) +#define HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM ((u16)3) +#define HFA384x_PORTSTATUS_DISABLED ((u16)1) +#define HFA384x_RATEBIT_1 ((u16)1) +#define HFA384x_RATEBIT_2 ((u16)2) +#define HFA384x_RATEBIT_5dot5 ((u16)4) +#define HFA384x_RATEBIT_11 ((u16)8) + +/*--- MAC Internal memory constants and macros ------*/ +/* masks and macros used to manipulate MAC internal memory addresses. */ +/* MAC internal memory addresses are 23 bit quantities. The MAC uses + * a paged address space where the upper 16 bits are the page number + * and the lower 7 bits are the offset. There are various Host API + * elements that require two 16-bit quantities to specify a MAC + * internal memory address. Unfortunately, some of the API's use a + * page/offset format where the offset value is JUST the lower seven + * bits and the page is the remaining 16 bits. Some of the API's + * assume that the 23 bit address has been split at the 16th bit. We + * refer to these two formats as AUX format and CMD format. The + * macros below help handle some of this. + */ + +/* Mask bits for discarding unwanted pieces in a flat address */ +#define HFA384x_ADDR_FLAT_AUX_PAGE_MASK (0x007fff80) +#define HFA384x_ADDR_FLAT_AUX_OFF_MASK (0x0000007f) +#define HFA384x_ADDR_FLAT_CMD_PAGE_MASK (0xffff0000) +#define HFA384x_ADDR_FLAT_CMD_OFF_MASK (0x0000ffff) + +/* Mask bits for discarding unwanted pieces in AUX format + * 16-bit address parts + */ +#define HFA384x_ADDR_AUX_PAGE_MASK (0xffff) +#define HFA384x_ADDR_AUX_OFF_MASK (0x007f) + +/* Make a 32-bit flat address from AUX format 16-bit page and offset */ +#define HFA384x_ADDR_AUX_MKFLAT(p, o) \ + ((((u32)(((u16)(p)) & HFA384x_ADDR_AUX_PAGE_MASK)) << 7) | \ + ((u32)(((u16)(o)) & HFA384x_ADDR_AUX_OFF_MASK))) + +/* Make CMD format offset and page from a 32-bit flat address */ +#define HFA384x_ADDR_CMD_MKPAGE(f) \ + ((u16)((((u32)(f)) & HFA384x_ADDR_FLAT_CMD_PAGE_MASK) >> 16)) +#define HFA384x_ADDR_CMD_MKOFF(f) \ + ((u16)(((u32)(f)) & HFA384x_ADDR_FLAT_CMD_OFF_MASK)) + +/*--- Controller Memory addresses -------------------*/ +#define HFA3842_PDA_BASE (0x007f0000UL) +#define HFA3841_PDA_BASE (0x003f0000UL) +#define HFA3841_PDA_BOGUS_BASE (0x00390000UL) + +/*--- Driver Download states -----------------------*/ +#define HFA384x_DLSTATE_DISABLED 0 +#define HFA384x_DLSTATE_RAMENABLED 1 +#define HFA384x_DLSTATE_FLASHENABLED 2 + +/*--- Register Field Masks --------------------------*/ +#define HFA384x_CMD_AINFO ((u16)GENMASK(14, 8)) +#define HFA384x_CMD_MACPORT ((u16)GENMASK(10, 8)) +#define HFA384x_CMD_PROGMODE ((u16)GENMASK(9, 8)) +#define HFA384x_CMD_CMDCODE ((u16)GENMASK(5, 0)) +#define HFA384x_STATUS_RESULT ((u16)GENMASK(14, 8)) + +/*--- Command Code Constants --------------------------*/ +/*--- Controller Commands --------------------------*/ +#define HFA384x_CMDCODE_INIT ((u16)0x00) +#define HFA384x_CMDCODE_ENABLE ((u16)0x01) +#define HFA384x_CMDCODE_DISABLE ((u16)0x02) + +/*--- Regulate Commands --------------------------*/ +#define HFA384x_CMDCODE_INQ ((u16)0x11) + +/*--- Configure Commands --------------------------*/ +#define HFA384x_CMDCODE_DOWNLD ((u16)0x22) + +/*--- Debugging Commands -----------------------------*/ +#define HFA384x_CMDCODE_MONITOR ((u16)(0x38)) +#define HFA384x_MONITOR_ENABLE ((u16)(0x0b)) +#define HFA384x_MONITOR_DISABLE ((u16)(0x0f)) + +/*--- Result Codes --------------------------*/ +#define HFA384x_CMD_ERR ((u16)(0x7F)) + +/*--- Programming Modes -------------------------- + * MODE 0: Disable programming + * MODE 1: Enable volatile memory programming + * MODE 2: Enable non-volatile memory programming + * MODE 3: Program non-volatile memory section + *------------------------------------------------- + */ +#define HFA384x_PROGMODE_DISABLE ((u16)0x00) +#define HFA384x_PROGMODE_RAM ((u16)0x01) +#define HFA384x_PROGMODE_NV ((u16)0x02) +#define HFA384x_PROGMODE_NVWRITE ((u16)0x03) + +/*--- Record ID Constants --------------------------*/ +/*-------------------------------------------------------------------- + * Configuration RIDs: Network Parameters, Static Configuration Entities + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_CNFPORTTYPE ((u16)0xFC00) +#define HFA384x_RID_CNFOWNMACADDR ((u16)0xFC01) +#define HFA384x_RID_CNFDESIREDSSID ((u16)0xFC02) +#define HFA384x_RID_CNFOWNCHANNEL ((u16)0xFC03) +#define HFA384x_RID_CNFOWNSSID ((u16)0xFC04) +#define HFA384x_RID_CNFMAXDATALEN ((u16)0xFC07) + +/*-------------------------------------------------------------------- + * Configuration RID lengths: Network Params, Static Config Entities + * This is the length of JUST the DATA part of the RID (does not + * include the len or code fields) + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_CNFOWNMACADDR_LEN ((u16)6) +#define HFA384x_RID_CNFDESIREDSSID_LEN ((u16)34) +#define HFA384x_RID_CNFOWNSSID_LEN ((u16)34) + +/*-------------------------------------------------------------------- + * Configuration RIDs: Network Parameters, Dynamic Configuration Entities + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_CREATEIBSS ((u16)0xFC81) +#define HFA384x_RID_FRAGTHRESH ((u16)0xFC82) +#define HFA384x_RID_RTSTHRESH ((u16)0xFC83) +#define HFA384x_RID_TXRATECNTL ((u16)0xFC84) +#define HFA384x_RID_PROMISCMODE ((u16)0xFC85) + +/*---------------------------------------------------------------------- + * Information RIDs: NIC Information + *---------------------------------------------------------------------- + */ +#define HFA384x_RID_MAXLOADTIME ((u16)0xFD00) +#define HFA384x_RID_DOWNLOADBUFFER ((u16)0xFD01) +#define HFA384x_RID_PRIIDENTITY ((u16)0xFD02) +#define HFA384x_RID_PRISUPRANGE ((u16)0xFD03) +#define HFA384x_RID_PRI_CFIACTRANGES ((u16)0xFD04) +#define HFA384x_RID_NICSERIALNUMBER ((u16)0xFD0A) +#define HFA384x_RID_NICIDENTITY ((u16)0xFD0B) +#define HFA384x_RID_MFISUPRANGE ((u16)0xFD0C) +#define HFA384x_RID_CFISUPRANGE ((u16)0xFD0D) +#define HFA384x_RID_STAIDENTITY ((u16)0xFD20) +#define HFA384x_RID_STASUPRANGE ((u16)0xFD21) +#define HFA384x_RID_STA_MFIACTRANGES ((u16)0xFD22) +#define HFA384x_RID_STA_CFIACTRANGES ((u16)0xFD23) + +/*---------------------------------------------------------------------- + * Information RID Lengths: NIC Information + * This is the length of JUST the DATA part of the RID (does not + * include the len or code fields) + *--------------------------------------------------------------------- + */ +#define HFA384x_RID_NICSERIALNUMBER_LEN ((u16)12) + +/*-------------------------------------------------------------------- + * Information RIDs: MAC Information + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_PORTSTATUS ((u16)0xFD40) +#define HFA384x_RID_CURRENTSSID ((u16)0xFD41) +#define HFA384x_RID_CURRENTBSSID ((u16)0xFD42) +#define HFA384x_RID_CURRENTTXRATE ((u16)0xFD44) +#define HFA384x_RID_SHORTRETRYLIMIT ((u16)0xFD48) +#define HFA384x_RID_LONGRETRYLIMIT ((u16)0xFD49) +#define HFA384x_RID_MAXTXLIFETIME ((u16)0xFD4A) +#define HFA384x_RID_PRIVACYOPTIMP ((u16)0xFD4F) +#define HFA384x_RID_DBMCOMMSQUALITY ((u16)0xFD51) + +/*-------------------------------------------------------------------- + * Information RID Lengths: MAC Information + * This is the length of JUST the DATA part of the RID (does not + * include the len or code fields) + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_DBMCOMMSQUALITY_LEN \ + ((u16)sizeof(struct hfa384x_dbmcommsquality)) +#define HFA384x_RID_JOINREQUEST_LEN \ + ((u16)sizeof(struct hfa384x_join_request_data)) + +/*-------------------------------------------------------------------- + * Information RIDs: Modem Information + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_CURRENTCHANNEL ((u16)0xFDC1) + +/*-------------------------------------------------------------------- + * API ENHANCEMENTS (NOT ALREADY IMPLEMENTED) + *-------------------------------------------------------------------- + */ +#define HFA384x_RID_CNFWEPDEFAULTKEYID ((u16)0xFC23) +#define HFA384x_RID_CNFWEPDEFAULTKEY0 ((u16)0xFC24) +#define HFA384x_RID_CNFWEPDEFAULTKEY1 ((u16)0xFC25) +#define HFA384x_RID_CNFWEPDEFAULTKEY2 ((u16)0xFC26) +#define HFA384x_RID_CNFWEPDEFAULTKEY3 ((u16)0xFC27) +#define HFA384x_RID_CNFWEPFLAGS ((u16)0xFC28) +#define HFA384x_RID_CNFAUTHENTICATION ((u16)0xFC2A) +#define HFA384x_RID_CNFROAMINGMODE ((u16)0xFC2D) +#define HFA384x_RID_CNFAPBCNINT ((u16)0xFC33) +#define HFA384x_RID_CNFDBMADJUST ((u16)0xFC46) +#define HFA384x_RID_CNFWPADATA ((u16)0xFC48) +#define HFA384x_RID_CNFBASICRATES ((u16)0xFCB3) +#define HFA384x_RID_CNFSUPPRATES ((u16)0xFCB4) +#define HFA384x_RID_CNFPASSIVESCANCTRL ((u16)0xFCBA) +#define HFA384x_RID_TXPOWERMAX ((u16)0xFCBE) +#define HFA384x_RID_JOINREQUEST ((u16)0xFCE2) +#define HFA384x_RID_AUTHENTICATESTA ((u16)0xFCE3) +#define HFA384x_RID_HOSTSCAN ((u16)0xFCE5) + +#define HFA384x_RID_CNFWEPDEFAULTKEY_LEN ((u16)6) +#define HFA384x_RID_CNFWEP128DEFAULTKEY_LEN ((u16)14) + +/*-------------------------------------------------------------------- + * PD Record codes + *-------------------------------------------------------------------- + */ +#define HFA384x_PDR_PCB_PARTNUM ((u16)0x0001) +#define HFA384x_PDR_PDAVER ((u16)0x0002) +#define HFA384x_PDR_NIC_SERIAL ((u16)0x0003) +#define HFA384x_PDR_MKK_MEASUREMENTS ((u16)0x0004) +#define HFA384x_PDR_NIC_RAMSIZE ((u16)0x0005) +#define HFA384x_PDR_MFISUPRANGE ((u16)0x0006) +#define HFA384x_PDR_CFISUPRANGE ((u16)0x0007) +#define HFA384x_PDR_NICID ((u16)0x0008) +#define HFA384x_PDR_MAC_ADDRESS ((u16)0x0101) +#define HFA384x_PDR_REGDOMAIN ((u16)0x0103) +#define HFA384x_PDR_ALLOWED_CHANNEL ((u16)0x0104) +#define HFA384x_PDR_DEFAULT_CHANNEL ((u16)0x0105) +#define HFA384x_PDR_TEMPTYPE ((u16)0x0107) +#define HFA384x_PDR_IFR_SETTING ((u16)0x0200) +#define HFA384x_PDR_RFR_SETTING ((u16)0x0201) +#define HFA384x_PDR_HFA3861_BASELINE ((u16)0x0202) +#define HFA384x_PDR_HFA3861_SHADOW ((u16)0x0203) +#define HFA384x_PDR_HFA3861_IFRF ((u16)0x0204) +#define HFA384x_PDR_HFA3861_CHCALSP ((u16)0x0300) +#define HFA384x_PDR_HFA3861_CHCALI ((u16)0x0301) +#define HFA384x_PDR_MAX_TX_POWER ((u16)0x0302) +#define HFA384x_PDR_MASTER_CHAN_LIST ((u16)0x0303) +#define HFA384x_PDR_3842_NIC_CONFIG ((u16)0x0400) +#define HFA384x_PDR_USB_ID ((u16)0x0401) +#define HFA384x_PDR_PCI_ID ((u16)0x0402) +#define HFA384x_PDR_PCI_IFCONF ((u16)0x0403) +#define HFA384x_PDR_PCI_PMCONF ((u16)0x0404) +#define HFA384x_PDR_RFENRGY ((u16)0x0406) +#define HFA384x_PDR_USB_POWER_TYPE ((u16)0x0407) +#define HFA384x_PDR_USB_MAX_POWER ((u16)0x0409) +#define HFA384x_PDR_USB_MANUFACTURER ((u16)0x0410) +#define HFA384x_PDR_USB_PRODUCT ((u16)0x0411) +#define HFA384x_PDR_ANT_DIVERSITY ((u16)0x0412) +#define HFA384x_PDR_HFO_DELAY ((u16)0x0413) +#define HFA384x_PDR_SCALE_THRESH ((u16)0x0414) + +#define HFA384x_PDR_HFA3861_MANF_TESTSP ((u16)0x0900) +#define HFA384x_PDR_HFA3861_MANF_TESTI ((u16)0x0901) +#define HFA384x_PDR_END_OF_PDA ((u16)0x0000) + +/*--- Register Test/Get/Set Field macros ------------------------*/ + +#define HFA384x_CMD_AINFO_SET(value) ((u16)((u16)(value) << 8)) +#define HFA384x_CMD_MACPORT_SET(value) \ + ((u16)HFA384x_CMD_AINFO_SET(value)) +#define HFA384x_CMD_PROGMODE_SET(value) \ + ((u16)HFA384x_CMD_AINFO_SET((u16)value)) +#define HFA384x_CMD_CMDCODE_SET(value) ((u16)(value)) + +#define HFA384x_STATUS_RESULT_SET(value) (((u16)(value)) << 8) + +/* Host Maintained State Info */ +#define HFA384x_STATE_PREINIT 0 +#define HFA384x_STATE_INIT 1 +#define HFA384x_STATE_RUNNING 2 + +/*-------------------------------------------------------------*/ +/* Commonly used basic types */ +struct hfa384x_bytestr { + __le16 len; + u8 data[]; +} __packed; + +struct hfa384x_bytestr32 { + __le16 len; + u8 data[32]; +} __packed; + +/*-------------------------------------------------------------------- + * Configuration Record Structures: + * Network Parameters, Static Configuration Entities + *-------------------------------------------------------------------- + */ + +/*-- Hardware/Firmware Component Information ----------*/ +struct hfa384x_compident { + u16 id; + u16 variant; + u16 major; + u16 minor; +} __packed; + +struct hfa384x_caplevel { + u16 role; + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __packed; + +/*-- Configuration Record: cnfAuthentication --*/ +#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001 +#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002 +#define HFA384x_CNFAUTHENTICATION_LEAP 0x0004 + +/*-------------------------------------------------------------------- + * Configuration Record Structures: + * Network Parameters, Dynamic Configuration Entities + *-------------------------------------------------------------------- + */ + +#define HFA384x_CREATEIBSS_JOINCREATEIBSS 0 + +/*-- Configuration Record: HostScanRequest (data portion only) --*/ +struct hfa384x_host_scan_request_data { + __le16 channel_list; + __le16 tx_rate; + struct hfa384x_bytestr32 ssid; +} __packed; + +/*-- Configuration Record: JoinRequest (data portion only) --*/ +struct hfa384x_join_request_data { + u8 bssid[WLAN_BSSID_LEN]; + u16 channel; +} __packed; + +/*-- Configuration Record: authenticateStation (data portion only) --*/ +struct hfa384x_authenticate_station_data { + u8 address[ETH_ALEN]; + __le16 status; + __le16 algorithm; +} __packed; + +/*-- Configuration Record: WPAData (data portion only) --*/ +struct hfa384x_wpa_data { + __le16 datalen; + u8 data[]; /* max 80 */ +} __packed; + +/*-------------------------------------------------------------------- + * Information Record Structures: NIC Information + *-------------------------------------------------------------------- + */ + +/*-- Information Record: DownLoadBuffer --*/ +/* NOTE: The page and offset are in AUX format */ +struct hfa384x_downloadbuffer { + u16 page; + u16 offset; + u16 len; +} __packed; + +/*-------------------------------------------------------------------- + * Information Record Structures: NIC Information + *-------------------------------------------------------------------- + */ + +#define HFA384x_PSTATUS_CONN_IBSS ((u16)3) + +/*-- Information Record: commsquality --*/ +struct hfa384x_commsquality { + __le16 cq_curr_bss; + __le16 asl_curr_bss; + __le16 anl_curr_fc; +} __packed; + +/*-- Information Record: dmbcommsquality --*/ +struct hfa384x_dbmcommsquality { + u16 cq_dbm_curr_bss; + u16 asl_dbm_curr_bss; + u16 anl_dbm_curr_fc; +} __packed; + +/*-------------------------------------------------------------------- + * FRAME STRUCTURES: Communication Frames + *-------------------------------------------------------------------- + * Communication Frames: Transmit Frames + *-------------------------------------------------------------------- + */ +/*-- Communication Frame: Transmit Frame Structure --*/ +struct hfa384x_tx_frame { + u16 status; + u16 reserved1; + u16 reserved2; + u32 sw_support; + u8 tx_retrycount; + u8 tx_rate; + u16 tx_control; + + /*-- 802.11 Header Information --*/ + struct p80211_hdr hdr; + __le16 data_len; /* little endian format */ + + /*-- 802.3 Header Information --*/ + + u8 dest_addr[6]; + u8 src_addr[6]; + u16 data_length; /* big endian format */ +} __packed; +/*-------------------------------------------------------------------- + * Communication Frames: Field Masks for Transmit Frames + *-------------------------------------------------------------------- + */ +/*-- Status Field --*/ +#define HFA384x_TXSTATUS_ACKERR ((u16)BIT(5)) +#define HFA384x_TXSTATUS_FORMERR ((u16)BIT(3)) +#define HFA384x_TXSTATUS_DISCON ((u16)BIT(2)) +#define HFA384x_TXSTATUS_AGEDERR ((u16)BIT(1)) +#define HFA384x_TXSTATUS_RETRYERR ((u16)BIT(0)) +/*-- Transmit Control Field --*/ +#define HFA384x_TX_MACPORT ((u16)GENMASK(10, 8)) +#define HFA384x_TX_STRUCTYPE ((u16)GENMASK(4, 3)) +#define HFA384x_TX_TXEX ((u16)BIT(2)) +#define HFA384x_TX_TXOK ((u16)BIT(1)) +/*-------------------------------------------------------------------- + * Communication Frames: Test/Get/Set Field Values for Transmit Frames + *-------------------------------------------------------------------- + */ +/*-- Status Field --*/ +#define HFA384x_TXSTATUS_ISERROR(v) \ + (((u16)(v)) & \ + (HFA384x_TXSTATUS_ACKERR | HFA384x_TXSTATUS_FORMERR | \ + HFA384x_TXSTATUS_DISCON | HFA384x_TXSTATUS_AGEDERR | \ + HFA384x_TXSTATUS_RETRYERR)) + +#define HFA384x_TX_SET(v, m, s) ((((u16)(v)) << ((u16)(s))) & ((u16)(m))) + +#define HFA384x_TX_MACPORT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_MACPORT, 8) +#define HFA384x_TX_STRUCTYPE_SET(v) HFA384x_TX_SET(v, \ + HFA384x_TX_STRUCTYPE, 3) +#define HFA384x_TX_TXEX_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXEX, 2) +#define HFA384x_TX_TXOK_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXOK, 1) +/*-------------------------------------------------------------------- + * Communication Frames: Receive Frames + *-------------------------------------------------------------------- + */ +/*-- Communication Frame: Receive Frame Structure --*/ +struct hfa384x_rx_frame { + /*-- MAC rx descriptor (hfa384x byte order) --*/ + u16 status; + u32 time; + u8 silence; + u8 signal; + u8 rate; + u8 rx_flow; + u16 reserved1; + u16 reserved2; + + /*-- 802.11 Header Information (802.11 byte order) --*/ + struct p80211_hdr hdr; + __le16 data_len; /* hfa384x (little endian) format */ + + /*-- 802.3 Header Information --*/ + u8 dest_addr[6]; + u8 src_addr[6]; + u16 data_length; /* IEEE? (big endian) format */ +} __packed; +/*-------------------------------------------------------------------- + * Communication Frames: Field Masks for Receive Frames + *-------------------------------------------------------------------- + */ + +/*-- Status Fields --*/ +#define HFA384x_RXSTATUS_MACPORT ((u16)GENMASK(10, 8)) +#define HFA384x_RXSTATUS_FCSERR ((u16)BIT(0)) +/*-------------------------------------------------------------------- + * Communication Frames: Test/Get/Set Field Values for Receive Frames + *-------------------------------------------------------------------- + */ +#define HFA384x_RXSTATUS_MACPORT_GET(value) ((u16)((((u16)(value)) \ + & HFA384x_RXSTATUS_MACPORT) >> 8)) +#define HFA384x_RXSTATUS_ISFCSERR(value) ((u16)(((u16)(value)) \ + & HFA384x_RXSTATUS_FCSERR)) +/*-------------------------------------------------------------------- + * FRAME STRUCTURES: Information Types and Information Frame Structures + *-------------------------------------------------------------------- + * Information Types + *-------------------------------------------------------------------- + */ +#define HFA384x_IT_HANDOVERADDR ((u16)0xF000UL) +#define HFA384x_IT_COMMTALLIES ((u16)0xF100UL) +#define HFA384x_IT_SCANRESULTS ((u16)0xF101UL) +#define HFA384x_IT_CHINFORESULTS ((u16)0xF102UL) +#define HFA384x_IT_HOSTSCANRESULTS ((u16)0xF103UL) +#define HFA384x_IT_LINKSTATUS ((u16)0xF200UL) +#define HFA384x_IT_ASSOCSTATUS ((u16)0xF201UL) +#define HFA384x_IT_AUTHREQ ((u16)0xF202UL) +#define HFA384x_IT_PSUSERCNT ((u16)0xF203UL) +#define HFA384x_IT_KEYIDCHANGED ((u16)0xF204UL) +#define HFA384x_IT_ASSOCREQ ((u16)0xF205UL) +#define HFA384x_IT_MICFAILURE ((u16)0xF206UL) + +/*-------------------------------------------------------------------- + * Information Frames Structures + *-------------------------------------------------------------------- + * Information Frames: Notification Frame Structures + *-------------------------------------------------------------------- + */ + +/*-- Inquiry Frame, Diagnose: Communication Tallies --*/ +struct hfa384x_comm_tallies_16 { + __le16 txunicastframes; + __le16 txmulticastframes; + __le16 txfragments; + __le16 txunicastoctets; + __le16 txmulticastoctets; + __le16 txdeferredtrans; + __le16 txsingleretryframes; + __le16 txmultipleretryframes; + __le16 txretrylimitexceeded; + __le16 txdiscards; + __le16 rxunicastframes; + __le16 rxmulticastframes; + __le16 rxfragments; + __le16 rxunicastoctets; + __le16 rxmulticastoctets; + __le16 rxfcserrors; + __le16 rxdiscardsnobuffer; + __le16 txdiscardswrongsa; + __le16 rxdiscardswepundecr; + __le16 rxmsginmsgfrag; + __le16 rxmsginbadmsgfrag; +} __packed; + +struct hfa384x_comm_tallies_32 { + __le32 txunicastframes; + __le32 txmulticastframes; + __le32 txfragments; + __le32 txunicastoctets; + __le32 txmulticastoctets; + __le32 txdeferredtrans; + __le32 txsingleretryframes; + __le32 txmultipleretryframes; + __le32 txretrylimitexceeded; + __le32 txdiscards; + __le32 rxunicastframes; + __le32 rxmulticastframes; + __le32 rxfragments; + __le32 rxunicastoctets; + __le32 rxmulticastoctets; + __le32 rxfcserrors; + __le32 rxdiscardsnobuffer; + __le32 txdiscardswrongsa; + __le32 rxdiscardswepundecr; + __le32 rxmsginmsgfrag; + __le32 rxmsginbadmsgfrag; +} __packed; + +/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/ +struct hfa384x_scan_result_sub { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[WLAN_BSSID_LEN]; + u16 bcnint; + u16 capinfo; + struct hfa384x_bytestr32 ssid; + u8 supprates[10]; /* 802.11 info element */ + u16 proberesp_rate; +} __packed; + +struct hfa384x_scan_result { + u16 rsvd; + u16 scanreason; + struct hfa384x_scan_result_sub result[HFA384x_SCANRESULT_MAX]; +} __packed; + +/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/ +struct hfa384x_ch_info_result_sub { + u16 chid; + u16 anl; + u16 pnl; + u16 active; +} __packed; + +#define HFA384x_CHINFORESULT_BSSACTIVE BIT(0) +#define HFA384x_CHINFORESULT_PCFACTIVE BIT(1) + +struct hfa384x_ch_info_result { + u16 scanchannels; + struct hfa384x_ch_info_result_sub result[HFA384x_CHINFORESULT_MAX]; +} __packed; + +/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/ +struct hfa384x_hscan_result_sub { + __le16 chid; + __le16 anl; + __le16 sl; + u8 bssid[WLAN_BSSID_LEN]; + __le16 bcnint; + __le16 capinfo; + struct hfa384x_bytestr32 ssid; + u8 supprates[10]; /* 802.11 info element */ + u16 proberesp_rate; + __le16 atim; +} __packed; + +struct hfa384x_hscan_result { + u16 nresult; + u16 rsvd; + struct hfa384x_hscan_result_sub result[HFA384x_HSCANRESULT_MAX]; +} __packed; + +/*-- Unsolicited Frame, MAC Mgmt: LinkStatus --*/ + +#define HFA384x_LINK_NOTCONNECTED ((u16)0) +#define HFA384x_LINK_CONNECTED ((u16)1) +#define HFA384x_LINK_DISCONNECTED ((u16)2) +#define HFA384x_LINK_AP_CHANGE ((u16)3) +#define HFA384x_LINK_AP_OUTOFRANGE ((u16)4) +#define HFA384x_LINK_AP_INRANGE ((u16)5) +#define HFA384x_LINK_ASSOCFAIL ((u16)6) + +struct hfa384x_link_status { + __le16 linkstatus; +} __packed; + +/*-- Unsolicited Frame, MAC Mgmt: AssociationStatus (--*/ + +#define HFA384x_ASSOCSTATUS_STAASSOC ((u16)1) +#define HFA384x_ASSOCSTATUS_REASSOC ((u16)2) +#define HFA384x_ASSOCSTATUS_AUTHFAIL ((u16)5) + +struct hfa384x_assoc_status { + u16 assocstatus; + u8 sta_addr[ETH_ALEN]; + /* old_ap_addr is only valid if assocstatus == 2 */ + u8 old_ap_addr[ETH_ALEN]; + u16 reason; + u16 reserved; +} __packed; + +/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/ + +struct hfa384x_auth_request { + u8 sta_addr[ETH_ALEN]; + __le16 algorithm; +} __packed; + +/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/ + +struct hfa384x_ps_user_count { + __le16 usercnt; +} __packed; + +struct hfa384x_key_id_changed { + u8 sta_addr[ETH_ALEN]; + u16 keyid; +} __packed; + +/*-- Collection of all Inf frames ---------------*/ +union hfa384x_infodata { + struct hfa384x_comm_tallies_16 commtallies16; + struct hfa384x_comm_tallies_32 commtallies32; + struct hfa384x_scan_result scanresult; + struct hfa384x_ch_info_result chinforesult; + struct hfa384x_hscan_result hscanresult; + struct hfa384x_link_status linkstatus; + struct hfa384x_assoc_status assocstatus; + struct hfa384x_auth_request authreq; + struct hfa384x_ps_user_count psusercnt; + struct hfa384x_key_id_changed keyidchanged; +} __packed; + +struct hfa384x_inf_frame { + u16 framelen; + u16 infotype; + union hfa384x_infodata info; +} __packed; + +/*-------------------------------------------------------------------- + * USB Packet structures and constants. + *-------------------------------------------------------------------- + */ + +/* Should be sent to the bulkout endpoint */ +#define HFA384x_USB_TXFRM 0 +#define HFA384x_USB_CMDREQ 1 +#define HFA384x_USB_WRIDREQ 2 +#define HFA384x_USB_RRIDREQ 3 +#define HFA384x_USB_WMEMREQ 4 +#define HFA384x_USB_RMEMREQ 5 + +/* Received from the bulkin endpoint */ +#define HFA384x_USB_ISTXFRM(a) (((a) & 0x9000) == 0x1000) +#define HFA384x_USB_ISRXFRM(a) (!((a) & 0x9000)) +#define HFA384x_USB_INFOFRM 0x8000 +#define HFA384x_USB_CMDRESP 0x8001 +#define HFA384x_USB_WRIDRESP 0x8002 +#define HFA384x_USB_RRIDRESP 0x8003 +#define HFA384x_USB_WMEMRESP 0x8004 +#define HFA384x_USB_RMEMRESP 0x8005 +#define HFA384x_USB_BUFAVAIL 0x8006 +#define HFA384x_USB_ERROR 0x8007 + +/*------------------------------------*/ +/* Request (bulk OUT) packet contents */ + +struct hfa384x_usb_txfrm { + struct hfa384x_tx_frame desc; + u8 data[WLAN_DATA_MAXLEN]; +} __packed; + +struct hfa384x_usb_cmdreq { + __le16 type; + __le16 cmd; + __le16 parm0; + __le16 parm1; + __le16 parm2; + u8 pad[54]; +} __packed; + +struct hfa384x_usb_wridreq { + __le16 type; + __le16 frmlen; + __le16 rid; + u8 data[HFA384x_RIDDATA_MAXLEN]; +} __packed; + +struct hfa384x_usb_rridreq { + __le16 type; + __le16 frmlen; + __le16 rid; + u8 pad[58]; +} __packed; + +struct hfa384x_usb_wmemreq { + __le16 type; + __le16 frmlen; + __le16 offset; + __le16 page; + u8 data[HFA384x_USB_RWMEM_MAXLEN]; +} __packed; + +struct hfa384x_usb_rmemreq { + __le16 type; + __le16 frmlen; + __le16 offset; + __le16 page; + u8 pad[56]; +} __packed; + +/*------------------------------------*/ +/* Response (bulk IN) packet contents */ + +struct hfa384x_usb_rxfrm { + struct hfa384x_rx_frame desc; + u8 data[WLAN_DATA_MAXLEN]; +} __packed; + +struct hfa384x_usb_infofrm { + u16 type; + struct hfa384x_inf_frame info; +} __packed; + +struct hfa384x_usb_statusresp { + u16 type; + __le16 status; + __le16 resp0; + __le16 resp1; + __le16 resp2; +} __packed; + +struct hfa384x_usb_rridresp { + u16 type; + __le16 frmlen; + __le16 rid; + u8 data[HFA384x_RIDDATA_MAXLEN]; +} __packed; + +struct hfa384x_usb_rmemresp { + u16 type; + u16 frmlen; + u8 data[HFA384x_USB_RWMEM_MAXLEN]; +} __packed; + +struct hfa384x_usb_bufavail { + u16 type; + u16 frmlen; +} __packed; + +struct hfa384x_usb_error { + u16 type; + u16 errortype; +} __packed; + +/*----------------------------------------------------------*/ +/* Unions for packaging all the known packet types together */ + +union hfa384x_usbout { + __le16 type; + struct hfa384x_usb_txfrm txfrm; + struct hfa384x_usb_cmdreq cmdreq; + struct hfa384x_usb_wridreq wridreq; + struct hfa384x_usb_rridreq rridreq; + struct hfa384x_usb_wmemreq wmemreq; + struct hfa384x_usb_rmemreq rmemreq; +} __packed; + +union hfa384x_usbin { + __le16 type; + struct hfa384x_usb_rxfrm rxfrm; + struct hfa384x_usb_txfrm txfrm; + struct hfa384x_usb_infofrm infofrm; + struct hfa384x_usb_statusresp cmdresp; + struct hfa384x_usb_statusresp wridresp; + struct hfa384x_usb_rridresp rridresp; + struct hfa384x_usb_statusresp wmemresp; + struct hfa384x_usb_rmemresp rmemresp; + struct hfa384x_usb_bufavail bufavail; + struct hfa384x_usb_error usberror; + u8 boguspad[3000]; +} __packed; + +/*-------------------------------------------------------------------- + * PD record structures. + *-------------------------------------------------------------------- + */ + +struct hfa384x_pdr_mfisuprange { + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __packed; + +struct hfa384x_pdr_cfisuprange { + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __packed; + +struct hfa384x_pdr_nicid { + u16 id; + u16 variant; + u16 major; + u16 minor; +} __packed; + +struct hfa384x_pdrec { + __le16 len; /* in words */ + __le16 code; + union pdr { + struct hfa384x_pdr_mfisuprange mfisuprange; + struct hfa384x_pdr_cfisuprange cfisuprange; + struct hfa384x_pdr_nicid nicid; + + } data; +} __packed; + +#ifdef __KERNEL__ +/*-------------------------------------------------------------------- + * --- MAC state structure, argument to all functions -- + * --- Also, a collection of support types -- + *-------------------------------------------------------------------- + */ +struct hfa384x_cmdresult { + u16 status; + u16 resp0; + u16 resp1; + u16 resp2; +}; + +/* USB Control Exchange (CTLX): + * A queue of the structure below is maintained for all of the + * Request/Response type USB packets supported by Prism2. + */ +/* The following hfa384x_* structures are arguments to + * the usercb() for the different CTLX types. + */ +struct hfa384x_rridresult { + u16 rid; + const void *riddata; + unsigned int riddata_len; +}; + +enum ctlx_state { + CTLX_START = 0, /* Start state, not queued */ + + CTLX_COMPLETE, /* CTLX successfully completed */ + CTLX_REQ_FAILED, /* OUT URB completed w/ error */ + + CTLX_PENDING, /* Queued, data valid */ + CTLX_REQ_SUBMITTED, /* OUT URB submitted */ + CTLX_REQ_COMPLETE, /* OUT URB complete */ + CTLX_RESP_COMPLETE /* IN URB received */ +}; + +struct hfa384x_usbctlx; +struct hfa384x; + +typedef void (*ctlx_cmdcb_t) (struct hfa384x *, const struct hfa384x_usbctlx *); + +typedef void (*ctlx_usercb_t) (struct hfa384x *hw, + void *ctlxresult, void *usercb_data); + +struct hfa384x_usbctlx { + struct list_head list; + + size_t outbufsize; + union hfa384x_usbout outbuf; /* pkt buf for OUT */ + union hfa384x_usbin inbuf; /* pkt buf for IN(a copy) */ + + enum ctlx_state state; /* Tracks running state */ + + struct completion done; + int reapable; /* Food for the reaper task */ + + ctlx_cmdcb_t cmdcb; /* Async command callback */ + ctlx_usercb_t usercb; /* Async user callback, */ + void *usercb_data; /* at CTLX completion */ +}; + +struct hfa384x_usbctlxq { + spinlock_t lock; + struct list_head pending; + struct list_head active; + struct list_head completing; + struct list_head reapable; +}; + +struct hfa384x_metacmd { + u16 cmd; + + u16 parm0; + u16 parm1; + u16 parm2; + + struct hfa384x_cmdresult result; +}; + +#define MAX_GRP_ADDR 32 +#define WLAN_COMMENT_MAX 80 /* Max. length of user comment string. */ + +#define WLAN_AUTH_MAX 60 /* Max. # of authenticated stations. */ +#define WLAN_ACCESS_MAX 60 /* Max. # of stations in an access list. */ +#define WLAN_ACCESS_NONE 0 /* No stations may be authenticated. */ +#define WLAN_ACCESS_ALL 1 /* All stations may be authenticated. */ +#define WLAN_ACCESS_ALLOW 2 /* Authenticate only "allowed" stations. */ +#define WLAN_ACCESS_DENY 3 /* Do not authenticate "denied" stations. */ + +/* XXX These are going away ASAP */ +struct prism2sta_authlist { + unsigned int cnt; + u8 addr[WLAN_AUTH_MAX][ETH_ALEN]; + u8 assoc[WLAN_AUTH_MAX]; +}; + +struct prism2sta_accesslist { + unsigned int modify; + unsigned int cnt; + u8 addr[WLAN_ACCESS_MAX][ETH_ALEN]; + unsigned int cnt1; + u8 addr1[WLAN_ACCESS_MAX][ETH_ALEN]; +}; + +struct hfa384x { + /* USB support data */ + struct usb_device *usb; + struct urb rx_urb; + struct sk_buff *rx_urb_skb; + struct urb tx_urb; + struct urb ctlx_urb; + union hfa384x_usbout txbuff; + struct hfa384x_usbctlxq ctlxq; + struct timer_list reqtimer; + struct timer_list resptimer; + + struct timer_list throttle; + + struct work_struct reaper_bh; + struct work_struct completion_bh; + + struct work_struct usb_work; + + unsigned long usb_flags; +#define THROTTLE_RX 0 +#define THROTTLE_TX 1 +#define WORK_RX_HALT 2 +#define WORK_TX_HALT 3 +#define WORK_RX_RESUME 4 +#define WORK_TX_RESUME 5 + + unsigned short req_timer_done:1; + unsigned short resp_timer_done:1; + + int endp_in; + int endp_out; + + int sniff_fcs; + int sniff_channel; + int sniff_truncate; + int sniffhdr; + + wait_queue_head_t cmdq; /* wait queue itself */ + + /* Controller state */ + u32 state; + u32 isap; + u8 port_enabled[HFA384x_NUMPORTS_MAX]; + + /* Download support */ + unsigned int dlstate; + struct hfa384x_downloadbuffer bufinfo; + u16 dltimeout; + + int scanflag; /* to signal scan complete */ + int join_ap; /* are we joined to a specific ap */ + int join_retries; /* number of join retries till we fail */ + struct hfa384x_join_request_data joinreq;/* join request saved data */ + + struct wlandevice *wlandev; + /* Timer to allow for the deferred processing of linkstatus messages */ + struct work_struct link_bh; + + struct work_struct commsqual_bh; + struct hfa384x_commsquality qual; + struct timer_list commsqual_timer; + + u16 link_status; + u16 link_status_new; + struct sk_buff_head authq; + + u32 txrate; + + /* And here we have stuff that used to be in priv */ + + /* State variables */ + unsigned int presniff_port_type; + u16 presniff_wepflags; + u32 dot11_desired_bss_type; + + int dbmadjust; + + /* Group Addresses - right now, there are up to a total + * of MAX_GRP_ADDR group addresses + */ + u8 dot11_grp_addr[MAX_GRP_ADDR][ETH_ALEN]; + unsigned int dot11_grpcnt; + + /* Component Identities */ + struct hfa384x_compident ident_nic; + struct hfa384x_compident ident_pri_fw; + struct hfa384x_compident ident_sta_fw; + struct hfa384x_compident ident_ap_fw; + u16 mm_mods; + + /* Supplier compatibility ranges */ + struct hfa384x_caplevel cap_sup_mfi; + struct hfa384x_caplevel cap_sup_cfi; + struct hfa384x_caplevel cap_sup_pri; + struct hfa384x_caplevel cap_sup_sta; + struct hfa384x_caplevel cap_sup_ap; + + /* Actor compatibility ranges */ + struct hfa384x_caplevel cap_act_pri_cfi; /* + * pri f/w to controller + * interface + */ + + struct hfa384x_caplevel cap_act_sta_cfi; /* + * sta f/w to controller + * interface + */ + + struct hfa384x_caplevel cap_act_sta_mfi; /* + * sta f/w to modem interface + */ + + struct hfa384x_caplevel cap_act_ap_cfi; /* + * ap f/w to controller + * interface + */ + + struct hfa384x_caplevel cap_act_ap_mfi; /* ap f/w to modem interface */ + + u32 psusercount; /* Power save user count. */ + struct hfa384x_comm_tallies_32 tallies; /* Communication tallies. */ + u8 comment[WLAN_COMMENT_MAX + 1]; /* User comment */ + + /* Channel Info request results (AP only) */ + struct { + atomic_t done; + u8 count; + struct hfa384x_ch_info_result results; + } channel_info; + + struct hfa384x_inf_frame *scanresults; + + struct prism2sta_authlist authlist; /* + * Authenticated station list. + */ + unsigned int accessmode; /* Access mode. */ + struct prism2sta_accesslist allow; /* Allowed station list. */ + struct prism2sta_accesslist deny; /* Denied station list. */ + +}; + +void hfa384x_create(struct hfa384x *hw, struct usb_device *usb); +void hfa384x_destroy(struct hfa384x *hw); + +int hfa384x_corereset(struct hfa384x *hw, int holdtime, int settletime, + int genesis); +int hfa384x_drvr_disable(struct hfa384x *hw, u16 macport); +int hfa384x_drvr_enable(struct hfa384x *hw, u16 macport); +int hfa384x_drvr_flashdl_enable(struct hfa384x *hw); +int hfa384x_drvr_flashdl_disable(struct hfa384x *hw); +int hfa384x_drvr_flashdl_write(struct hfa384x *hw, u32 daddr, void *buf, + u32 len); +int hfa384x_drvr_getconfig(struct hfa384x *hw, u16 rid, void *buf, u16 len); +int hfa384x_drvr_ramdl_enable(struct hfa384x *hw, u32 exeaddr); +int hfa384x_drvr_ramdl_disable(struct hfa384x *hw); +int hfa384x_drvr_ramdl_write(struct hfa384x *hw, u32 daddr, void *buf, u32 len); +int hfa384x_drvr_readpda(struct hfa384x *hw, void *buf, unsigned int len); +int hfa384x_drvr_setconfig(struct hfa384x *hw, u16 rid, void *buf, u16 len); + +static inline int +hfa384x_drvr_getconfig16(struct hfa384x *hw, u16 rid, void *val) +{ + int result = 0; + + result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(u16)); + if (result == 0) + le16_to_cpus(val); + return result; +} + +static inline int hfa384x_drvr_setconfig16(struct hfa384x *hw, u16 rid, u16 val) +{ + __le16 value = cpu_to_le16(val); + + return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value)); +} + +int +hfa384x_drvr_setconfig_async(struct hfa384x *hw, + u16 rid, + void *buf, + u16 len, ctlx_usercb_t usercb, void *usercb_data); + +static inline int +hfa384x_drvr_setconfig16_async(struct hfa384x *hw, u16 rid, u16 val) +{ + __le16 value = cpu_to_le16(val); + + return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value), + NULL, NULL); +} + +int hfa384x_drvr_start(struct hfa384x *hw); +int hfa384x_drvr_stop(struct hfa384x *hw); +int +hfa384x_drvr_txframe(struct hfa384x *hw, struct sk_buff *skb, + struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep); +void hfa384x_tx_timeout(struct wlandevice *wlandev); + +int hfa384x_cmd_initialize(struct hfa384x *hw); +int hfa384x_cmd_enable(struct hfa384x *hw, u16 macport); +int hfa384x_cmd_disable(struct hfa384x *hw, u16 macport); +int hfa384x_cmd_allocate(struct hfa384x *hw, u16 len); +int hfa384x_cmd_monitor(struct hfa384x *hw, u16 enable); +int +hfa384x_cmd_download(struct hfa384x *hw, + u16 mode, u16 lowaddr, u16 highaddr, u16 codelen); + +#endif /*__KERNEL__ */ + +#endif /*_HFA384x_H */ diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c new file mode 100644 index 0000000000..3e8c92675c --- /dev/null +++ b/drivers/staging/wlan-ng/hfa384x_usb.c @@ -0,0 +1,3880 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Functions that talk to the USB variant of the Intersil hfa384x MAC + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file implements functions that correspond to the prism2/hfa384x + * 802.11 MAC hardware and firmware host interface. + * + * The functions can be considered to represent several levels of + * abstraction. The lowest level functions are simply C-callable wrappers + * around the register accesses. The next higher level represents C-callable + * prism2 API functions that match the Intersil documentation as closely + * as is reasonable. The next higher layer implements common sequences + * of invocations of the API layer (e.g. write to bap, followed by cmd). + * + * Common sequences: + * hfa384x_drvr_xxx Highest level abstractions provided by the + * hfa384x code. They are driver defined wrappers + * for common sequences. These functions generally + * use the services of the lower levels. + * + * hfa384x_drvr_xxxconfig An example of the drvr level abstraction. These + * functions are wrappers for the RID get/set + * sequence. They call copy_[to|from]_bap() and + * cmd_access(). These functions operate on the + * RIDs and buffers without validation. The caller + * is responsible for that. + * + * API wrapper functions: + * hfa384x_cmd_xxx functions that provide access to the f/w commands. + * The function arguments correspond to each command + * argument, even command arguments that get packed + * into single registers. These functions _just_ + * issue the command by setting the cmd/parm regs + * & reading the status/resp regs. Additional + * activities required to fully use a command + * (read/write from/to bap, get/set int status etc.) + * are implemented separately. Think of these as + * C-callable prism2 commands. + * + * Lowest Layer Functions: + * hfa384x_docmd_xxx These functions implement the sequence required + * to issue any prism2 command. Primarily used by the + * hfa384x_cmd_xxx functions. + * + * hfa384x_bap_xxx BAP read/write access functions. + * Note: we usually use BAP0 for non-interrupt context + * and BAP1 for interrupt context. + * + * hfa384x_dl_xxx download related functions. + * + * Driver State Issues: + * Note that there are two pairs of functions that manage the + * 'initialized' and 'running' states of the hw/MAC combo. The four + * functions are create(), destroy(), start(), and stop(). create() + * sets up the data structures required to support the hfa384x_* + * functions and destroy() cleans them up. The start() function gets + * the actual hardware running and enables the interrupts. The stop() + * function shuts the hardware down. The sequence should be: + * create() + * start() + * . + * . Do interesting things w/ the hardware + * . + * stop() + * destroy() + * + * Note that destroy() can be called without calling stop() first. + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211mgmt.h" +#include "p80211conv.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211req.h" +#include "p80211metadef.h" +#include "p80211metastruct.h" +#include "hfa384x.h" +#include "prism2mgmt.h" + +enum cmd_mode { + DOWAIT = 0, + DOASYNC +}; + +#define THROTTLE_JIFFIES (HZ / 8) +#define URB_ASYNC_UNLINK 0 +#define USB_QUEUE_BULK 0 + +#define ROUNDUP64(a) (((a) + 63) & ~63) + +#ifdef DEBUG_USB +static void dbprint_urb(struct urb *urb); +#endif + +static void hfa384x_int_rxmonitor(struct wlandevice *wlandev, + struct hfa384x_usb_rxfrm *rxfrm); + +static void hfa384x_usb_defer(struct work_struct *data); + +static int submit_rx_urb(struct hfa384x *hw, gfp_t flags); + +static int submit_tx_urb(struct hfa384x *hw, struct urb *tx_urb, gfp_t flags); + +/*---------------------------------------------------*/ +/* Callbacks */ +static void hfa384x_usbout_callback(struct urb *urb); +static void hfa384x_ctlxout_callback(struct urb *urb); +static void hfa384x_usbin_callback(struct urb *urb); + +static void +hfa384x_usbin_txcompl(struct wlandevice *wlandev, union hfa384x_usbin *usbin); + +static void hfa384x_usbin_rx(struct wlandevice *wlandev, struct sk_buff *skb); + +static void hfa384x_usbin_info(struct wlandevice *wlandev, + union hfa384x_usbin *usbin); + +static void hfa384x_usbin_ctlx(struct hfa384x *hw, union hfa384x_usbin *usbin, + int urb_status); + +/*---------------------------------------------------*/ +/* Functions to support the prism2 usb command queue */ + +static void hfa384x_usbctlxq_run(struct hfa384x *hw); + +static void hfa384x_usbctlx_reqtimerfn(struct timer_list *t); + +static void hfa384x_usbctlx_resptimerfn(struct timer_list *t); + +static void hfa384x_usb_throttlefn(struct timer_list *t); + +static void hfa384x_usbctlx_completion_task(struct work_struct *work); + +static void hfa384x_usbctlx_reaper_task(struct work_struct *work); + +static int hfa384x_usbctlx_submit(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx); + +static void unlocked_usbctlx_complete(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx); + +struct usbctlx_completor { + int (*complete)(struct usbctlx_completor *completor); +}; + +static int +hfa384x_usbctlx_complete_sync(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx, + struct usbctlx_completor *completor); + +static int +unlocked_usbctlx_cancel_async(struct hfa384x *hw, struct hfa384x_usbctlx *ctlx); + +static void hfa384x_cb_status(struct hfa384x *hw, + const struct hfa384x_usbctlx *ctlx); + +static int +usbctlx_get_status(const struct hfa384x_usb_statusresp *cmdresp, + struct hfa384x_cmdresult *result); + +static void +usbctlx_get_rridresult(const struct hfa384x_usb_rridresp *rridresp, + struct hfa384x_rridresult *result); + +/*---------------------------------------------------*/ +/* Low level req/resp CTLX formatters and submitters */ +static inline int +hfa384x_docmd(struct hfa384x *hw, + struct hfa384x_metacmd *cmd); + +static int +hfa384x_dorrid(struct hfa384x *hw, + enum cmd_mode mode, + u16 rid, + void *riddata, + unsigned int riddatalen, + ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data); + +static int +hfa384x_dowrid(struct hfa384x *hw, + enum cmd_mode mode, + u16 rid, + void *riddata, + unsigned int riddatalen, + ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data); + +static int +hfa384x_dormem(struct hfa384x *hw, + u16 page, + u16 offset, + void *data, + unsigned int len); + +static int +hfa384x_dowmem(struct hfa384x *hw, + u16 page, + u16 offset, + void *data, + unsigned int len); + +static int hfa384x_isgood_pdrcode(u16 pdrcode); + +static inline const char *ctlxstr(enum ctlx_state s) +{ + static const char * const ctlx_str[] = { + "Initial state", + "Complete", + "Request failed", + "Request pending", + "Request packet submitted", + "Request packet completed", + "Response packet completed" + }; + + return ctlx_str[s]; +}; + +static inline struct hfa384x_usbctlx *get_active_ctlx(struct hfa384x *hw) +{ + return list_entry(hw->ctlxq.active.next, struct hfa384x_usbctlx, list); +} + +#ifdef DEBUG_USB +void dbprint_urb(struct urb *urb) +{ + pr_debug("urb->pipe=0x%08x\n", urb->pipe); + pr_debug("urb->status=0x%08x\n", urb->status); + pr_debug("urb->transfer_flags=0x%08x\n", urb->transfer_flags); + pr_debug("urb->transfer_buffer=0x%08x\n", + (unsigned int)urb->transfer_buffer); + pr_debug("urb->transfer_buffer_length=0x%08x\n", + urb->transfer_buffer_length); + pr_debug("urb->actual_length=0x%08x\n", urb->actual_length); + pr_debug("urb->setup_packet(ctl)=0x%08x\n", + (unsigned int)urb->setup_packet); + pr_debug("urb->start_frame(iso/irq)=0x%08x\n", urb->start_frame); + pr_debug("urb->interval(irq)=0x%08x\n", urb->interval); + pr_debug("urb->error_count(iso)=0x%08x\n", urb->error_count); + pr_debug("urb->context=0x%08x\n", (unsigned int)urb->context); + pr_debug("urb->complete=0x%08x\n", (unsigned int)urb->complete); +} +#endif + +/*---------------------------------------------------------------- + * submit_rx_urb + * + * Listen for input data on the BULK-IN pipe. If the pipe has + * stalled then schedule it to be reset. + * + * Arguments: + * hw device struct + * memflags memory allocation flags + * + * Returns: + * error code from submission + * + * Call context: + * Any + *---------------------------------------------------------------- + */ +static int submit_rx_urb(struct hfa384x *hw, gfp_t memflags) +{ + struct sk_buff *skb; + int result; + + skb = dev_alloc_skb(sizeof(union hfa384x_usbin)); + if (!skb) { + result = -ENOMEM; + goto done; + } + + /* Post the IN urb */ + usb_fill_bulk_urb(&hw->rx_urb, hw->usb, + hw->endp_in, + skb->data, sizeof(union hfa384x_usbin), + hfa384x_usbin_callback, hw->wlandev); + + hw->rx_urb_skb = skb; + + result = -ENOLINK; + if (!hw->wlandev->hwremoved && + !test_bit(WORK_RX_HALT, &hw->usb_flags)) { + result = usb_submit_urb(&hw->rx_urb, memflags); + + /* Check whether we need to reset the RX pipe */ + if (result == -EPIPE) { + netdev_warn(hw->wlandev->netdev, + "%s rx pipe stalled: requesting reset\n", + hw->wlandev->netdev->name); + if (!test_and_set_bit(WORK_RX_HALT, &hw->usb_flags)) + schedule_work(&hw->usb_work); + } + } + + /* Don't leak memory if anything should go wrong */ + if (result != 0) { + dev_kfree_skb(skb); + hw->rx_urb_skb = NULL; + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * submit_tx_urb + * + * Prepares and submits the URB of transmitted data. If the + * submission fails then it will schedule the output pipe to + * be reset. + * + * Arguments: + * hw device struct + * tx_urb URB of data for transmission + * memflags memory allocation flags + * + * Returns: + * error code from submission + * + * Call context: + * Any + *---------------------------------------------------------------- + */ +static int submit_tx_urb(struct hfa384x *hw, struct urb *tx_urb, gfp_t memflags) +{ + struct net_device *netdev = hw->wlandev->netdev; + int result; + + result = -ENOLINK; + if (netif_running(netdev)) { + if (!hw->wlandev->hwremoved && + !test_bit(WORK_TX_HALT, &hw->usb_flags)) { + result = usb_submit_urb(tx_urb, memflags); + + /* Test whether we need to reset the TX pipe */ + if (result == -EPIPE) { + netdev_warn(hw->wlandev->netdev, + "%s tx pipe stalled: requesting reset\n", + netdev->name); + set_bit(WORK_TX_HALT, &hw->usb_flags); + schedule_work(&hw->usb_work); + } else if (result == 0) { + netif_stop_queue(netdev); + } + } + } + + return result; +} + +/*---------------------------------------------------------------- + * hfa394x_usb_defer + * + * There are some things that the USB stack cannot do while + * in interrupt context, so we arrange this function to run + * in process context. + * + * Arguments: + * hw device structure + * + * Returns: + * nothing + * + * Call context: + * process (by design) + *---------------------------------------------------------------- + */ +static void hfa384x_usb_defer(struct work_struct *data) +{ + struct hfa384x *hw = container_of(data, struct hfa384x, usb_work); + struct net_device *netdev = hw->wlandev->netdev; + + /* Don't bother trying to reset anything if the plug + * has been pulled ... + */ + if (hw->wlandev->hwremoved) + return; + + /* Reception has stopped: try to reset the input pipe */ + if (test_bit(WORK_RX_HALT, &hw->usb_flags)) { + int ret; + + usb_kill_urb(&hw->rx_urb); /* Cannot be holding spinlock! */ + + ret = usb_clear_halt(hw->usb, hw->endp_in); + if (ret != 0) { + netdev_err(hw->wlandev->netdev, + "Failed to clear rx pipe for %s: err=%d\n", + netdev->name, ret); + } else { + netdev_info(hw->wlandev->netdev, "%s rx pipe reset complete.\n", + netdev->name); + clear_bit(WORK_RX_HALT, &hw->usb_flags); + set_bit(WORK_RX_RESUME, &hw->usb_flags); + } + } + + /* Resume receiving data back from the device. */ + if (test_bit(WORK_RX_RESUME, &hw->usb_flags)) { + int ret; + + ret = submit_rx_urb(hw, GFP_KERNEL); + if (ret != 0) { + netdev_err(hw->wlandev->netdev, + "Failed to resume %s rx pipe.\n", + netdev->name); + } else { + clear_bit(WORK_RX_RESUME, &hw->usb_flags); + } + } + + /* Transmission has stopped: try to reset the output pipe */ + if (test_bit(WORK_TX_HALT, &hw->usb_flags)) { + int ret; + + usb_kill_urb(&hw->tx_urb); + ret = usb_clear_halt(hw->usb, hw->endp_out); + if (ret != 0) { + netdev_err(hw->wlandev->netdev, + "Failed to clear tx pipe for %s: err=%d\n", + netdev->name, ret); + } else { + netdev_info(hw->wlandev->netdev, "%s tx pipe reset complete.\n", + netdev->name); + clear_bit(WORK_TX_HALT, &hw->usb_flags); + set_bit(WORK_TX_RESUME, &hw->usb_flags); + + /* Stopping the BULK-OUT pipe also blocked + * us from sending any more CTLX URBs, so + * we need to re-run our queue ... + */ + hfa384x_usbctlxq_run(hw); + } + } + + /* Resume transmitting. */ + if (test_and_clear_bit(WORK_TX_RESUME, &hw->usb_flags)) + netif_wake_queue(hw->wlandev->netdev); +} + +/*---------------------------------------------------------------- + * hfa384x_create + * + * Sets up the struct hfa384x data structure for use. Note this + * does _not_ initialize the actual hardware, just the data structures + * we use to keep track of its state. + * + * Arguments: + * hw device structure + * irq device irq number + * iobase i/o base address for register access + * membase memory base address for register access + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +void hfa384x_create(struct hfa384x *hw, struct usb_device *usb) +{ + hw->usb = usb; + + /* Set up the waitq */ + init_waitqueue_head(&hw->cmdq); + + /* Initialize the command queue */ + spin_lock_init(&hw->ctlxq.lock); + INIT_LIST_HEAD(&hw->ctlxq.pending); + INIT_LIST_HEAD(&hw->ctlxq.active); + INIT_LIST_HEAD(&hw->ctlxq.completing); + INIT_LIST_HEAD(&hw->ctlxq.reapable); + + /* Initialize the authentication queue */ + skb_queue_head_init(&hw->authq); + + INIT_WORK(&hw->reaper_bh, hfa384x_usbctlx_reaper_task); + INIT_WORK(&hw->completion_bh, hfa384x_usbctlx_completion_task); + INIT_WORK(&hw->link_bh, prism2sta_processing_defer); + INIT_WORK(&hw->usb_work, hfa384x_usb_defer); + + timer_setup(&hw->throttle, hfa384x_usb_throttlefn, 0); + + timer_setup(&hw->resptimer, hfa384x_usbctlx_resptimerfn, 0); + + timer_setup(&hw->reqtimer, hfa384x_usbctlx_reqtimerfn, 0); + + usb_init_urb(&hw->rx_urb); + usb_init_urb(&hw->tx_urb); + usb_init_urb(&hw->ctlx_urb); + + hw->link_status = HFA384x_LINK_NOTCONNECTED; + hw->state = HFA384x_STATE_INIT; + + INIT_WORK(&hw->commsqual_bh, prism2sta_commsqual_defer); + timer_setup(&hw->commsqual_timer, prism2sta_commsqual_timer, 0); +} + +/*---------------------------------------------------------------- + * hfa384x_destroy + * + * Partner to hfa384x_create(). This function cleans up the hw + * structure so that it can be freed by the caller using a simple + * kfree. Currently, this function is just a placeholder. If, at some + * point in the future, an hw in the 'shutdown' state requires a 'deep' + * kfree, this is where it should be done. Note that if this function + * is called on a _running_ hw structure, the drvr_stop() function is + * called. + * + * Arguments: + * hw device structure + * + * Returns: + * nothing, this function is not allowed to fail. + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +void hfa384x_destroy(struct hfa384x *hw) +{ + struct sk_buff *skb; + + if (hw->state == HFA384x_STATE_RUNNING) + hfa384x_drvr_stop(hw); + hw->state = HFA384x_STATE_PREINIT; + + kfree(hw->scanresults); + hw->scanresults = NULL; + + /* Now to clean out the auth queue */ + while ((skb = skb_dequeue(&hw->authq))) + dev_kfree_skb(skb); +} + +static struct hfa384x_usbctlx *usbctlx_alloc(void) +{ + struct hfa384x_usbctlx *ctlx; + + ctlx = kzalloc(sizeof(*ctlx), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (ctlx) + init_completion(&ctlx->done); + + return ctlx; +} + +static int +usbctlx_get_status(const struct hfa384x_usb_statusresp *cmdresp, + struct hfa384x_cmdresult *result) +{ + result->status = le16_to_cpu(cmdresp->status); + result->resp0 = le16_to_cpu(cmdresp->resp0); + result->resp1 = le16_to_cpu(cmdresp->resp1); + result->resp2 = le16_to_cpu(cmdresp->resp2); + + pr_debug("cmdresult:status=0x%04x resp0=0x%04x resp1=0x%04x resp2=0x%04x\n", + result->status, result->resp0, result->resp1, result->resp2); + + return result->status & HFA384x_STATUS_RESULT; +} + +static void +usbctlx_get_rridresult(const struct hfa384x_usb_rridresp *rridresp, + struct hfa384x_rridresult *result) +{ + result->rid = le16_to_cpu(rridresp->rid); + result->riddata = rridresp->data; + result->riddata_len = ((le16_to_cpu(rridresp->frmlen) - 1) * 2); +} + +/*---------------------------------------------------------------- + * Completor object: + * This completor must be passed to hfa384x_usbctlx_complete_sync() + * when processing a CTLX that returns a struct hfa384x_cmdresult structure. + *---------------------------------------------------------------- + */ +struct usbctlx_cmd_completor { + struct usbctlx_completor head; + + const struct hfa384x_usb_statusresp *cmdresp; + struct hfa384x_cmdresult *result; +}; + +static inline int usbctlx_cmd_completor_fn(struct usbctlx_completor *head) +{ + struct usbctlx_cmd_completor *complete; + + complete = (struct usbctlx_cmd_completor *)head; + return usbctlx_get_status(complete->cmdresp, complete->result); +} + +static inline struct usbctlx_completor * +init_cmd_completor(struct usbctlx_cmd_completor *completor, + const struct hfa384x_usb_statusresp *cmdresp, + struct hfa384x_cmdresult *result) +{ + completor->head.complete = usbctlx_cmd_completor_fn; + completor->cmdresp = cmdresp; + completor->result = result; + return &completor->head; +} + +/*---------------------------------------------------------------- + * Completor object: + * This completor must be passed to hfa384x_usbctlx_complete_sync() + * when processing a CTLX that reads a RID. + *---------------------------------------------------------------- + */ +struct usbctlx_rrid_completor { + struct usbctlx_completor head; + + const struct hfa384x_usb_rridresp *rridresp; + void *riddata; + unsigned int riddatalen; +}; + +static int usbctlx_rrid_completor_fn(struct usbctlx_completor *head) +{ + struct usbctlx_rrid_completor *complete; + struct hfa384x_rridresult rridresult; + + complete = (struct usbctlx_rrid_completor *)head; + usbctlx_get_rridresult(complete->rridresp, &rridresult); + + /* Validate the length, note body len calculation in bytes */ + if (rridresult.riddata_len != complete->riddatalen) { + pr_warn("RID len mismatch, rid=0x%04x hlen=%d fwlen=%d\n", + rridresult.rid, + complete->riddatalen, rridresult.riddata_len); + return -ENODATA; + } + + memcpy(complete->riddata, rridresult.riddata, complete->riddatalen); + return 0; +} + +static inline struct usbctlx_completor * +init_rrid_completor(struct usbctlx_rrid_completor *completor, + const struct hfa384x_usb_rridresp *rridresp, + void *riddata, + unsigned int riddatalen) +{ + completor->head.complete = usbctlx_rrid_completor_fn; + completor->rridresp = rridresp; + completor->riddata = riddata; + completor->riddatalen = riddatalen; + return &completor->head; +} + +/*---------------------------------------------------------------- + * Completor object: + * Interprets the results of a synchronous RID-write + *---------------------------------------------------------------- + */ +#define init_wrid_completor init_cmd_completor + +/*---------------------------------------------------------------- + * Completor object: + * Interprets the results of a synchronous memory-write + *---------------------------------------------------------------- + */ +#define init_wmem_completor init_cmd_completor + +/*---------------------------------------------------------------- + * Completor object: + * Interprets the results of a synchronous memory-read + *---------------------------------------------------------------- + */ +struct usbctlx_rmem_completor { + struct usbctlx_completor head; + + const struct hfa384x_usb_rmemresp *rmemresp; + void *data; + unsigned int len; +}; + +static int usbctlx_rmem_completor_fn(struct usbctlx_completor *head) +{ + struct usbctlx_rmem_completor *complete = + (struct usbctlx_rmem_completor *)head; + + pr_debug("rmemresp:len=%d\n", complete->rmemresp->frmlen); + memcpy(complete->data, complete->rmemresp->data, complete->len); + return 0; +} + +static inline struct usbctlx_completor * +init_rmem_completor(struct usbctlx_rmem_completor *completor, + struct hfa384x_usb_rmemresp *rmemresp, + void *data, + unsigned int len) +{ + completor->head.complete = usbctlx_rmem_completor_fn; + completor->rmemresp = rmemresp; + completor->data = data; + completor->len = len; + return &completor->head; +} + +/*---------------------------------------------------------------- + * hfa384x_cb_status + * + * Ctlx_complete handler for async CMD type control exchanges. + * mark the hw struct as such. + * + * Note: If the handling is changed here, it should probably be + * changed in docmd as well. + * + * Arguments: + * hw hw struct + * ctlx completed CTLX + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_cb_status(struct hfa384x *hw, + const struct hfa384x_usbctlx *ctlx) +{ + if (ctlx->usercb) { + struct hfa384x_cmdresult cmdresult; + + if (ctlx->state != CTLX_COMPLETE) { + memset(&cmdresult, 0, sizeof(cmdresult)); + cmdresult.status = + HFA384x_STATUS_RESULT_SET(HFA384x_CMD_ERR); + } else { + usbctlx_get_status(&ctlx->inbuf.cmdresp, &cmdresult); + } + + ctlx->usercb(hw, &cmdresult, ctlx->usercb_data); + } +} + +/*---------------------------------------------------------------- + * hfa384x_cmd_initialize + * + * Issues the initialize command and sets the hw->state based + * on the result. + * + * Arguments: + * hw device structure + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_cmd_initialize(struct hfa384x *hw) +{ + int result = 0; + int i; + struct hfa384x_metacmd cmd; + + cmd.cmd = HFA384x_CMDCODE_INIT; + cmd.parm0 = 0; + cmd.parm1 = 0; + cmd.parm2 = 0; + + result = hfa384x_docmd(hw, &cmd); + + pr_debug("cmdresp.init: status=0x%04x, resp0=0x%04x, resp1=0x%04x, resp2=0x%04x\n", + cmd.result.status, + cmd.result.resp0, cmd.result.resp1, cmd.result.resp2); + if (result == 0) { + for (i = 0; i < HFA384x_NUMPORTS_MAX; i++) + hw->port_enabled[i] = 0; + } + + hw->link_status = HFA384x_LINK_NOTCONNECTED; + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_cmd_disable + * + * Issues the disable command to stop communications on one of + * the MACs 'ports'. + * + * Arguments: + * hw device structure + * macport MAC port number (host order) + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_cmd_disable(struct hfa384x *hw, u16 macport) +{ + struct hfa384x_metacmd cmd; + + cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DISABLE) | + HFA384x_CMD_MACPORT_SET(macport); + cmd.parm0 = 0; + cmd.parm1 = 0; + cmd.parm2 = 0; + + return hfa384x_docmd(hw, &cmd); +} + +/*---------------------------------------------------------------- + * hfa384x_cmd_enable + * + * Issues the enable command to enable communications on one of + * the MACs 'ports'. + * + * Arguments: + * hw device structure + * macport MAC port number + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_cmd_enable(struct hfa384x *hw, u16 macport) +{ + struct hfa384x_metacmd cmd; + + cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | + HFA384x_CMD_MACPORT_SET(macport); + cmd.parm0 = 0; + cmd.parm1 = 0; + cmd.parm2 = 0; + + return hfa384x_docmd(hw, &cmd); +} + +/*---------------------------------------------------------------- + * hfa384x_cmd_monitor + * + * Enables the 'monitor mode' of the MAC. Here's the description of + * monitor mode that I've received thus far: + * + * "The "monitor mode" of operation is that the MAC passes all + * frames for which the PLCP checks are correct. All received + * MPDUs are passed to the host with MAC Port = 7, with a + * receive status of good, FCS error, or undecryptable. Passing + * certain MPDUs is a violation of the 802.11 standard, but useful + * for a debugging tool." Normal communication is not possible + * while monitor mode is enabled. + * + * Arguments: + * hw device structure + * enable a code (0x0b|0x0f) that enables/disables + * monitor mode. (host order) + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_cmd_monitor(struct hfa384x *hw, u16 enable) +{ + struct hfa384x_metacmd cmd; + + cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_MONITOR) | + HFA384x_CMD_AINFO_SET(enable); + cmd.parm0 = 0; + cmd.parm1 = 0; + cmd.parm2 = 0; + + return hfa384x_docmd(hw, &cmd); +} + +/*---------------------------------------------------------------- + * hfa384x_cmd_download + * + * Sets the controls for the MAC controller code/data download + * process. The arguments set the mode and address associated + * with a download. Note that the aux registers should be enabled + * prior to setting one of the download enable modes. + * + * Arguments: + * hw device structure + * mode 0 - Disable programming and begin code exec + * 1 - Enable volatile mem programming + * 2 - Enable non-volatile mem programming + * 3 - Program non-volatile section from NV download + * buffer. + * (host order) + * lowaddr + * highaddr For mode 1, sets the high & low order bits of + * the "destination address". This address will be + * the execution start address when download is + * subsequently disabled. + * For mode 2, sets the high & low order bits of + * the destination in NV ram. + * For modes 0 & 3, should be zero. (host order) + * NOTE: these are CMD format. + * codelen Length of the data to write in mode 2, + * zero otherwise. (host order) + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_cmd_download(struct hfa384x *hw, u16 mode, u16 lowaddr, + u16 highaddr, u16 codelen) +{ + struct hfa384x_metacmd cmd; + + pr_debug("mode=%d, lowaddr=0x%04x, highaddr=0x%04x, codelen=%d\n", + mode, lowaddr, highaddr, codelen); + + cmd.cmd = (HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DOWNLD) | + HFA384x_CMD_PROGMODE_SET(mode)); + + cmd.parm0 = lowaddr; + cmd.parm1 = highaddr; + cmd.parm2 = codelen; + + return hfa384x_docmd(hw, &cmd); +} + +/*---------------------------------------------------------------- + * hfa384x_corereset + * + * Perform a reset of the hfa38xx MAC core. We assume that the hw + * structure is in its "created" state. That is, it is initialized + * with proper values. Note that if a reset is done after the + * device has been active for awhile, the caller might have to clean + * up some leftover cruft in the hw structure. + * + * Arguments: + * hw device structure + * holdtime how long (in ms) to hold the reset + * settletime how long (in ms) to wait after releasing + * the reset + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_corereset(struct hfa384x *hw, int holdtime, + int settletime, int genesis) +{ + int result; + + result = usb_reset_device(hw->usb); + if (result < 0) { + netdev_err(hw->wlandev->netdev, "usb_reset_device() failed, result=%d.\n", + result); + } + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_complete_sync + * + * Waits for a synchronous CTLX object to complete, + * and then handles the response. + * + * Arguments: + * hw device structure + * ctlx CTLX ptr + * completor functor object to decide what to + * do with the CTLX's result. + * + * Returns: + * 0 Success + * -ERESTARTSYS Interrupted by a signal + * -EIO CTLX failed + * -ENODEV Adapter was unplugged + * ??? Result from completor + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +static int hfa384x_usbctlx_complete_sync(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx, + struct usbctlx_completor *completor) +{ + unsigned long flags; + int result; + + result = wait_for_completion_interruptible(&ctlx->done); + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* + * We can only handle the CTLX if the USB disconnect + * function has not run yet ... + */ +cleanup: + if (hw->wlandev->hwremoved) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + result = -ENODEV; + } else if (result != 0) { + int runqueue = 0; + + /* + * We were probably interrupted, so delete + * this CTLX asynchronously, kill the timers + * and the URB, and then start the next + * pending CTLX. + * + * NOTE: We can only delete the timers and + * the URB if this CTLX is active. + */ + if (ctlx == get_active_ctlx(hw)) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + del_timer_sync(&hw->reqtimer); + del_timer_sync(&hw->resptimer); + hw->req_timer_done = 1; + hw->resp_timer_done = 1; + usb_kill_urb(&hw->ctlx_urb); + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + runqueue = 1; + + /* + * This scenario is so unlikely that I'm + * happy with a grubby "goto" solution ... + */ + if (hw->wlandev->hwremoved) + goto cleanup; + } + + /* + * The completion task will send this CTLX + * to the reaper the next time it runs. We + * are no longer in a hurry. + */ + ctlx->reapable = 1; + ctlx->state = CTLX_REQ_FAILED; + list_move_tail(&ctlx->list, &hw->ctlxq.completing); + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + if (runqueue) + hfa384x_usbctlxq_run(hw); + } else { + if (ctlx->state == CTLX_COMPLETE) { + result = completor->complete(completor); + } else { + netdev_warn(hw->wlandev->netdev, "CTLX[%d] error: state(%s)\n", + le16_to_cpu(ctlx->outbuf.type), + ctlxstr(ctlx->state)); + result = -EIO; + } + + list_del(&ctlx->list); + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + kfree(ctlx); + } + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_docmd + * + * Constructs a command CTLX and submits it. + * + * NOTE: Any changes to the 'post-submit' code in this function + * need to be carried over to hfa384x_cbcmd() since the handling + * is virtually identical. + * + * Arguments: + * hw device structure + * cmd cmd structure. Includes all arguments and result + * data points. All in host order. in host order + * + * Returns: + * 0 success + * -EIO CTLX failure + * -ERESTARTSYS Awakened on signal + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + * + * Side effects: + * + * + * Call context: + * process + *---------------------------------------------------------------- + */ +static inline int +hfa384x_docmd(struct hfa384x *hw, + struct hfa384x_metacmd *cmd) +{ + int result; + struct hfa384x_usbctlx *ctlx; + + ctlx = usbctlx_alloc(); + if (!ctlx) { + result = -ENOMEM; + goto done; + } + + /* Initialize the command */ + ctlx->outbuf.cmdreq.type = cpu_to_le16(HFA384x_USB_CMDREQ); + ctlx->outbuf.cmdreq.cmd = cpu_to_le16(cmd->cmd); + ctlx->outbuf.cmdreq.parm0 = cpu_to_le16(cmd->parm0); + ctlx->outbuf.cmdreq.parm1 = cpu_to_le16(cmd->parm1); + ctlx->outbuf.cmdreq.parm2 = cpu_to_le16(cmd->parm2); + + ctlx->outbufsize = sizeof(ctlx->outbuf.cmdreq); + + pr_debug("cmdreq: cmd=0x%04x parm0=0x%04x parm1=0x%04x parm2=0x%04x\n", + cmd->cmd, cmd->parm0, cmd->parm1, cmd->parm2); + + ctlx->reapable = DOWAIT; + ctlx->cmdcb = NULL; + ctlx->usercb = NULL; + ctlx->usercb_data = NULL; + + result = hfa384x_usbctlx_submit(hw, ctlx); + if (result != 0) { + kfree(ctlx); + } else { + struct usbctlx_cmd_completor cmd_completor; + struct usbctlx_completor *completor; + + completor = init_cmd_completor(&cmd_completor, + &ctlx->inbuf.cmdresp, + &cmd->result); + + result = hfa384x_usbctlx_complete_sync(hw, ctlx, completor); + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_dorrid + * + * Constructs a read rid CTLX and issues it. + * + * NOTE: Any changes to the 'post-submit' code in this function + * need to be carried over to hfa384x_cbrrid() since the handling + * is virtually identical. + * + * Arguments: + * hw device structure + * mode DOWAIT or DOASYNC + * rid Read RID number (host order) + * riddata Caller supplied buffer that MAC formatted RID.data + * record will be written to for DOWAIT calls. Should + * be NULL for DOASYNC calls. + * riddatalen Buffer length for DOWAIT calls. Zero for DOASYNC calls. + * cmdcb command callback for async calls, NULL for DOWAIT calls + * usercb user callback for async calls, NULL for DOWAIT calls + * usercb_data user supplied data pointer for async calls, NULL + * for DOWAIT calls + * + * Returns: + * 0 success + * -EIO CTLX failure + * -ERESTARTSYS Awakened on signal + * -ENODATA riddatalen != macdatalen + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + * + * Side effects: + * + * Call context: + * interrupt (DOASYNC) + * process (DOWAIT or DOASYNC) + *---------------------------------------------------------------- + */ +static int +hfa384x_dorrid(struct hfa384x *hw, + enum cmd_mode mode, + u16 rid, + void *riddata, + unsigned int riddatalen, + ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data) +{ + int result; + struct hfa384x_usbctlx *ctlx; + + ctlx = usbctlx_alloc(); + if (!ctlx) { + result = -ENOMEM; + goto done; + } + + /* Initialize the command */ + ctlx->outbuf.rridreq.type = cpu_to_le16(HFA384x_USB_RRIDREQ); + ctlx->outbuf.rridreq.frmlen = + cpu_to_le16(sizeof(ctlx->outbuf.rridreq.rid)); + ctlx->outbuf.rridreq.rid = cpu_to_le16(rid); + + ctlx->outbufsize = sizeof(ctlx->outbuf.rridreq); + + ctlx->reapable = mode; + ctlx->cmdcb = cmdcb; + ctlx->usercb = usercb; + ctlx->usercb_data = usercb_data; + + /* Submit the CTLX */ + result = hfa384x_usbctlx_submit(hw, ctlx); + if (result != 0) { + kfree(ctlx); + } else if (mode == DOWAIT) { + struct usbctlx_rrid_completor completor; + + result = + hfa384x_usbctlx_complete_sync(hw, ctlx, + init_rrid_completor + (&completor, + &ctlx->inbuf.rridresp, + riddata, riddatalen)); + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_dowrid + * + * Constructs a write rid CTLX and issues it. + * + * NOTE: Any changes to the 'post-submit' code in this function + * need to be carried over to hfa384x_cbwrid() since the handling + * is virtually identical. + * + * Arguments: + * hw device structure + * enum cmd_mode DOWAIT or DOASYNC + * rid RID code + * riddata Data portion of RID formatted for MAC + * riddatalen Length of the data portion in bytes + * cmdcb command callback for async calls, NULL for DOWAIT calls + * usercb user callback for async calls, NULL for DOWAIT calls + * usercb_data user supplied data pointer for async calls + * + * Returns: + * 0 success + * -ETIMEDOUT timed out waiting for register ready or + * command completion + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + * + * Side effects: + * + * Call context: + * interrupt (DOASYNC) + * process (DOWAIT or DOASYNC) + *---------------------------------------------------------------- + */ +static int +hfa384x_dowrid(struct hfa384x *hw, + enum cmd_mode mode, + u16 rid, + void *riddata, + unsigned int riddatalen, + ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data) +{ + int result; + struct hfa384x_usbctlx *ctlx; + + ctlx = usbctlx_alloc(); + if (!ctlx) { + result = -ENOMEM; + goto done; + } + + /* Initialize the command */ + ctlx->outbuf.wridreq.type = cpu_to_le16(HFA384x_USB_WRIDREQ); + ctlx->outbuf.wridreq.frmlen = cpu_to_le16((sizeof + (ctlx->outbuf.wridreq.rid) + + riddatalen + 1) / 2); + ctlx->outbuf.wridreq.rid = cpu_to_le16(rid); + memcpy(ctlx->outbuf.wridreq.data, riddata, riddatalen); + + ctlx->outbufsize = sizeof(ctlx->outbuf.wridreq.type) + + sizeof(ctlx->outbuf.wridreq.frmlen) + + sizeof(ctlx->outbuf.wridreq.rid) + riddatalen; + + ctlx->reapable = mode; + ctlx->cmdcb = cmdcb; + ctlx->usercb = usercb; + ctlx->usercb_data = usercb_data; + + /* Submit the CTLX */ + result = hfa384x_usbctlx_submit(hw, ctlx); + if (result != 0) { + kfree(ctlx); + } else if (mode == DOWAIT) { + struct usbctlx_cmd_completor completor; + struct hfa384x_cmdresult wridresult; + + result = hfa384x_usbctlx_complete_sync(hw, + ctlx, + init_wrid_completor + (&completor, + &ctlx->inbuf.wridresp, + &wridresult)); + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_dormem + * + * Constructs a readmem CTLX and issues it. + * + * NOTE: Any changes to the 'post-submit' code in this function + * need to be carried over to hfa384x_cbrmem() since the handling + * is virtually identical. + * + * Arguments: + * hw device structure + * page MAC address space page (CMD format) + * offset MAC address space offset + * data Ptr to data buffer to receive read + * len Length of the data to read (max == 2048) + * + * Returns: + * 0 success + * -ETIMEDOUT timed out waiting for register ready or + * command completion + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + * + * Side effects: + * + * Call context: + * process (DOWAIT) + *---------------------------------------------------------------- + */ +static int +hfa384x_dormem(struct hfa384x *hw, + u16 page, + u16 offset, + void *data, + unsigned int len) +{ + int result; + struct hfa384x_usbctlx *ctlx; + + ctlx = usbctlx_alloc(); + if (!ctlx) { + result = -ENOMEM; + goto done; + } + + /* Initialize the command */ + ctlx->outbuf.rmemreq.type = cpu_to_le16(HFA384x_USB_RMEMREQ); + ctlx->outbuf.rmemreq.frmlen = + cpu_to_le16(sizeof(ctlx->outbuf.rmemreq.offset) + + sizeof(ctlx->outbuf.rmemreq.page) + len); + ctlx->outbuf.rmemreq.offset = cpu_to_le16(offset); + ctlx->outbuf.rmemreq.page = cpu_to_le16(page); + + ctlx->outbufsize = sizeof(ctlx->outbuf.rmemreq); + + pr_debug("type=0x%04x frmlen=%d offset=0x%04x page=0x%04x\n", + ctlx->outbuf.rmemreq.type, + ctlx->outbuf.rmemreq.frmlen, + ctlx->outbuf.rmemreq.offset, ctlx->outbuf.rmemreq.page); + + pr_debug("pktsize=%zd\n", ROUNDUP64(sizeof(ctlx->outbuf.rmemreq))); + + ctlx->reapable = DOWAIT; + ctlx->cmdcb = NULL; + ctlx->usercb = NULL; + ctlx->usercb_data = NULL; + + result = hfa384x_usbctlx_submit(hw, ctlx); + if (result != 0) { + kfree(ctlx); + } else { + struct usbctlx_rmem_completor completor; + + result = + hfa384x_usbctlx_complete_sync(hw, ctlx, + init_rmem_completor + (&completor, + &ctlx->inbuf.rmemresp, data, + len)); + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_dowmem + * + * Constructs a writemem CTLX and issues it. + * + * NOTE: Any changes to the 'post-submit' code in this function + * need to be carried over to hfa384x_cbwmem() since the handling + * is virtually identical. + * + * Arguments: + * hw device structure + * page MAC address space page (CMD format) + * offset MAC address space offset + * data Ptr to data buffer containing write data + * len Length of the data to read (max == 2048) + * + * Returns: + * 0 success + * -ETIMEDOUT timed out waiting for register ready or + * command completion + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + * + * Side effects: + * + * Call context: + * interrupt (DOWAIT) + * process (DOWAIT) + *---------------------------------------------------------------- + */ +static int +hfa384x_dowmem(struct hfa384x *hw, + u16 page, + u16 offset, + void *data, + unsigned int len) +{ + int result; + struct hfa384x_usbctlx *ctlx; + + pr_debug("page=0x%04x offset=0x%04x len=%d\n", page, offset, len); + + ctlx = usbctlx_alloc(); + if (!ctlx) { + result = -ENOMEM; + goto done; + } + + /* Initialize the command */ + ctlx->outbuf.wmemreq.type = cpu_to_le16(HFA384x_USB_WMEMREQ); + ctlx->outbuf.wmemreq.frmlen = + cpu_to_le16(sizeof(ctlx->outbuf.wmemreq.offset) + + sizeof(ctlx->outbuf.wmemreq.page) + len); + ctlx->outbuf.wmemreq.offset = cpu_to_le16(offset); + ctlx->outbuf.wmemreq.page = cpu_to_le16(page); + memcpy(ctlx->outbuf.wmemreq.data, data, len); + + ctlx->outbufsize = sizeof(ctlx->outbuf.wmemreq.type) + + sizeof(ctlx->outbuf.wmemreq.frmlen) + + sizeof(ctlx->outbuf.wmemreq.offset) + + sizeof(ctlx->outbuf.wmemreq.page) + len; + + ctlx->reapable = DOWAIT; + ctlx->cmdcb = NULL; + ctlx->usercb = NULL; + ctlx->usercb_data = NULL; + + result = hfa384x_usbctlx_submit(hw, ctlx); + if (result != 0) { + kfree(ctlx); + } else { + struct usbctlx_cmd_completor completor; + struct hfa384x_cmdresult wmemresult; + + result = hfa384x_usbctlx_complete_sync(hw, + ctlx, + init_wmem_completor + (&completor, + &ctlx->inbuf.wmemresp, + &wmemresult)); + } + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_disable + * + * Issues the disable command to stop communications on one of + * the MACs 'ports'. Only macport 0 is valid for stations. + * APs may also disable macports 1-6. Only ports that have been + * previously enabled may be disabled. + * + * Arguments: + * hw device structure + * macport MAC port number (host order) + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_disable(struct hfa384x *hw, u16 macport) +{ + int result = 0; + + if ((!hw->isap && macport != 0) || + (hw->isap && !(macport <= HFA384x_PORTID_MAX)) || + !(hw->port_enabled[macport])) { + result = -EINVAL; + } else { + result = hfa384x_cmd_disable(hw, macport); + if (result == 0) + hw->port_enabled[macport] = 0; + } + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_enable + * + * Issues the enable command to enable communications on one of + * the MACs 'ports'. Only macport 0 is valid for stations. + * APs may also enable macports 1-6. Only ports that are currently + * disabled may be enabled. + * + * Arguments: + * hw device structure + * macport MAC port number + * + * Returns: + * 0 success + * >0 f/w reported failure - f/w status code + * <0 driver reported error (timeout|bad arg) + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_enable(struct hfa384x *hw, u16 macport) +{ + int result = 0; + + if ((!hw->isap && macport != 0) || + (hw->isap && !(macport <= HFA384x_PORTID_MAX)) || + (hw->port_enabled[macport])) { + result = -EINVAL; + } else { + result = hfa384x_cmd_enable(hw, macport); + if (result == 0) + hw->port_enabled[macport] = 1; + } + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_flashdl_enable + * + * Begins the flash download state. Checks to see that we're not + * already in a download state and that a port isn't enabled. + * Sets the download state and retrieves the flash download + * buffer location, buffer size, and timeout length. + * + * Arguments: + * hw device structure + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_flashdl_enable(struct hfa384x *hw) +{ + int result = 0; + int i; + + /* Check that a port isn't active */ + for (i = 0; i < HFA384x_PORTID_MAX; i++) { + if (hw->port_enabled[i]) { + pr_debug("called when port enabled.\n"); + return -EINVAL; + } + } + + /* Check that we're not already in a download state */ + if (hw->dlstate != HFA384x_DLSTATE_DISABLED) + return -EINVAL; + + /* Retrieve the buffer loc&size and timeout */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_DOWNLOADBUFFER, + &hw->bufinfo, sizeof(hw->bufinfo)); + if (result) + return result; + + le16_to_cpus(&hw->bufinfo.page); + le16_to_cpus(&hw->bufinfo.offset); + le16_to_cpus(&hw->bufinfo.len); + result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_MAXLOADTIME, + &hw->dltimeout); + if (result) + return result; + + le16_to_cpus(&hw->dltimeout); + + pr_debug("flashdl_enable\n"); + + hw->dlstate = HFA384x_DLSTATE_FLASHENABLED; + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_flashdl_disable + * + * Ends the flash download state. Note that this will cause the MAC + * firmware to restart. + * + * Arguments: + * hw device structure + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_flashdl_disable(struct hfa384x *hw) +{ + /* Check that we're already in the download state */ + if (hw->dlstate != HFA384x_DLSTATE_FLASHENABLED) + return -EINVAL; + + pr_debug("flashdl_enable\n"); + + /* There isn't much we can do at this point, so I don't */ + /* bother w/ the return value */ + hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0, 0); + hw->dlstate = HFA384x_DLSTATE_DISABLED; + + return 0; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_flashdl_write + * + * Performs a FLASH download of a chunk of data. First checks to see + * that we're in the FLASH download state, then sets the download + * mode, uses the aux functions to 1) copy the data to the flash + * buffer, 2) sets the download 'write flash' mode, 3) readback and + * compare. Lather rinse, repeat as many times an necessary to get + * all the given data into flash. + * When all data has been written using this function (possibly + * repeatedly), call drvr_flashdl_disable() to end the download state + * and restart the MAC. + * + * Arguments: + * hw device structure + * daddr Card address to write to. (host order) + * buf Ptr to data to write. + * len Length of data (host order). + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_flashdl_write(struct hfa384x *hw, u32 daddr, + void *buf, u32 len) +{ + int result = 0; + u32 dlbufaddr; + int nburns; + u32 burnlen; + u32 burndaddr; + u16 burnlo; + u16 burnhi; + int nwrites; + u8 *writebuf; + u16 writepage; + u16 writeoffset; + u32 writelen; + int i; + int j; + + pr_debug("daddr=0x%08x len=%d\n", daddr, len); + + /* Check that we're in the flash download state */ + if (hw->dlstate != HFA384x_DLSTATE_FLASHENABLED) + return -EINVAL; + + netdev_info(hw->wlandev->netdev, + "Download %d bytes to flash @0x%06x\n", len, daddr); + + /* Convert to flat address for arithmetic */ + /* NOTE: dlbuffer RID stores the address in AUX format */ + dlbufaddr = + HFA384x_ADDR_AUX_MKFLAT(hw->bufinfo.page, hw->bufinfo.offset); + pr_debug("dlbuf.page=0x%04x dlbuf.offset=0x%04x dlbufaddr=0x%08x\n", + hw->bufinfo.page, hw->bufinfo.offset, dlbufaddr); + /* Calculations to determine how many fills of the dlbuffer to do + * and how many USB wmemreq's to do for each fill. At this point + * in time, the dlbuffer size and the wmemreq size are the same. + * Therefore, nwrites should always be 1. The extra complexity + * here is a hedge against future changes. + */ + + /* Figure out how many times to do the flash programming */ + nburns = len / hw->bufinfo.len; + nburns += (len % hw->bufinfo.len) ? 1 : 0; + + /* For each flash program cycle, how many USB wmemreq's are needed? */ + nwrites = hw->bufinfo.len / HFA384x_USB_RWMEM_MAXLEN; + nwrites += (hw->bufinfo.len % HFA384x_USB_RWMEM_MAXLEN) ? 1 : 0; + + /* For each burn */ + for (i = 0; i < nburns; i++) { + /* Get the dest address and len */ + burnlen = (len - (hw->bufinfo.len * i)) > hw->bufinfo.len ? + hw->bufinfo.len : (len - (hw->bufinfo.len * i)); + burndaddr = daddr + (hw->bufinfo.len * i); + burnlo = HFA384x_ADDR_CMD_MKOFF(burndaddr); + burnhi = HFA384x_ADDR_CMD_MKPAGE(burndaddr); + + netdev_info(hw->wlandev->netdev, "Writing %d bytes to flash @0x%06x\n", + burnlen, burndaddr); + + /* Set the download mode */ + result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_NV, + burnlo, burnhi, burnlen); + if (result) { + netdev_err(hw->wlandev->netdev, + "download(NV,lo=%x,hi=%x,len=%x) cmd failed, result=%d. Aborting d/l\n", + burnlo, burnhi, burnlen, result); + goto exit_proc; + } + + /* copy the data to the flash download buffer */ + for (j = 0; j < nwrites; j++) { + writebuf = buf + + (i * hw->bufinfo.len) + + (j * HFA384x_USB_RWMEM_MAXLEN); + + writepage = HFA384x_ADDR_CMD_MKPAGE(dlbufaddr + + (j * HFA384x_USB_RWMEM_MAXLEN)); + writeoffset = HFA384x_ADDR_CMD_MKOFF(dlbufaddr + + (j * HFA384x_USB_RWMEM_MAXLEN)); + + writelen = burnlen - (j * HFA384x_USB_RWMEM_MAXLEN); + writelen = writelen > HFA384x_USB_RWMEM_MAXLEN ? + HFA384x_USB_RWMEM_MAXLEN : writelen; + + result = hfa384x_dowmem(hw, + writepage, + writeoffset, + writebuf, writelen); + } + + /* set the download 'write flash' mode */ + result = hfa384x_cmd_download(hw, + HFA384x_PROGMODE_NVWRITE, + 0, 0, 0); + if (result) { + netdev_err(hw->wlandev->netdev, + "download(NVWRITE,lo=%x,hi=%x,len=%x) cmd failed, result=%d. Aborting d/l\n", + burnlo, burnhi, burnlen, result); + goto exit_proc; + } + + /* TODO: We really should do a readback and compare. */ + } + +exit_proc: + + /* Leave the firmware in the 'post-prog' mode. flashdl_disable will */ + /* actually disable programming mode. Remember, that will cause the */ + /* the firmware to effectively reset itself. */ + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_getconfig + * + * Performs the sequence necessary to read a config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (host order) + * buf host side record buffer. Upon return it will + * contain the body portion of the record (minus the + * RID and len). + * len buffer length (in bytes, should match record length) + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * -ENODATA length mismatch between argument and retrieved + * record. + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_getconfig(struct hfa384x *hw, u16 rid, void *buf, u16 len) +{ + return hfa384x_dorrid(hw, DOWAIT, rid, buf, len, NULL, NULL, NULL); +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_setconfig_async + * + * Performs the sequence necessary to write a config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (in host order) + * buf host side record buffer + * len buffer length (in bytes) + * usercb completion callback + * usercb_data completion callback argument + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int +hfa384x_drvr_setconfig_async(struct hfa384x *hw, + u16 rid, + void *buf, + u16 len, ctlx_usercb_t usercb, void *usercb_data) +{ + return hfa384x_dowrid(hw, DOASYNC, rid, buf, len, hfa384x_cb_status, + usercb, usercb_data); +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_ramdl_disable + * + * Ends the ram download state. + * + * Arguments: + * hw device structure + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_ramdl_disable(struct hfa384x *hw) +{ + /* Check that we're already in the download state */ + if (hw->dlstate != HFA384x_DLSTATE_RAMENABLED) + return -EINVAL; + + pr_debug("ramdl_disable()\n"); + + /* There isn't much we can do at this point, so I don't */ + /* bother w/ the return value */ + hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0, 0); + hw->dlstate = HFA384x_DLSTATE_DISABLED; + + return 0; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_ramdl_enable + * + * Begins the ram download state. Checks to see that we're not + * already in a download state and that a port isn't enabled. + * Sets the download state and calls cmd_download with the + * ENABLE_VOLATILE subcommand and the exeaddr argument. + * + * Arguments: + * hw device structure + * exeaddr the card execution address that will be + * jumped to when ramdl_disable() is called + * (host order). + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_ramdl_enable(struct hfa384x *hw, u32 exeaddr) +{ + int result = 0; + u16 lowaddr; + u16 hiaddr; + int i; + + /* Check that a port isn't active */ + for (i = 0; i < HFA384x_PORTID_MAX; i++) { + if (hw->port_enabled[i]) { + netdev_err(hw->wlandev->netdev, + "Can't download with a macport enabled.\n"); + return -EINVAL; + } + } + + /* Check that we're not already in a download state */ + if (hw->dlstate != HFA384x_DLSTATE_DISABLED) { + netdev_err(hw->wlandev->netdev, + "Download state not disabled.\n"); + return -EINVAL; + } + + pr_debug("ramdl_enable, exeaddr=0x%08x\n", exeaddr); + + /* Call the download(1,addr) function */ + lowaddr = HFA384x_ADDR_CMD_MKOFF(exeaddr); + hiaddr = HFA384x_ADDR_CMD_MKPAGE(exeaddr); + + result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_RAM, + lowaddr, hiaddr, 0); + + if (result == 0) { + /* Set the download state */ + hw->dlstate = HFA384x_DLSTATE_RAMENABLED; + } else { + pr_debug("cmd_download(0x%04x, 0x%04x) failed, result=%d.\n", + lowaddr, hiaddr, result); + } + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_ramdl_write + * + * Performs a RAM download of a chunk of data. First checks to see + * that we're in the RAM download state, then uses the [read|write]mem USB + * commands to 1) copy the data, 2) readback and compare. The download + * state is unaffected. When all data has been written using + * this function, call drvr_ramdl_disable() to end the download state + * and restart the MAC. + * + * Arguments: + * hw device structure + * daddr Card address to write to. (host order) + * buf Ptr to data to write. + * len Length of data (host order). + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_ramdl_write(struct hfa384x *hw, u32 daddr, void *buf, u32 len) +{ + int result = 0; + int nwrites; + u8 *data = buf; + int i; + u32 curraddr; + u16 currpage; + u16 curroffset; + u16 currlen; + + /* Check that we're in the ram download state */ + if (hw->dlstate != HFA384x_DLSTATE_RAMENABLED) + return -EINVAL; + + netdev_info(hw->wlandev->netdev, "Writing %d bytes to ram @0x%06x\n", + len, daddr); + + /* How many dowmem calls? */ + nwrites = len / HFA384x_USB_RWMEM_MAXLEN; + nwrites += len % HFA384x_USB_RWMEM_MAXLEN ? 1 : 0; + + /* Do blocking wmem's */ + for (i = 0; i < nwrites; i++) { + /* make address args */ + curraddr = daddr + (i * HFA384x_USB_RWMEM_MAXLEN); + currpage = HFA384x_ADDR_CMD_MKPAGE(curraddr); + curroffset = HFA384x_ADDR_CMD_MKOFF(curraddr); + currlen = len - (i * HFA384x_USB_RWMEM_MAXLEN); + if (currlen > HFA384x_USB_RWMEM_MAXLEN) + currlen = HFA384x_USB_RWMEM_MAXLEN; + + /* Do blocking ctlx */ + result = hfa384x_dowmem(hw, + currpage, + curroffset, + data + (i * HFA384x_USB_RWMEM_MAXLEN), + currlen); + + if (result) + break; + + /* TODO: We really should have a readback. */ + } + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_readpda + * + * Performs the sequence to read the PDA space. Note there is no + * drvr_writepda() function. Writing a PDA is + * generally implemented by a calling component via calls to + * cmd_download and writing to the flash download buffer via the + * aux regs. + * + * Arguments: + * hw device structure + * buf buffer to store PDA in + * len buffer length + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * -ETIMEDOUT timeout waiting for the cmd regs to become + * available, or waiting for the control reg + * to indicate the Aux port is enabled. + * -ENODATA the buffer does NOT contain a valid PDA. + * Either the card PDA is bad, or the auxdata + * reads are giving us garbage. + * + * + * Side effects: + * + * Call context: + * process or non-card interrupt. + *---------------------------------------------------------------- + */ +int hfa384x_drvr_readpda(struct hfa384x *hw, void *buf, unsigned int len) +{ + int result = 0; + __le16 *pda = buf; + int pdaok = 0; + int morepdrs = 1; + int currpdr = 0; /* word offset of the current pdr */ + size_t i; + u16 pdrlen; /* pdr length in bytes, host order */ + u16 pdrcode; /* pdr code, host order */ + u16 currpage; + u16 curroffset; + struct pdaloc { + u32 cardaddr; + u16 auxctl; + } pdaloc[] = { + { + HFA3842_PDA_BASE, 0}, { + HFA3841_PDA_BASE, 0}, { + HFA3841_PDA_BOGUS_BASE, 0} + }; + + /* Read the pda from each known address. */ + for (i = 0; i < ARRAY_SIZE(pdaloc); i++) { + /* Make address */ + currpage = HFA384x_ADDR_CMD_MKPAGE(pdaloc[i].cardaddr); + curroffset = HFA384x_ADDR_CMD_MKOFF(pdaloc[i].cardaddr); + + /* units of bytes */ + result = hfa384x_dormem(hw, currpage, curroffset, buf, + len); + + if (result) { + netdev_warn(hw->wlandev->netdev, + "Read from index %zd failed, continuing\n", + i); + continue; + } + + /* Test for garbage */ + pdaok = 1; /* initially assume good */ + morepdrs = 1; + while (pdaok && morepdrs) { + pdrlen = le16_to_cpu(pda[currpdr]) * 2; + pdrcode = le16_to_cpu(pda[currpdr + 1]); + /* Test the record length */ + if (pdrlen > HFA384x_PDR_LEN_MAX || pdrlen == 0) { + netdev_err(hw->wlandev->netdev, + "pdrlen invalid=%d\n", pdrlen); + pdaok = 0; + break; + } + /* Test the code */ + if (!hfa384x_isgood_pdrcode(pdrcode)) { + netdev_err(hw->wlandev->netdev, "pdrcode invalid=%d\n", + pdrcode); + pdaok = 0; + break; + } + /* Test for completion */ + if (pdrcode == HFA384x_PDR_END_OF_PDA) + morepdrs = 0; + + /* Move to the next pdr (if necessary) */ + if (morepdrs) { + /* note the access to pda[], need words here */ + currpdr += le16_to_cpu(pda[currpdr]) + 1; + } + } + if (pdaok) { + netdev_info(hw->wlandev->netdev, + "PDA Read from 0x%08x in %s space.\n", + pdaloc[i].cardaddr, + pdaloc[i].auxctl == 0 ? "EXTDS" : + pdaloc[i].auxctl == 1 ? "NV" : + pdaloc[i].auxctl == 2 ? "PHY" : + pdaloc[i].auxctl == 3 ? "ICSRAM" : + ""); + break; + } + } + result = pdaok ? 0 : -ENODATA; + + if (result) + pr_debug("Failure: pda is not okay\n"); + + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_setconfig + * + * Performs the sequence necessary to write a config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (in host order) + * buf host side record buffer + * len buffer length (in bytes) + * + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_setconfig(struct hfa384x *hw, u16 rid, void *buf, u16 len) +{ + return hfa384x_dowrid(hw, DOWAIT, rid, buf, len, NULL, NULL, NULL); +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_start + * + * Issues the MAC initialize command, sets up some data structures, + * and enables the interrupts. After this function completes, the + * low-level stuff should be ready for any/all commands. + * + * Arguments: + * hw device structure + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_start(struct hfa384x *hw) +{ + int result, result1, result2; + u16 status; + + might_sleep(); + + /* Clear endpoint stalls - but only do this if the endpoint + * is showing a stall status. Some prism2 cards seem to behave + * badly if a clear_halt is called when the endpoint is already + * ok + */ + result = + usb_get_std_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_in, + &status); + if (result < 0) { + netdev_err(hw->wlandev->netdev, "Cannot get bulk in endpoint status.\n"); + goto done; + } + if ((status == 1) && usb_clear_halt(hw->usb, hw->endp_in)) + netdev_err(hw->wlandev->netdev, "Failed to reset bulk in endpoint.\n"); + + result = + usb_get_std_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_out, + &status); + if (result < 0) { + netdev_err(hw->wlandev->netdev, "Cannot get bulk out endpoint status.\n"); + goto done; + } + if ((status == 1) && usb_clear_halt(hw->usb, hw->endp_out)) + netdev_err(hw->wlandev->netdev, "Failed to reset bulk out endpoint.\n"); + + /* Synchronous unlink, in case we're trying to restart the driver */ + usb_kill_urb(&hw->rx_urb); + + /* Post the IN urb */ + result = submit_rx_urb(hw, GFP_KERNEL); + if (result != 0) { + netdev_err(hw->wlandev->netdev, + "Fatal, failed to submit RX URB, result=%d\n", + result); + goto done; + } + + /* Call initialize twice, with a 1 second sleep in between. + * This is a nasty work-around since many prism2 cards seem to + * need time to settle after an init from cold. The second + * call to initialize in theory is not necessary - but we call + * it anyway as a double insurance policy: + * 1) If the first init should fail, the second may well succeed + * and the card can still be used + * 2) It helps ensures all is well with the card after the first + * init and settle time. + */ + result1 = hfa384x_cmd_initialize(hw); + msleep(1000); + result = hfa384x_cmd_initialize(hw); + result2 = result; + if (result1 != 0) { + if (result2 != 0) { + netdev_err(hw->wlandev->netdev, + "cmd_initialize() failed on two attempts, results %d and %d\n", + result1, result2); + usb_kill_urb(&hw->rx_urb); + goto done; + } else { + pr_debug("First cmd_initialize() failed (result %d),\n", + result1); + pr_debug("but second attempt succeeded. All should be ok\n"); + } + } else if (result2 != 0) { + netdev_warn(hw->wlandev->netdev, "First cmd_initialize() succeeded, but second attempt failed (result=%d)\n", + result2); + netdev_warn(hw->wlandev->netdev, + "Most likely the card will be functional\n"); + goto done; + } + + hw->state = HFA384x_STATE_RUNNING; + +done: + return result; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_stop + * + * Shuts down the MAC to the point where it is safe to unload the + * driver. Any subsystem that may be holding a data or function + * ptr into the driver must be cleared/deinitialized. + * + * Arguments: + * hw device structure + * Returns: + * 0 success + * >0 f/w reported error - f/w status code + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process + *---------------------------------------------------------------- + */ +int hfa384x_drvr_stop(struct hfa384x *hw) +{ + int i; + + might_sleep(); + + /* There's no need for spinlocks here. The USB "disconnect" + * function sets this "removed" flag and then calls us. + */ + if (!hw->wlandev->hwremoved) { + /* Call initialize to leave the MAC in its 'reset' state */ + hfa384x_cmd_initialize(hw); + + /* Cancel the rxurb */ + usb_kill_urb(&hw->rx_urb); + } + + hw->link_status = HFA384x_LINK_NOTCONNECTED; + hw->state = HFA384x_STATE_INIT; + + del_timer_sync(&hw->commsqual_timer); + + /* Clear all the port status */ + for (i = 0; i < HFA384x_NUMPORTS_MAX; i++) + hw->port_enabled[i] = 0; + + return 0; +} + +/*---------------------------------------------------------------- + * hfa384x_drvr_txframe + * + * Takes a frame from prism2sta and queues it for transmission. + * + * Arguments: + * hw device structure + * skb packet buffer struct. Contains an 802.11 + * data frame. + * p80211_hdr points to the 802.11 header for the packet. + * Returns: + * 0 Success and more buffs available + * 1 Success but no more buffs + * 2 Allocation failure + * 4 Buffer full or queue busy + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +int hfa384x_drvr_txframe(struct hfa384x *hw, struct sk_buff *skb, + struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep) +{ + int usbpktlen = sizeof(struct hfa384x_tx_frame); + int result; + int ret; + char *ptr; + + if (hw->tx_urb.status == -EINPROGRESS) { + netdev_warn(hw->wlandev->netdev, "TX URB already in use\n"); + result = 3; + goto exit; + } + + /* Build Tx frame structure */ + /* Set up the control field */ + memset(&hw->txbuff.txfrm.desc, 0, sizeof(hw->txbuff.txfrm.desc)); + + /* Setup the usb type field */ + hw->txbuff.type = cpu_to_le16(HFA384x_USB_TXFRM); + + /* Set up the sw_support field to identify this frame */ + hw->txbuff.txfrm.desc.sw_support = 0x0123; + +/* Tx complete and Tx exception disable per dleach. Might be causing + * buf depletion + */ +/* #define DOEXC SLP -- doboth breaks horribly under load, doexc less so. */ +#if defined(DOBOTH) + hw->txbuff.txfrm.desc.tx_control = + HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | + HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1); +#elif defined(DOEXC) + hw->txbuff.txfrm.desc.tx_control = + HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | + HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(0); +#else + hw->txbuff.txfrm.desc.tx_control = + HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | + HFA384x_TX_TXEX_SET(0) | HFA384x_TX_TXOK_SET(0); +#endif + cpu_to_le16s(&hw->txbuff.txfrm.desc.tx_control); + + /* copy the header over to the txdesc */ + hw->txbuff.txfrm.desc.hdr = *p80211_hdr; + + /* if we're using host WEP, increase size by IV+ICV */ + if (p80211_wep->data) { + hw->txbuff.txfrm.desc.data_len = cpu_to_le16(skb->len + 8); + usbpktlen += 8; + } else { + hw->txbuff.txfrm.desc.data_len = cpu_to_le16(skb->len); + } + + usbpktlen += skb->len; + + /* copy over the WEP IV if we are using host WEP */ + ptr = hw->txbuff.txfrm.data; + if (p80211_wep->data) { + memcpy(ptr, p80211_wep->iv, sizeof(p80211_wep->iv)); + ptr += sizeof(p80211_wep->iv); + memcpy(ptr, p80211_wep->data, skb->len); + } else { + memcpy(ptr, skb->data, skb->len); + } + /* copy over the packet data */ + ptr += skb->len; + + /* copy over the WEP ICV if we are using host WEP */ + if (p80211_wep->data) + memcpy(ptr, p80211_wep->icv, sizeof(p80211_wep->icv)); + + /* Send the USB packet */ + usb_fill_bulk_urb(&hw->tx_urb, hw->usb, + hw->endp_out, + &hw->txbuff, ROUNDUP64(usbpktlen), + hfa384x_usbout_callback, hw->wlandev); + hw->tx_urb.transfer_flags |= USB_QUEUE_BULK; + + result = 1; + ret = submit_tx_urb(hw, &hw->tx_urb, GFP_ATOMIC); + if (ret != 0) { + netdev_err(hw->wlandev->netdev, + "submit_tx_urb() failed, error=%d\n", ret); + result = 3; + } + +exit: + return result; +} + +void hfa384x_tx_timeout(struct wlandevice *wlandev) +{ + struct hfa384x *hw = wlandev->priv; + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + if (!hw->wlandev->hwremoved) { + int sched; + + sched = !test_and_set_bit(WORK_TX_HALT, &hw->usb_flags); + sched |= !test_and_set_bit(WORK_RX_HALT, &hw->usb_flags); + if (sched) + schedule_work(&hw->usb_work); + } + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_reaper_task + * + * Deferred work callback to delete dead CTLX objects + * + * Arguments: + * work contains ptr to a struct hfa384x + * + * Returns: + * + * Call context: + * Task + *---------------------------------------------------------------- + */ +static void hfa384x_usbctlx_reaper_task(struct work_struct *work) +{ + struct hfa384x *hw = container_of(work, struct hfa384x, reaper_bh); + struct hfa384x_usbctlx *ctlx, *temp; + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* This list is guaranteed to be empty if someone + * has unplugged the adapter. + */ + list_for_each_entry_safe(ctlx, temp, &hw->ctlxq.reapable, list) { + list_del(&ctlx->list); + kfree(ctlx); + } + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_completion_task + * + * Deferred work callback to call completion handlers for returned CTLXs + * + * Arguments: + * work contains ptr to a struct hfa384x + * + * Returns: + * Nothing + * + * Call context: + * Task + *---------------------------------------------------------------- + */ +static void hfa384x_usbctlx_completion_task(struct work_struct *work) +{ + struct hfa384x *hw = container_of(work, struct hfa384x, completion_bh); + struct hfa384x_usbctlx *ctlx, *temp; + unsigned long flags; + + int reap = 0; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* This list is guaranteed to be empty if someone + * has unplugged the adapter ... + */ + list_for_each_entry_safe(ctlx, temp, &hw->ctlxq.completing, list) { + /* Call the completion function that this + * command was assigned, assuming it has one. + */ + if (ctlx->cmdcb) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + ctlx->cmdcb(hw, ctlx); + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* Make sure we don't try and complete + * this CTLX more than once! + */ + ctlx->cmdcb = NULL; + + /* Did someone yank the adapter out + * while our list was (briefly) unlocked? + */ + if (hw->wlandev->hwremoved) { + reap = 0; + break; + } + } + + /* + * "Reapable" CTLXs are ones which don't have any + * threads waiting for them to die. Hence they must + * be delivered to The Reaper! + */ + if (ctlx->reapable) { + /* Move the CTLX off the "completing" list (hopefully) + * on to the "reapable" list where the reaper task + * can find it. And "reapable" means that this CTLX + * isn't sitting on a wait-queue somewhere. + */ + list_move_tail(&ctlx->list, &hw->ctlxq.reapable); + reap = 1; + } + + complete(&ctlx->done); + } + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + if (reap) + schedule_work(&hw->reaper_bh); +} + +/*---------------------------------------------------------------- + * unlocked_usbctlx_cancel_async + * + * Mark the CTLX dead asynchronously, and ensure that the + * next command on the queue is run afterwards. + * + * Arguments: + * hw ptr to the struct hfa384x structure + * ctlx ptr to a CTLX structure + * + * Returns: + * 0 the CTLX's URB is inactive + * -EINPROGRESS the URB is currently being unlinked + * + * Call context: + * Either process or interrupt, but presumably interrupt + *---------------------------------------------------------------- + */ +static int unlocked_usbctlx_cancel_async(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx) +{ + int ret; + + /* + * Try to delete the URB containing our request packet. + * If we succeed, then its completion handler will be + * called with a status of -ECONNRESET. + */ + hw->ctlx_urb.transfer_flags |= URB_ASYNC_UNLINK; + ret = usb_unlink_urb(&hw->ctlx_urb); + + if (ret != -EINPROGRESS) { + /* + * The OUT URB had either already completed + * or was still in the pending queue, so the + * URB's completion function will not be called. + * We will have to complete the CTLX ourselves. + */ + ctlx->state = CTLX_REQ_FAILED; + unlocked_usbctlx_complete(hw, ctlx); + ret = 0; + } + + return ret; +} + +/*---------------------------------------------------------------- + * unlocked_usbctlx_complete + * + * A CTLX has completed. It may have been successful, it may not + * have been. At this point, the CTLX should be quiescent. The URBs + * aren't active and the timers should have been stopped. + * + * The CTLX is migrated to the "completing" queue, and the completing + * work is scheduled. + * + * Arguments: + * hw ptr to a struct hfa384x structure + * ctlx ptr to a ctlx structure + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * Either, assume interrupt + *---------------------------------------------------------------- + */ +static void unlocked_usbctlx_complete(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx) +{ + /* Timers have been stopped, and ctlx should be in + * a terminal state. Retire it from the "active" + * queue. + */ + list_move_tail(&ctlx->list, &hw->ctlxq.completing); + schedule_work(&hw->completion_bh); + + switch (ctlx->state) { + case CTLX_COMPLETE: + case CTLX_REQ_FAILED: + /* This are the correct terminating states. */ + break; + + default: + netdev_err(hw->wlandev->netdev, "CTLX[%d] not in a terminating state(%s)\n", + le16_to_cpu(ctlx->outbuf.type), + ctlxstr(ctlx->state)); + break; + } /* switch */ +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlxq_run + * + * Checks to see if the head item is running. If not, starts it. + * + * Arguments: + * hw ptr to struct hfa384x + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * any + *---------------------------------------------------------------- + */ +static void hfa384x_usbctlxq_run(struct hfa384x *hw) +{ + unsigned long flags; + + /* acquire lock */ + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* Only one active CTLX at any one time, because there's no + * other (reliable) way to match the response URB to the + * correct CTLX. + * + * Don't touch any of these CTLXs if the hardware + * has been removed or the USB subsystem is stalled. + */ + if (!list_empty(&hw->ctlxq.active) || + test_bit(WORK_TX_HALT, &hw->usb_flags) || hw->wlandev->hwremoved) + goto unlock; + + while (!list_empty(&hw->ctlxq.pending)) { + struct hfa384x_usbctlx *head; + int result; + + /* This is the first pending command */ + head = list_entry(hw->ctlxq.pending.next, + struct hfa384x_usbctlx, list); + + /* We need to split this off to avoid a race condition */ + list_move_tail(&head->list, &hw->ctlxq.active); + + /* Fill the out packet */ + usb_fill_bulk_urb(&hw->ctlx_urb, hw->usb, + hw->endp_out, + &head->outbuf, ROUNDUP64(head->outbufsize), + hfa384x_ctlxout_callback, hw); + hw->ctlx_urb.transfer_flags |= USB_QUEUE_BULK; + + /* Now submit the URB and update the CTLX's state */ + result = usb_submit_urb(&hw->ctlx_urb, GFP_ATOMIC); + if (result == 0) { + /* This CTLX is now running on the active queue */ + head->state = CTLX_REQ_SUBMITTED; + + /* Start the OUT wait timer */ + hw->req_timer_done = 0; + hw->reqtimer.expires = jiffies + HZ; + add_timer(&hw->reqtimer); + + /* Start the IN wait timer */ + hw->resp_timer_done = 0; + hw->resptimer.expires = jiffies + 2 * HZ; + add_timer(&hw->resptimer); + + break; + } + + if (result == -EPIPE) { + /* The OUT pipe needs resetting, so put + * this CTLX back in the "pending" queue + * and schedule a reset ... + */ + netdev_warn(hw->wlandev->netdev, + "%s tx pipe stalled: requesting reset\n", + hw->wlandev->netdev->name); + list_move(&head->list, &hw->ctlxq.pending); + set_bit(WORK_TX_HALT, &hw->usb_flags); + schedule_work(&hw->usb_work); + break; + } + + if (result == -ESHUTDOWN) { + netdev_warn(hw->wlandev->netdev, "%s urb shutdown!\n", + hw->wlandev->netdev->name); + break; + } + + netdev_err(hw->wlandev->netdev, "Failed to submit CTLX[%d]: error=%d\n", + le16_to_cpu(head->outbuf.type), result); + unlocked_usbctlx_complete(hw, head); + } /* while */ + +unlock: + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usbin_callback + * + * Callback for URBs on the BULKIN endpoint. + * + * Arguments: + * urb ptr to the completed urb + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbin_callback(struct urb *urb) +{ + struct wlandevice *wlandev = urb->context; + struct hfa384x *hw; + union hfa384x_usbin *usbin; + struct sk_buff *skb = NULL; + int result; + int urb_status; + u16 type; + + enum USBIN_ACTION { + HANDLE, + RESUBMIT, + ABORT + } action; + + if (!wlandev || !wlandev->netdev || wlandev->hwremoved) + goto exit; + + hw = wlandev->priv; + if (!hw) + goto exit; + + skb = hw->rx_urb_skb; + if (!skb || (skb->data != urb->transfer_buffer)) { + WARN_ON(1); + return; + } + + hw->rx_urb_skb = NULL; + + /* Check for error conditions within the URB */ + switch (urb->status) { + case 0: + action = HANDLE; + + /* Check for short packet */ + if (urb->actual_length == 0) { + wlandev->netdev->stats.rx_errors++; + wlandev->netdev->stats.rx_length_errors++; + action = RESUBMIT; + } + break; + + case -EPIPE: + netdev_warn(hw->wlandev->netdev, "%s rx pipe stalled: requesting reset\n", + wlandev->netdev->name); + if (!test_and_set_bit(WORK_RX_HALT, &hw->usb_flags)) + schedule_work(&hw->usb_work); + wlandev->netdev->stats.rx_errors++; + action = ABORT; + break; + + case -EILSEQ: + case -ETIMEDOUT: + case -EPROTO: + if (!test_and_set_bit(THROTTLE_RX, &hw->usb_flags) && + !timer_pending(&hw->throttle)) { + mod_timer(&hw->throttle, jiffies + THROTTLE_JIFFIES); + } + wlandev->netdev->stats.rx_errors++; + action = ABORT; + break; + + case -EOVERFLOW: + wlandev->netdev->stats.rx_over_errors++; + action = RESUBMIT; + break; + + case -ENODEV: + case -ESHUTDOWN: + pr_debug("status=%d, device removed.\n", urb->status); + action = ABORT; + break; + + case -ENOENT: + case -ECONNRESET: + pr_debug("status=%d, urb explicitly unlinked.\n", urb->status); + action = ABORT; + break; + + default: + pr_debug("urb status=%d, transfer flags=0x%x\n", + urb->status, urb->transfer_flags); + wlandev->netdev->stats.rx_errors++; + action = RESUBMIT; + break; + } + + /* Save values from the RX URB before reposting overwrites it. */ + urb_status = urb->status; + usbin = (union hfa384x_usbin *)urb->transfer_buffer; + + if (action != ABORT) { + /* Repost the RX URB */ + result = submit_rx_urb(hw, GFP_ATOMIC); + + if (result != 0) { + netdev_err(hw->wlandev->netdev, + "Fatal, failed to resubmit rx_urb. error=%d\n", + result); + } + } + + /* Handle any USB-IN packet */ + /* Note: the check of the sw_support field, the type field doesn't + * have bit 12 set like the docs suggest. + */ + type = le16_to_cpu(usbin->type); + if (HFA384x_USB_ISRXFRM(type)) { + if (action == HANDLE) { + if (usbin->txfrm.desc.sw_support == 0x0123) { + hfa384x_usbin_txcompl(wlandev, usbin); + } else { + skb_put(skb, sizeof(*usbin)); + hfa384x_usbin_rx(wlandev, skb); + skb = NULL; + } + } + goto exit; + } + if (HFA384x_USB_ISTXFRM(type)) { + if (action == HANDLE) + hfa384x_usbin_txcompl(wlandev, usbin); + goto exit; + } + switch (type) { + case HFA384x_USB_INFOFRM: + if (action == ABORT) + goto exit; + if (action == HANDLE) + hfa384x_usbin_info(wlandev, usbin); + break; + + case HFA384x_USB_CMDRESP: + case HFA384x_USB_WRIDRESP: + case HFA384x_USB_RRIDRESP: + case HFA384x_USB_WMEMRESP: + case HFA384x_USB_RMEMRESP: + /* ALWAYS, ALWAYS, ALWAYS handle this CTLX!!!! */ + hfa384x_usbin_ctlx(hw, usbin, urb_status); + break; + + case HFA384x_USB_BUFAVAIL: + pr_debug("Received BUFAVAIL packet, frmlen=%d\n", + usbin->bufavail.frmlen); + break; + + case HFA384x_USB_ERROR: + pr_debug("Received USB_ERROR packet, errortype=%d\n", + usbin->usberror.errortype); + break; + + default: + pr_debug("Unrecognized USBIN packet, type=%x, status=%d\n", + usbin->type, urb_status); + break; + } /* switch */ + +exit: + + if (skb) + dev_kfree_skb(skb); +} + +/*---------------------------------------------------------------- + * hfa384x_usbin_ctlx + * + * We've received a URB containing a Prism2 "response" message. + * This message needs to be matched up with a CTLX on the active + * queue and our state updated accordingly. + * + * Arguments: + * hw ptr to struct hfa384x + * usbin ptr to USB IN packet + * urb_status status of this Bulk-In URB + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbin_ctlx(struct hfa384x *hw, union hfa384x_usbin *usbin, + int urb_status) +{ + struct hfa384x_usbctlx *ctlx; + int run_queue = 0; + unsigned long flags; + +retry: + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* There can be only one CTLX on the active queue + * at any one time, and this is the CTLX that the + * timers are waiting for. + */ + if (list_empty(&hw->ctlxq.active)) + goto unlock; + + /* Remove the "response timeout". It's possible that + * we are already too late, and that the timeout is + * already running. And that's just too bad for us, + * because we could lose our CTLX from the active + * queue here ... + */ + if (del_timer(&hw->resptimer) == 0) { + if (hw->resp_timer_done == 0) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + goto retry; + } + } else { + hw->resp_timer_done = 1; + } + + ctlx = get_active_ctlx(hw); + + if (urb_status != 0) { + /* + * Bad CTLX, so get rid of it. But we only + * remove it from the active queue if we're no + * longer expecting the OUT URB to complete. + */ + if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0) + run_queue = 1; + } else { + const __le16 intype = (usbin->type & ~cpu_to_le16(0x8000)); + + /* + * Check that our message is what we're expecting ... + */ + if (ctlx->outbuf.type != intype) { + netdev_warn(hw->wlandev->netdev, + "Expected IN[%d], received IN[%d] - ignored.\n", + le16_to_cpu(ctlx->outbuf.type), + le16_to_cpu(intype)); + goto unlock; + } + + /* This URB has succeeded, so grab the data ... */ + memcpy(&ctlx->inbuf, usbin, sizeof(ctlx->inbuf)); + + switch (ctlx->state) { + case CTLX_REQ_SUBMITTED: + /* + * We have received our response URB before + * our request has been acknowledged. Odd, + * but our OUT URB is still alive... + */ + pr_debug("Causality violation: please reboot Universe\n"); + ctlx->state = CTLX_RESP_COMPLETE; + break; + + case CTLX_REQ_COMPLETE: + /* + * This is the usual path: our request + * has already been acknowledged, and + * now we have received the reply too. + */ + ctlx->state = CTLX_COMPLETE; + unlocked_usbctlx_complete(hw, ctlx); + run_queue = 1; + break; + + default: + /* + * Throw this CTLX away ... + */ + netdev_err(hw->wlandev->netdev, + "Matched IN URB, CTLX[%d] in invalid state(%s). Discarded.\n", + le16_to_cpu(ctlx->outbuf.type), + ctlxstr(ctlx->state)); + if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0) + run_queue = 1; + break; + } /* switch */ + } + +unlock: + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + if (run_queue) + hfa384x_usbctlxq_run(hw); +} + +/*---------------------------------------------------------------- + * hfa384x_usbin_txcompl + * + * At this point we have the results of a previous transmit. + * + * Arguments: + * wlandev wlan device + * usbin ptr to the usb transfer buffer + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbin_txcompl(struct wlandevice *wlandev, + union hfa384x_usbin *usbin) +{ + u16 status; + + status = le16_to_cpu(usbin->type); /* yeah I know it says type... */ + + /* Was there an error? */ + if (HFA384x_TXSTATUS_ISERROR(status)) + prism2sta_ev_txexc(wlandev, status); + else + prism2sta_ev_tx(wlandev, status); +} + +/*---------------------------------------------------------------- + * hfa384x_usbin_rx + * + * At this point we have a successful received a rx frame packet. + * + * Arguments: + * wlandev wlan device + * usbin ptr to the usb transfer buffer + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbin_rx(struct wlandevice *wlandev, struct sk_buff *skb) +{ + union hfa384x_usbin *usbin = (union hfa384x_usbin *)skb->data; + struct hfa384x *hw = wlandev->priv; + int hdrlen; + struct p80211_rxmeta *rxmeta; + u16 data_len; + u16 fc; + u16 status; + + /* Byte order convert once up front. */ + le16_to_cpus(&usbin->rxfrm.desc.status); + le32_to_cpus(&usbin->rxfrm.desc.time); + + /* Now handle frame based on port# */ + status = HFA384x_RXSTATUS_MACPORT_GET(usbin->rxfrm.desc.status); + + switch (status) { + case 0: + fc = le16_to_cpu(usbin->rxfrm.desc.hdr.frame_control); + + /* If exclude and we receive an unencrypted, drop it */ + if ((wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED) && + !WLAN_GET_FC_ISWEP(fc)) { + break; + } + + data_len = le16_to_cpu(usbin->rxfrm.desc.data_len); + + /* How much header data do we have? */ + hdrlen = p80211_headerlen(fc); + + /* Pull off the descriptor */ + skb_pull(skb, sizeof(struct hfa384x_rx_frame)); + + /* Now shunt the header block up against the data block + * with an "overlapping" copy + */ + memmove(skb_push(skb, hdrlen), + &usbin->rxfrm.desc.hdr, hdrlen); + + skb->dev = wlandev->netdev; + + /* And set the frame length properly */ + skb_trim(skb, data_len + hdrlen); + + /* The prism2 series does not return the CRC */ + memset(skb_put(skb, WLAN_CRC_LEN), 0xff, WLAN_CRC_LEN); + + skb_reset_mac_header(skb); + + /* Attach the rxmeta, set some stuff */ + p80211skb_rxmeta_attach(wlandev, skb); + rxmeta = p80211skb_rxmeta(skb); + rxmeta->mactime = usbin->rxfrm.desc.time; + rxmeta->rxrate = usbin->rxfrm.desc.rate; + rxmeta->signal = usbin->rxfrm.desc.signal - hw->dbmadjust; + rxmeta->noise = usbin->rxfrm.desc.silence - hw->dbmadjust; + + p80211netdev_rx(wlandev, skb); + + break; + + case 7: + if (!HFA384x_RXSTATUS_ISFCSERR(usbin->rxfrm.desc.status)) { + /* Copy to wlansnif skb */ + hfa384x_int_rxmonitor(wlandev, &usbin->rxfrm); + dev_kfree_skb(skb); + } else { + pr_debug("Received monitor frame: FCSerr set\n"); + } + break; + + default: + netdev_warn(hw->wlandev->netdev, + "Received frame on unsupported port=%d\n", + status); + break; + } +} + +/*---------------------------------------------------------------- + * hfa384x_int_rxmonitor + * + * Helper function for int_rx. Handles monitor frames. + * Note that this function allocates space for the FCS and sets it + * to 0xffffffff. The hfa384x doesn't give us the FCS value but the + * higher layers expect it. 0xffffffff is used as a flag to indicate + * the FCS is bogus. + * + * Arguments: + * wlandev wlan device structure + * rxfrm rx descriptor read from card in int_rx + * + * Returns: + * nothing + * + * Side effects: + * Allocates an skb and passes it up via the PF_PACKET interface. + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_int_rxmonitor(struct wlandevice *wlandev, + struct hfa384x_usb_rxfrm *rxfrm) +{ + struct hfa384x_rx_frame *rxdesc = &rxfrm->desc; + unsigned int hdrlen = 0; + unsigned int datalen = 0; + unsigned int skblen = 0; + u8 *datap; + u16 fc; + struct sk_buff *skb; + struct hfa384x *hw = wlandev->priv; + + /* Remember the status, time, and data_len fields are in host order */ + /* Figure out how big the frame is */ + fc = le16_to_cpu(rxdesc->hdr.frame_control); + hdrlen = p80211_headerlen(fc); + datalen = le16_to_cpu(rxdesc->data_len); + + /* Allocate an ind message+framesize skb */ + skblen = sizeof(struct p80211_caphdr) + hdrlen + datalen + WLAN_CRC_LEN; + + /* sanity check the length */ + if (skblen > + (sizeof(struct p80211_caphdr) + + WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)) { + pr_debug("overlen frm: len=%zd\n", + skblen - sizeof(struct p80211_caphdr)); + + return; + } + + skb = dev_alloc_skb(skblen); + if (!skb) + return; + + /* only prepend the prism header if in the right mode */ + if ((wlandev->netdev->type == ARPHRD_IEEE80211_PRISM) && + (hw->sniffhdr != 0)) { + struct p80211_caphdr *caphdr; + /* The NEW header format! */ + datap = skb_put(skb, sizeof(struct p80211_caphdr)); + caphdr = (struct p80211_caphdr *)datap; + + caphdr->version = htonl(P80211CAPTURE_VERSION); + caphdr->length = htonl(sizeof(struct p80211_caphdr)); + caphdr->mactime = __cpu_to_be64(rxdesc->time * 1000); + caphdr->hosttime = __cpu_to_be64(jiffies); + caphdr->phytype = htonl(4); /* dss_dot11_b */ + caphdr->channel = htonl(hw->sniff_channel); + caphdr->datarate = htonl(rxdesc->rate); + caphdr->antenna = htonl(0); /* unknown */ + caphdr->priority = htonl(0); /* unknown */ + caphdr->ssi_type = htonl(3); /* rssi_raw */ + caphdr->ssi_signal = htonl(rxdesc->signal); + caphdr->ssi_noise = htonl(rxdesc->silence); + caphdr->preamble = htonl(0); /* unknown */ + caphdr->encoding = htonl(1); /* cck */ + } + + /* Copy the 802.11 header to the skb + * (ctl frames may be less than a full header) + */ + skb_put_data(skb, &rxdesc->hdr.frame_control, hdrlen); + + /* If any, copy the data from the card to the skb */ + if (datalen > 0) { + datap = skb_put_data(skb, rxfrm->data, datalen); + + /* check for unencrypted stuff if WEP bit set. */ + if (*(datap - hdrlen + 1) & 0x40) /* wep set */ + if ((*(datap) == 0xaa) && (*(datap + 1) == 0xaa)) + /* clear wep; it's the 802.2 header! */ + *(datap - hdrlen + 1) &= 0xbf; + } + + if (hw->sniff_fcs) { + /* Set the FCS */ + datap = skb_put(skb, WLAN_CRC_LEN); + memset(datap, 0xff, WLAN_CRC_LEN); + } + + /* pass it back up */ + p80211netdev_rx(wlandev, skb); +} + +/*---------------------------------------------------------------- + * hfa384x_usbin_info + * + * At this point we have a successful received a Prism2 info frame. + * + * Arguments: + * wlandev wlan device + * usbin ptr to the usb transfer buffer + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbin_info(struct wlandevice *wlandev, + union hfa384x_usbin *usbin) +{ + le16_to_cpus(&usbin->infofrm.info.framelen); + prism2sta_ev_info(wlandev, &usbin->infofrm.info); +} + +/*---------------------------------------------------------------- + * hfa384x_usbout_callback + * + * Callback for URBs on the BULKOUT endpoint. + * + * Arguments: + * urb ptr to the completed urb + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbout_callback(struct urb *urb) +{ + struct wlandevice *wlandev = urb->context; + +#ifdef DEBUG_USB + dbprint_urb(urb); +#endif + + if (wlandev && wlandev->netdev) { + switch (urb->status) { + case 0: + prism2sta_ev_alloc(wlandev); + break; + + case -EPIPE: { + struct hfa384x *hw = wlandev->priv; + + netdev_warn(hw->wlandev->netdev, + "%s tx pipe stalled: requesting reset\n", + wlandev->netdev->name); + if (!test_and_set_bit(WORK_TX_HALT, &hw->usb_flags)) + schedule_work(&hw->usb_work); + wlandev->netdev->stats.tx_errors++; + break; + } + + case -EPROTO: + case -ETIMEDOUT: + case -EILSEQ: { + struct hfa384x *hw = wlandev->priv; + + if (!test_and_set_bit(THROTTLE_TX, &hw->usb_flags) && + !timer_pending(&hw->throttle)) { + mod_timer(&hw->throttle, + jiffies + THROTTLE_JIFFIES); + } + wlandev->netdev->stats.tx_errors++; + netif_stop_queue(wlandev->netdev); + break; + } + + case -ENOENT: + case -ESHUTDOWN: + /* Ignorable errors */ + break; + + default: + netdev_info(wlandev->netdev, "unknown urb->status=%d\n", + urb->status); + wlandev->netdev->stats.tx_errors++; + break; + } /* switch */ + } +} + +/*---------------------------------------------------------------- + * hfa384x_ctlxout_callback + * + * Callback for control data on the BULKOUT endpoint. + * + * Arguments: + * urb ptr to the completed urb + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_ctlxout_callback(struct urb *urb) +{ + struct hfa384x *hw = urb->context; + int delete_resptimer = 0; + int timer_ok = 1; + int run_queue = 0; + struct hfa384x_usbctlx *ctlx; + unsigned long flags; + + pr_debug("urb->status=%d\n", urb->status); +#ifdef DEBUG_USB + dbprint_urb(urb); +#endif + if ((urb->status == -ESHUTDOWN) || + (urb->status == -ENODEV) || !hw) + return; + +retry: + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + /* + * Only one CTLX at a time on the "active" list, and + * none at all if we are unplugged. However, we can + * rely on the disconnect function to clean everything + * up if someone unplugged the adapter. + */ + if (list_empty(&hw->ctlxq.active)) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + return; + } + + /* + * Having something on the "active" queue means + * that we have timers to worry about ... + */ + if (del_timer(&hw->reqtimer) == 0) { + if (hw->req_timer_done == 0) { + /* + * This timer was actually running while we + * were trying to delete it. Let it terminate + * gracefully instead. + */ + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + goto retry; + } + } else { + hw->req_timer_done = 1; + } + + ctlx = get_active_ctlx(hw); + + if (urb->status == 0) { + /* Request portion of a CTLX is successful */ + switch (ctlx->state) { + case CTLX_REQ_SUBMITTED: + /* This OUT-ACK received before IN */ + ctlx->state = CTLX_REQ_COMPLETE; + break; + + case CTLX_RESP_COMPLETE: + /* IN already received before this OUT-ACK, + * so this command must now be complete. + */ + ctlx->state = CTLX_COMPLETE; + unlocked_usbctlx_complete(hw, ctlx); + run_queue = 1; + break; + + default: + /* This is NOT a valid CTLX "success" state! */ + netdev_err(hw->wlandev->netdev, + "Illegal CTLX[%d] success state(%s, %d) in OUT URB\n", + le16_to_cpu(ctlx->outbuf.type), + ctlxstr(ctlx->state), urb->status); + break; + } /* switch */ + } else { + /* If the pipe has stalled then we need to reset it */ + if ((urb->status == -EPIPE) && + !test_and_set_bit(WORK_TX_HALT, &hw->usb_flags)) { + netdev_warn(hw->wlandev->netdev, + "%s tx pipe stalled: requesting reset\n", + hw->wlandev->netdev->name); + schedule_work(&hw->usb_work); + } + + /* If someone cancels the OUT URB then its status + * should be either -ECONNRESET or -ENOENT. + */ + ctlx->state = CTLX_REQ_FAILED; + unlocked_usbctlx_complete(hw, ctlx); + delete_resptimer = 1; + run_queue = 1; + } + +delresp: + if (delete_resptimer) { + timer_ok = del_timer(&hw->resptimer); + if (timer_ok != 0) + hw->resp_timer_done = 1; + } + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + if (!timer_ok && (hw->resp_timer_done == 0)) { + spin_lock_irqsave(&hw->ctlxq.lock, flags); + goto delresp; + } + + if (run_queue) + hfa384x_usbctlxq_run(hw); +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_reqtimerfn + * + * Timer response function for CTLX request timeouts. If this + * function is called, it means that the callback for the OUT + * URB containing a Prism2.x XXX_Request was never called. + * + * Arguments: + * data a ptr to the struct hfa384x + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbctlx_reqtimerfn(struct timer_list *t) +{ + struct hfa384x *hw = from_timer(hw, t, reqtimer); + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + hw->req_timer_done = 1; + + /* Removing the hardware automatically empties + * the active list ... + */ + if (!list_empty(&hw->ctlxq.active)) { + /* + * We must ensure that our URB is removed from + * the system, if it hasn't already expired. + */ + hw->ctlx_urb.transfer_flags |= URB_ASYNC_UNLINK; + if (usb_unlink_urb(&hw->ctlx_urb) == -EINPROGRESS) { + struct hfa384x_usbctlx *ctlx = get_active_ctlx(hw); + + ctlx->state = CTLX_REQ_FAILED; + + /* This URB was active, but has now been + * cancelled. It will now have a status of + * -ECONNRESET in the callback function. + * + * We are cancelling this CTLX, so we're + * not going to need to wait for a response. + * The URB's callback function will check + * that this timer is truly dead. + */ + if (del_timer(&hw->resptimer) != 0) + hw->resp_timer_done = 1; + } + } + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_resptimerfn + * + * Timer response function for CTLX response timeouts. If this + * function is called, it means that the callback for the IN + * URB containing a Prism2.x XXX_Response was never called. + * + * Arguments: + * data a ptr to the struct hfa384x + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usbctlx_resptimerfn(struct timer_list *t) +{ + struct hfa384x *hw = from_timer(hw, t, resptimer); + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + hw->resp_timer_done = 1; + + /* The active list will be empty if the + * adapter has been unplugged ... + */ + if (!list_empty(&hw->ctlxq.active)) { + struct hfa384x_usbctlx *ctlx = get_active_ctlx(hw); + + if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + hfa384x_usbctlxq_run(hw); + return; + } + } + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usb_throttlefn + * + * + * Arguments: + * data ptr to hw + * + * Returns: + * Nothing + * + * Side effects: + * + * Call context: + * Interrupt + *---------------------------------------------------------------- + */ +static void hfa384x_usb_throttlefn(struct timer_list *t) +{ + struct hfa384x *hw = from_timer(hw, t, throttle); + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + pr_debug("flags=0x%lx\n", hw->usb_flags); + if (!hw->wlandev->hwremoved) { + bool rx_throttle = test_and_clear_bit(THROTTLE_RX, &hw->usb_flags) && + !test_and_set_bit(WORK_RX_RESUME, &hw->usb_flags); + bool tx_throttle = test_and_clear_bit(THROTTLE_TX, &hw->usb_flags) && + !test_and_set_bit(WORK_TX_RESUME, &hw->usb_flags); + /* + * We need to check BOTH the RX and the TX throttle controls, + * so we use the bitwise OR instead of the logical OR. + */ + if (rx_throttle | tx_throttle) + schedule_work(&hw->usb_work); + } + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); +} + +/*---------------------------------------------------------------- + * hfa384x_usbctlx_submit + * + * Called from the doxxx functions to submit a CTLX to the queue + * + * Arguments: + * hw ptr to the hw struct + * ctlx ctlx structure to enqueue + * + * Returns: + * -ENODEV if the adapter is unplugged + * 0 + * + * Side effects: + * + * Call context: + * process or interrupt + *---------------------------------------------------------------- + */ +static int hfa384x_usbctlx_submit(struct hfa384x *hw, + struct hfa384x_usbctlx *ctlx) +{ + unsigned long flags; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + if (hw->wlandev->hwremoved) { + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + return -ENODEV; + } + + ctlx->state = CTLX_PENDING; + list_add_tail(&ctlx->list, &hw->ctlxq.pending); + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + hfa384x_usbctlxq_run(hw); + + return 0; +} + +/*---------------------------------------------------------------- + * hfa384x_isgood_pdrcore + * + * Quick check of PDR codes. + * + * Arguments: + * pdrcode PDR code number (host order) + * + * Returns: + * zero not good. + * one is good. + * + * Side effects: + * + * Call context: + *---------------------------------------------------------------- + */ +static int hfa384x_isgood_pdrcode(u16 pdrcode) +{ + switch (pdrcode) { + case HFA384x_PDR_END_OF_PDA: + case HFA384x_PDR_PCB_PARTNUM: + case HFA384x_PDR_PDAVER: + case HFA384x_PDR_NIC_SERIAL: + case HFA384x_PDR_MKK_MEASUREMENTS: + case HFA384x_PDR_NIC_RAMSIZE: + case HFA384x_PDR_MFISUPRANGE: + case HFA384x_PDR_CFISUPRANGE: + case HFA384x_PDR_NICID: + case HFA384x_PDR_MAC_ADDRESS: + case HFA384x_PDR_REGDOMAIN: + case HFA384x_PDR_ALLOWED_CHANNEL: + case HFA384x_PDR_DEFAULT_CHANNEL: + case HFA384x_PDR_TEMPTYPE: + case HFA384x_PDR_IFR_SETTING: + case HFA384x_PDR_RFR_SETTING: + case HFA384x_PDR_HFA3861_BASELINE: + case HFA384x_PDR_HFA3861_SHADOW: + case HFA384x_PDR_HFA3861_IFRF: + case HFA384x_PDR_HFA3861_CHCALSP: + case HFA384x_PDR_HFA3861_CHCALI: + case HFA384x_PDR_3842_NIC_CONFIG: + case HFA384x_PDR_USB_ID: + case HFA384x_PDR_PCI_ID: + case HFA384x_PDR_PCI_IFCONF: + case HFA384x_PDR_PCI_PMCONF: + case HFA384x_PDR_RFENRGY: + case HFA384x_PDR_HFA3861_MANF_TESTSP: + case HFA384x_PDR_HFA3861_MANF_TESTI: + /* code is OK */ + return 1; + default: + if (pdrcode < 0x1000) { + /* code is OK, but we don't know exactly what it is */ + pr_debug("Encountered unknown PDR#=0x%04x, assuming it's ok.\n", + pdrcode); + return 1; + } + break; + } + /* bad code */ + pr_debug("Encountered unknown PDR#=0x%04x, (>=0x1000), assuming it's bad.\n", + pdrcode); + return 0; +} diff --git a/drivers/staging/wlan-ng/p80211conv.c b/drivers/staging/wlan-ng/p80211conv.c new file mode 100644 index 0000000000..048e1c3fe1 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211conv.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Ether/802.11 conversions and packet buffer routines + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file defines the functions that perform Ethernet to/from + * 802.11 frame conversions. + * + * -------------------------------------------------------------------- + * + *================================================================ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211conv.h" +#include "p80211mgmt.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211ioctl.h" +#include "p80211req.h" + +static const u8 oui_rfc1042[] = { 0x00, 0x00, 0x00 }; +static const u8 oui_8021h[] = { 0x00, 0x00, 0xf8 }; + +/*---------------------------------------------------------------- + * p80211pb_ether_to_80211 + * + * Uses the contents of the ether frame and the etherconv setting + * to build the elements of the 802.11 frame. + * + * We don't actually set + * up the frame header here. That's the MAC's job. We're only handling + * conversion of DIXII or 802.3+LLC frames to something that works + * with 802.11. + * + * Note -- 802.11 header is NOT part of the skb. Likewise, the 802.11 + * FCS is also not present and will need to be added elsewhere. + * + * Arguments: + * ethconv Conversion type to perform + * skb skbuff containing the ether frame + * p80211_hdr 802.11 header + * + * Returns: + * 0 on success, non-zero otherwise + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +int skb_ether_to_p80211(struct wlandevice *wlandev, u32 ethconv, + struct sk_buff *skb, struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep) +{ + __le16 fc; + u16 proto; + struct wlan_ethhdr e_hdr; + struct wlan_llc *e_llc; + struct wlan_snap *e_snap; + int foo; + + memcpy(&e_hdr, skb->data, sizeof(e_hdr)); + + if (skb->len <= 0) { + pr_debug("zero-length skb!\n"); + return 1; + } + + if (ethconv == WLAN_ETHCONV_ENCAP) { /* simplest case */ + pr_debug("ENCAP len: %d\n", skb->len); + /* here, we don't care what kind of ether frm. Just stick it */ + /* in the 80211 payload */ + /* which is to say, leave the skb alone. */ + } else { + /* step 1: classify ether frame, DIX or 802.3? */ + proto = ntohs(e_hdr.type); + if (proto <= ETH_DATA_LEN) { + pr_debug("802.3 len: %d\n", skb->len); + /* codes <= 1500 reserved for 802.3 lengths */ + /* it's 802.3, pass ether payload unchanged, */ + + /* trim off ethernet header */ + skb_pull(skb, ETH_HLEN); + + /* leave off any PAD octets. */ + skb_trim(skb, proto); + } else { + pr_debug("DIXII len: %d\n", skb->len); + /* it's DIXII, time for some conversion */ + + /* trim off ethernet header */ + skb_pull(skb, ETH_HLEN); + + /* tack on SNAP */ + e_snap = skb_push(skb, sizeof(struct wlan_snap)); + e_snap->type = htons(proto); + if (ethconv == WLAN_ETHCONV_8021h && + p80211_stt_findproto(proto)) { + memcpy(e_snap->oui, oui_8021h, + WLAN_IEEE_OUI_LEN); + } else { + memcpy(e_snap->oui, oui_rfc1042, + WLAN_IEEE_OUI_LEN); + } + + /* tack on llc */ + e_llc = skb_push(skb, sizeof(struct wlan_llc)); + e_llc->dsap = 0xAA; /* SNAP, see IEEE 802 */ + e_llc->ssap = 0xAA; + e_llc->ctl = 0x03; + } + } + + /* Set up the 802.11 header */ + /* It's a data frame */ + fc = cpu_to_le16(WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) | + WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY)); + + switch (wlandev->macmode) { + case WLAN_MACMODE_IBSS_STA: + memcpy(p80211_hdr->address1, &e_hdr.daddr, ETH_ALEN); + memcpy(p80211_hdr->address2, wlandev->netdev->dev_addr, ETH_ALEN); + memcpy(p80211_hdr->address3, wlandev->bssid, ETH_ALEN); + break; + case WLAN_MACMODE_ESS_STA: + fc |= cpu_to_le16(WLAN_SET_FC_TODS(1)); + memcpy(p80211_hdr->address1, wlandev->bssid, ETH_ALEN); + memcpy(p80211_hdr->address2, wlandev->netdev->dev_addr, ETH_ALEN); + memcpy(p80211_hdr->address3, &e_hdr.daddr, ETH_ALEN); + break; + case WLAN_MACMODE_ESS_AP: + fc |= cpu_to_le16(WLAN_SET_FC_FROMDS(1)); + memcpy(p80211_hdr->address1, &e_hdr.daddr, ETH_ALEN); + memcpy(p80211_hdr->address2, wlandev->bssid, ETH_ALEN); + memcpy(p80211_hdr->address3, &e_hdr.saddr, ETH_ALEN); + break; + default: + netdev_err(wlandev->netdev, + "Error: Converting eth to wlan in unknown mode.\n"); + return 1; + } + + p80211_wep->data = NULL; + + if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) && + (wlandev->hostwep & HOSTWEP_ENCRYPT)) { + /* XXXX need to pick keynum other than default? */ + + p80211_wep->data = kmalloc(skb->len, GFP_ATOMIC); + if (!p80211_wep->data) + return -ENOMEM; + foo = wep_encrypt(wlandev, skb->data, p80211_wep->data, + skb->len, + wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK, + p80211_wep->iv, p80211_wep->icv); + if (foo) { + netdev_warn(wlandev->netdev, + "Host en-WEP failed, dropping frame (%d).\n", + foo); + kfree(p80211_wep->data); + return 2; + } + fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1)); + } + + /* skb->nh.raw = skb->data; */ + + p80211_hdr->frame_control = fc; + p80211_hdr->duration_id = 0; + p80211_hdr->sequence_control = 0; + + return 0; +} + +/* jkriegl: from orinoco, modified */ +static void orinoco_spy_gather(struct wlandevice *wlandev, char *mac, + struct p80211_rxmeta *rxmeta) +{ + int i; + + /* Gather wireless spy statistics: for each packet, compare the + * source address with out list, and if match, get the stats... + */ + + for (i = 0; i < wlandev->spy_number; i++) { + if (!memcmp(wlandev->spy_address[i], mac, ETH_ALEN)) { + wlandev->spy_stat[i].level = rxmeta->signal; + wlandev->spy_stat[i].noise = rxmeta->noise; + wlandev->spy_stat[i].qual = + (rxmeta->signal > + rxmeta->noise) ? (rxmeta->signal - + rxmeta->noise) : 0; + wlandev->spy_stat[i].updated = 0x7; + } + } +} + +/*---------------------------------------------------------------- + * p80211pb_80211_to_ether + * + * Uses the contents of a received 802.11 frame and the etherconv + * setting to build an ether frame. + * + * This function extracts the src and dest address from the 802.11 + * frame to use in the construction of the eth frame. + * + * Arguments: + * ethconv Conversion type to perform + * skb Packet buffer containing the 802.11 frame + * + * Returns: + * 0 on success, non-zero otherwise + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +int skb_p80211_to_ether(struct wlandevice *wlandev, u32 ethconv, + struct sk_buff *skb) +{ + struct net_device *netdev = wlandev->netdev; + u16 fc; + unsigned int payload_length; + unsigned int payload_offset; + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + struct p80211_hdr *w_hdr; + struct wlan_ethhdr *e_hdr; + struct wlan_llc *e_llc; + struct wlan_snap *e_snap; + + int foo; + + payload_length = skb->len - WLAN_HDR_A3_LEN - WLAN_CRC_LEN; + payload_offset = WLAN_HDR_A3_LEN; + + w_hdr = (struct p80211_hdr *)skb->data; + + /* setup some vars for convenience */ + fc = le16_to_cpu(w_hdr->frame_control); + if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 0)) { + ether_addr_copy(daddr, w_hdr->address1); + ether_addr_copy(saddr, w_hdr->address2); + } else if ((WLAN_GET_FC_TODS(fc) == 0) && + (WLAN_GET_FC_FROMDS(fc) == 1)) { + ether_addr_copy(daddr, w_hdr->address1); + ether_addr_copy(saddr, w_hdr->address3); + } else if ((WLAN_GET_FC_TODS(fc) == 1) && + (WLAN_GET_FC_FROMDS(fc) == 0)) { + ether_addr_copy(daddr, w_hdr->address3); + ether_addr_copy(saddr, w_hdr->address2); + } else { + payload_offset = WLAN_HDR_A4_LEN; + if (payload_length < WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN) { + netdev_err(netdev, "A4 frame too short!\n"); + return 1; + } + payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); + ether_addr_copy(daddr, w_hdr->address3); + ether_addr_copy(saddr, w_hdr->address4); + } + + /* perform de-wep if necessary.. */ + if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) && + WLAN_GET_FC_ISWEP(fc) && + (wlandev->hostwep & HOSTWEP_DECRYPT)) { + if (payload_length <= 8) { + netdev_err(netdev, + "WEP frame too short (%u).\n", skb->len); + return 1; + } + foo = wep_decrypt(wlandev, skb->data + payload_offset + 4, + payload_length - 8, -1, + skb->data + payload_offset, + skb->data + payload_offset + + payload_length - 4); + if (foo) { + /* de-wep failed, drop skb. */ + pr_debug("Host de-WEP failed, dropping frame (%d).\n", + foo); + wlandev->rx.decrypt_err++; + return 2; + } + + /* subtract the IV+ICV length off the payload */ + payload_length -= 8; + /* chop off the IV */ + skb_pull(skb, 4); + /* chop off the ICV. */ + skb_trim(skb, skb->len - 4); + + wlandev->rx.decrypt++; + } + + e_hdr = (struct wlan_ethhdr *)(skb->data + payload_offset); + + e_llc = (struct wlan_llc *)(skb->data + payload_offset); + e_snap = + (struct wlan_snap *)(skb->data + payload_offset + + sizeof(struct wlan_llc)); + + /* Test for the various encodings */ + if ((payload_length >= sizeof(struct wlan_ethhdr)) && + (e_llc->dsap != 0xaa || e_llc->ssap != 0xaa) && + ((!ether_addr_equal_unaligned(daddr, e_hdr->daddr)) || + (!ether_addr_equal_unaligned(saddr, e_hdr->saddr)))) { + pr_debug("802.3 ENCAP len: %d\n", payload_length); + /* 802.3 Encapsulated */ + /* Test for an overlength frame */ + if (payload_length > (netdev->mtu + ETH_HLEN)) { + /* A bogus length ethfrm has been encap'd. */ + /* Is someone trying an oflow attack? */ + netdev_err(netdev, "ENCAP frame too large (%d > %d)\n", + payload_length, netdev->mtu + ETH_HLEN); + return 1; + } + + /* Chop off the 802.11 header. it's already sane. */ + skb_pull(skb, payload_offset); + /* chop off the 802.11 CRC */ + skb_trim(skb, skb->len - WLAN_CRC_LEN); + + } else if ((payload_length >= sizeof(struct wlan_llc) + + sizeof(struct wlan_snap)) && + (e_llc->dsap == 0xaa) && + (e_llc->ssap == 0xaa) && + (e_llc->ctl == 0x03) && + (((memcmp(e_snap->oui, oui_rfc1042, + WLAN_IEEE_OUI_LEN) == 0) && + (ethconv == WLAN_ETHCONV_8021h) && + (p80211_stt_findproto(be16_to_cpu(e_snap->type)))) || + (memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) != + 0))) { + pr_debug("SNAP+RFC1042 len: %d\n", payload_length); + /* it's a SNAP + RFC1042 frame && protocol is in STT */ + /* build 802.3 + RFC1042 */ + + /* Test for an overlength frame */ + if (payload_length > netdev->mtu) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + netdev_err(netdev, "SNAP frame too large (%d > %d)\n", + payload_length, netdev->mtu); + return 1; + } + + /* chop 802.11 header from skb. */ + skb_pull(skb, payload_offset); + + /* create 802.3 header at beginning of skb. */ + e_hdr = skb_push(skb, ETH_HLEN); + ether_addr_copy(e_hdr->daddr, daddr); + ether_addr_copy(e_hdr->saddr, saddr); + e_hdr->type = htons(payload_length); + + /* chop off the 802.11 CRC */ + skb_trim(skb, skb->len - WLAN_CRC_LEN); + + } else if ((payload_length >= sizeof(struct wlan_llc) + + sizeof(struct wlan_snap)) && + (e_llc->dsap == 0xaa) && + (e_llc->ssap == 0xaa) && + (e_llc->ctl == 0x03)) { + pr_debug("802.1h/RFC1042 len: %d\n", payload_length); + /* it's an 802.1h frame || (an RFC1042 && protocol not in STT) + * build a DIXII + RFC894 + */ + + /* Test for an overlength frame */ + if ((payload_length - sizeof(struct wlan_llc) - + sizeof(struct wlan_snap)) + > netdev->mtu) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + netdev_err(netdev, "DIXII frame too large (%ld > %d)\n", + (long)(payload_length - + sizeof(struct wlan_llc) - + sizeof(struct wlan_snap)), netdev->mtu); + return 1; + } + + /* chop 802.11 header from skb. */ + skb_pull(skb, payload_offset); + + /* chop llc header from skb. */ + skb_pull(skb, sizeof(struct wlan_llc)); + + /* chop snap header from skb. */ + skb_pull(skb, sizeof(struct wlan_snap)); + + /* create 802.3 header at beginning of skb. */ + e_hdr = skb_push(skb, ETH_HLEN); + e_hdr->type = e_snap->type; + ether_addr_copy(e_hdr->daddr, daddr); + ether_addr_copy(e_hdr->saddr, saddr); + + /* chop off the 802.11 CRC */ + skb_trim(skb, skb->len - WLAN_CRC_LEN); + } else { + pr_debug("NON-ENCAP len: %d\n", payload_length); + /* any NON-ENCAP */ + /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ + /* build an 802.3 frame */ + /* allocate space and setup hostbuf */ + + /* Test for an overlength frame */ + if (payload_length > netdev->mtu) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + netdev_err(netdev, "OTHER frame too large (%d > %d)\n", + payload_length, netdev->mtu); + return 1; + } + + /* Chop off the 802.11 header. */ + skb_pull(skb, payload_offset); + + /* create 802.3 header at beginning of skb. */ + e_hdr = skb_push(skb, ETH_HLEN); + ether_addr_copy(e_hdr->daddr, daddr); + ether_addr_copy(e_hdr->saddr, saddr); + e_hdr->type = htons(payload_length); + + /* chop off the 802.11 CRC */ + skb_trim(skb, skb->len - WLAN_CRC_LEN); + } + + /* + * Note that eth_type_trans() expects an skb w/ skb->data pointing + * at the MAC header, it then sets the following skb members: + * skb->mac_header, + * skb->data, and + * skb->pkt_type. + * It then _returns_ the value that _we're_ supposed to stuff in + * skb->protocol. This is nuts. + */ + skb->protocol = eth_type_trans(skb, netdev); + + /* jkriegl: process signal and noise as set in hfa384x_int_rx() */ + /* jkriegl: only process signal/noise if requested by iwspy */ + if (wlandev->spy_number) + orinoco_spy_gather(wlandev, eth_hdr(skb)->h_source, + p80211skb_rxmeta(skb)); + + /* Free the metadata */ + p80211skb_rxmeta_detach(skb); + + return 0; +} + +/*---------------------------------------------------------------- + * p80211_stt_findproto + * + * Searches the 802.1h Selective Translation Table for a given + * protocol. + * + * Arguments: + * proto protocol number (in host order) to search for. + * + * Returns: + * 1 - if the table is empty or a match is found. + * 0 - if the table is non-empty and a match is not found. + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +int p80211_stt_findproto(u16 proto) +{ + /* Always return found for now. This is the behavior used by the */ + /* Zoom Win95 driver when 802.1h mode is selected */ + /* TODO: If necessary, add an actual search we'll probably + * need this to match the CMAC's way of doing things. + * Need to do some testing to confirm. + */ + + if (proto == ETH_P_AARP) /* APPLETALK */ + return 1; + + return 0; +} + +/*---------------------------------------------------------------- + * p80211skb_rxmeta_detach + * + * Disconnects the frmmeta and rxmeta from an skb. + * + * Arguments: + * wlandev The wlandev this skb belongs to. + * skb The skb we're attaching to. + * + * Returns: + * 0 on success, non-zero otherwise + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +void p80211skb_rxmeta_detach(struct sk_buff *skb) +{ + struct p80211_rxmeta *rxmeta; + struct p80211_frmmeta *frmmeta; + + /* Sanity checks */ + if (!skb) { /* bad skb */ + pr_debug("Called w/ null skb.\n"); + return; + } + frmmeta = p80211skb_frmmeta(skb); + if (!frmmeta) { /* no magic */ + pr_debug("Called w/ bad frmmeta magic.\n"); + return; + } + rxmeta = frmmeta->rx; + if (!rxmeta) { /* bad meta ptr */ + pr_debug("Called w/ bad rxmeta ptr.\n"); + return; + } + + /* Free rxmeta */ + kfree(rxmeta); + + /* Clear skb->cb */ + memset(skb->cb, 0, sizeof(skb->cb)); +} + +/*---------------------------------------------------------------- + * p80211skb_rxmeta_attach + * + * Allocates a p80211rxmeta structure, initializes it, and attaches + * it to an skb. + * + * Arguments: + * wlandev The wlandev this skb belongs to. + * skb The skb we're attaching to. + * + * Returns: + * 0 on success, non-zero otherwise + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb) +{ + int result = 0; + struct p80211_rxmeta *rxmeta; + struct p80211_frmmeta *frmmeta; + + /* If these already have metadata, we error out! */ + if (p80211skb_rxmeta(skb)) { + netdev_err(wlandev->netdev, + "%s: RXmeta already attached!\n", wlandev->name); + result = 0; + goto exit; + } + + /* Allocate the rxmeta */ + rxmeta = kzalloc(sizeof(*rxmeta), GFP_ATOMIC); + + if (!rxmeta) { + result = 1; + goto exit; + } + + /* Initialize the rxmeta */ + rxmeta->wlandev = wlandev; + rxmeta->hosttime = jiffies; + + /* Overlay a frmmeta_t onto skb->cb */ + memset(skb->cb, 0, sizeof(struct p80211_frmmeta)); + frmmeta = (struct p80211_frmmeta *)(skb->cb); + frmmeta->magic = P80211_FRMMETA_MAGIC; + frmmeta->rx = rxmeta; +exit: + return result; +} + +/*---------------------------------------------------------------- + * p80211skb_free + * + * Frees an entire p80211skb by checking and freeing the meta struct + * and then freeing the skb. + * + * Arguments: + * wlandev The wlandev this skb belongs to. + * skb The skb we're attaching to. + * + * Returns: + * 0 on success, non-zero otherwise + * + * Call context: + * May be called in interrupt or non-interrupt context + *---------------------------------------------------------------- + */ +void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb) +{ + struct p80211_frmmeta *meta; + + meta = p80211skb_frmmeta(skb); + if (meta && meta->rx) + p80211skb_rxmeta_detach(skb); + else + netdev_err(wlandev->netdev, + "Freeing an skb (%p) w/ no frmmeta.\n", skb); + dev_kfree_skb(skb); +} diff --git a/drivers/staging/wlan-ng/p80211conv.h b/drivers/staging/wlan-ng/p80211conv.h new file mode 100644 index 0000000000..45234769f4 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211conv.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Ether/802.11 conversions and packet buffer routines + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file declares the functions, types and macros that perform + * Ethernet to/from 802.11 frame conversions. + * + * -------------------------------------------------------------------- + */ + +#ifndef _LINUX_P80211CONV_H +#define _LINUX_P80211CONV_H + +#define WLAN_IEEE_OUI_LEN 3 + +#define WLAN_ETHCONV_ENCAP 1 +#define WLAN_ETHCONV_8021h 3 + +#define P80211CAPTURE_VERSION 0x80211001 + +#define P80211_FRMMETA_MAGIC 0x802110 + +struct p80211_rxmeta { + struct wlandevice *wlandev; + + u64 mactime; /* Hi-rez MAC-supplied time value */ + u64 hosttime; /* Best-rez host supplied time value */ + + unsigned int rxrate; /* Receive data rate in 100kbps */ + unsigned int priority; /* 0-15, 0=contention, 6=CF */ + int signal; /* An SSI, see p80211netdev.h */ + int noise; /* An SSI, see p80211netdev.h */ + unsigned int channel; /* Receive channel (mostly for snifs) */ + unsigned int preamble; /* P80211ENUM_preambletype_* */ + unsigned int encoding; /* P80211ENUM_encoding_* */ + +}; + +struct p80211_frmmeta { + unsigned int magic; + struct p80211_rxmeta *rx; +}; + +void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb); +int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb); +void p80211skb_rxmeta_detach(struct sk_buff *skb); + +static inline struct p80211_frmmeta *p80211skb_frmmeta(struct sk_buff *skb) +{ + struct p80211_frmmeta *frmmeta = (struct p80211_frmmeta *)skb->cb; + + return frmmeta->magic == P80211_FRMMETA_MAGIC ? frmmeta : NULL; +} + +static inline struct p80211_rxmeta *p80211skb_rxmeta(struct sk_buff *skb) +{ + struct p80211_frmmeta *frmmeta = p80211skb_frmmeta(skb); + + return frmmeta ? frmmeta->rx : NULL; +} + +/* + * Frame capture header. (See doc/capturefrm.txt) + */ +struct p80211_caphdr { + __be32 version; + __be32 length; + __be64 mactime; + __be64 hosttime; + __be32 phytype; + __be32 channel; + __be32 datarate; + __be32 antenna; + __be32 priority; + __be32 ssi_type; + __be32 ssi_signal; + __be32 ssi_noise; + __be32 preamble; + __be32 encoding; +}; + +struct p80211_metawep { + void *data; + u8 iv[4]; + u8 icv[4]; +}; + +/* local ether header type */ +struct wlan_ethhdr { + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + __be16 type; +} __packed; + +/* local llc header type */ +struct wlan_llc { + u8 dsap; + u8 ssap; + u8 ctl; +} __packed; + +/* local snap header type */ +struct wlan_snap { + u8 oui[WLAN_IEEE_OUI_LEN]; + __be16 type; +} __packed; + +/* Circular include trick */ +struct wlandevice; + +int skb_p80211_to_ether(struct wlandevice *wlandev, u32 ethconv, + struct sk_buff *skb); +int skb_ether_to_p80211(struct wlandevice *wlandev, u32 ethconv, + struct sk_buff *skb, struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep); + +int p80211_stt_findproto(u16 proto); + +#endif diff --git a/drivers/staging/wlan-ng/p80211hdr.h b/drivers/staging/wlan-ng/p80211hdr.h new file mode 100644 index 0000000000..7ea1c8ec05 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211hdr.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Macros, types, and functions for handling 802.11 MAC headers + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file declares the constants and types used in the interface + * between a wlan driver and the user mode utilities. + * + * Note: + * - Constant values are always in HOST byte order. To assign + * values to multi-byte fields they _must_ be converted to + * ieee byte order. To retrieve multi-byte values from incoming + * frames, they must be converted to host order. + * + * All functions declared here are implemented in p80211.c + * -------------------------------------------------------------------- + */ + +#ifndef _P80211HDR_H +#define _P80211HDR_H + +#include + +/*--- Sizes -----------------------------------------------*/ +#define WLAN_CRC_LEN 4 +#define WLAN_BSSID_LEN 6 +#define WLAN_HDR_A3_LEN 24 +#define WLAN_HDR_A4_LEN 30 +#define WLAN_SSID_MAXLEN 32 +#define WLAN_DATA_MAXLEN 2312 +#define WLAN_WEP_IV_LEN 4 +#define WLAN_WEP_ICV_LEN 4 + +/*--- Frame Control Field -------------------------------------*/ +/* Frame Types */ +#define WLAN_FTYPE_MGMT 0x00 +#define WLAN_FTYPE_CTL 0x01 +#define WLAN_FTYPE_DATA 0x02 + +/* Frame subtypes */ +/* Management */ +#define WLAN_FSTYPE_ASSOCREQ 0x00 +#define WLAN_FSTYPE_ASSOCRESP 0x01 +#define WLAN_FSTYPE_REASSOCREQ 0x02 +#define WLAN_FSTYPE_REASSOCRESP 0x03 +#define WLAN_FSTYPE_PROBEREQ 0x04 +#define WLAN_FSTYPE_PROBERESP 0x05 +#define WLAN_FSTYPE_BEACON 0x08 +#define WLAN_FSTYPE_ATIM 0x09 +#define WLAN_FSTYPE_DISASSOC 0x0a +#define WLAN_FSTYPE_AUTHEN 0x0b +#define WLAN_FSTYPE_DEAUTHEN 0x0c + +/* Control */ +#define WLAN_FSTYPE_BLOCKACKREQ 0x8 +#define WLAN_FSTYPE_BLOCKACK 0x9 +#define WLAN_FSTYPE_PSPOLL 0x0a +#define WLAN_FSTYPE_RTS 0x0b +#define WLAN_FSTYPE_CTS 0x0c +#define WLAN_FSTYPE_ACK 0x0d +#define WLAN_FSTYPE_CFEND 0x0e +#define WLAN_FSTYPE_CFENDCFACK 0x0f + +/* Data */ +#define WLAN_FSTYPE_DATAONLY 0x00 +#define WLAN_FSTYPE_DATA_CFACK 0x01 +#define WLAN_FSTYPE_DATA_CFPOLL 0x02 +#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03 +#define WLAN_FSTYPE_NULL 0x04 +#define WLAN_FSTYPE_CFACK 0x05 +#define WLAN_FSTYPE_CFPOLL 0x06 +#define WLAN_FSTYPE_CFACK_CFPOLL 0x07 + +/*--- FC Macros ----------------------------------------------*/ +/* Macros to get/set the bitfields of the Frame Control Field */ +/* GET_FC_??? - takes the host byte-order value of an FC */ +/* and retrieves the value of one of the */ +/* bitfields and moves that value so its lsb is */ +/* in bit 0. */ +/* SET_FC_??? - takes a host order value for one of the FC */ +/* bitfields and moves it to the proper bit */ +/* location for ORing into a host order FC. */ +/* To send the FC produced from SET_FC_???, */ +/* one must put the bytes in IEEE order. */ +/* e.g. */ +/* printf("the frame subtype is %x", */ +/* GET_FC_FTYPE( ieee2host( rx.fc ))) */ +/* */ +/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */ +/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */ +/*------------------------------------------------------------*/ + +#define WLAN_GET_FC_FTYPE(n) ((((u16)(n)) & GENMASK(3, 2)) >> 2) +#define WLAN_GET_FC_FSTYPE(n) ((((u16)(n)) & GENMASK(7, 4)) >> 4) +#define WLAN_GET_FC_TODS(n) ((((u16)(n)) & (BIT(8))) >> 8) +#define WLAN_GET_FC_FROMDS(n) ((((u16)(n)) & (BIT(9))) >> 9) +#define WLAN_GET_FC_ISWEP(n) ((((u16)(n)) & (BIT(14))) >> 14) + +#define WLAN_SET_FC_FTYPE(n) (((u16)(n)) << 2) +#define WLAN_SET_FC_FSTYPE(n) (((u16)(n)) << 4) +#define WLAN_SET_FC_TODS(n) (((u16)(n)) << 8) +#define WLAN_SET_FC_FROMDS(n) (((u16)(n)) << 9) +#define WLAN_SET_FC_ISWEP(n) (((u16)(n)) << 14) + +#define DOT11_RATE5_ISBASIC_GET(r) (((u8)(r)) & BIT(7)) + +/* Generic 802.11 Header types */ + +struct p80211_hdr { + __le16 frame_control; + u16 duration_id; + u8 address1[ETH_ALEN]; + u8 address2[ETH_ALEN]; + u8 address3[ETH_ALEN]; + u16 sequence_control; + u8 address4[ETH_ALEN]; +} __packed; + +/* Frame and header length macros */ + +static inline u16 wlan_ctl_framelen(u16 fstype) +{ + switch (fstype) { + case WLAN_FSTYPE_BLOCKACKREQ: + return 24; + case WLAN_FSTYPE_BLOCKACK: + return 152; + case WLAN_FSTYPE_PSPOLL: + case WLAN_FSTYPE_RTS: + case WLAN_FSTYPE_CFEND: + case WLAN_FSTYPE_CFENDCFACK: + return 20; + case WLAN_FSTYPE_CTS: + case WLAN_FSTYPE_ACK: + return 14; + default: + return 4; + } +} + +#define WLAN_FCS_LEN 4 + +/* ftcl in HOST order */ +static inline u16 p80211_headerlen(u16 fctl) +{ + u16 hdrlen = 0; + + switch (WLAN_GET_FC_FTYPE(fctl)) { + case WLAN_FTYPE_MGMT: + hdrlen = WLAN_HDR_A3_LEN; + break; + case WLAN_FTYPE_DATA: + hdrlen = WLAN_HDR_A3_LEN; + if (WLAN_GET_FC_TODS(fctl) && WLAN_GET_FC_FROMDS(fctl)) + hdrlen += ETH_ALEN; + break; + case WLAN_FTYPE_CTL: + hdrlen = wlan_ctl_framelen(WLAN_GET_FC_FSTYPE(fctl)) - + WLAN_FCS_LEN; + break; + default: + hdrlen = WLAN_HDR_A3_LEN; + } + + return hdrlen; +} + +#endif /* _P80211HDR_H */ diff --git a/drivers/staging/wlan-ng/p80211ioctl.h b/drivers/staging/wlan-ng/p80211ioctl.h new file mode 100644 index 0000000000..176e327a45 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211ioctl.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Declares constants and types for the p80211 ioctls + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * While this file is called 'ioctl' is purpose goes a little beyond + * that. This file defines the types and contants used to implement + * the p80211 request/confirm/indicate interfaces on Linux. The + * request/confirm interface is, in fact, normally implemented as an + * ioctl. The indicate interface on the other hand, is implemented + * using the Linux 'netlink' interface. + * + * The reason I say that request/confirm is 'normally' implemented + * via ioctl is that we're reserving the right to be able to send + * request commands via the netlink interface. This will be necessary + * if we ever need to send request messages when there aren't any + * wlan network devices present (i.e. sending a message that only p80211 + * cares about. + * -------------------------------------------------------------------- + */ + +#ifndef _P80211IOCTL_H +#define _P80211IOCTL_H + +/* p80211 ioctl "request" codes. See argument 2 of ioctl(2). */ + +#define P80211_IFTEST (SIOCDEVPRIVATE + 0) +#define P80211_IFREQ (SIOCDEVPRIVATE + 1) + +/*----------------------------------------------------------------*/ +/* Magic number, a quick test to see we're getting the desired struct */ + +#define P80211_IOCTL_MAGIC (0x4a2d464dUL) + +/*----------------------------------------------------------------*/ +/* A ptr to the following structure type is passed as the third */ +/* argument to the ioctl system call when issuing a request to */ +/* the p80211 module. */ + +struct p80211ioctl_req { + char name[WLAN_DEVNAMELEN_MAX]; + char __user *data; + u32 magic; + u16 len; + u32 result; +} __packed; + +#endif /* _P80211IOCTL_H */ diff --git a/drivers/staging/wlan-ng/p80211metadef.h b/drivers/staging/wlan-ng/p80211metadef.h new file mode 100644 index 0000000000..1cbb4b67a9 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211metadef.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* -------------------------------------------------------------------- + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +#ifndef _P80211MKMETADEF_H +#define _P80211MKMETADEF_H + +#define DIDMSG_DOT11REQ_MIBGET \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(1)) +#define DIDMSG_DOT11REQ_MIBGET_MIBATTRIBUTE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(1) | 0x00000000) +#define DIDMSG_DOT11REQ_MIBGET_RESULTCODE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(2) | 0x00000000) +#define DIDMSG_DOT11REQ_MIBSET \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(2)) +#define DIDMSG_DOT11REQ_MIBSET_MIBATTRIBUTE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(2) | \ + P80211DID_MKITEM(1) | 0x00000000) +#define DIDMSG_DOT11REQ_MIBSET_RESULTCODE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(2) | \ + P80211DID_MKITEM(2) | 0x00000000) +#define DIDMSG_DOT11REQ_SCAN \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(4)) +#define DIDMSG_DOT11REQ_SCAN_RESULTS \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(5)) +#define DIDMSG_DOT11REQ_START \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(13)) +#define DIDMSG_DOT11IND_AUTHENTICATE \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1)) +#define DIDMSG_DOT11IND_ASSOCIATE \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(3)) +#define DIDMSG_LNXREQ_IFSTATE \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(1)) +#define DIDMSG_LNXREQ_WLANSNIFF \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(2)) +#define DIDMSG_LNXREQ_HOSTWEP \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(3)) +#define DIDMSG_LNXREQ_COMMSQUALITY \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(4)) +#define DIDMSG_LNXREQ_AUTOJOIN \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(5)) +#define DIDMSG_P2REQ_READPDA \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(2)) +#define DIDMSG_P2REQ_READPDA_PDA \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(2) | \ + P80211DID_MKITEM(1) | 0x00000000) +#define DIDMSG_P2REQ_READPDA_RESULTCODE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(2) | \ + P80211DID_MKITEM(2) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_STATE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(11)) +#define DIDMSG_P2REQ_RAMDL_STATE_ENABLE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(11) | \ + P80211DID_MKITEM(1) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_STATE_EXEADDR \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(11) | \ + P80211DID_MKITEM(2) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_STATE_RESULTCODE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(11) | \ + P80211DID_MKITEM(3) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_WRITE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(12)) +#define DIDMSG_P2REQ_RAMDL_WRITE_ADDR \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(12) | \ + P80211DID_MKITEM(1) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_WRITE_LEN \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(12) | \ + P80211DID_MKITEM(2) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_WRITE_DATA \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(12) | \ + P80211DID_MKITEM(3) | 0x00000000) +#define DIDMSG_P2REQ_RAMDL_WRITE_RESULTCODE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(12) | \ + P80211DID_MKITEM(4) | 0x00000000) +#define DIDMSG_P2REQ_FLASHDL_STATE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(13)) +#define DIDMSG_P2REQ_FLASHDL_WRITE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(14)) +#define DIDMIB_CAT_DOT11SMT \ + P80211DID_MKSECTION(1) +#define DIDMIB_DOT11SMT_WEPDEFAULTKEYSTABLE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(4)) +#define didmib_dot11smt_wepdefaultkeystable_key(_i) \ + (DIDMIB_DOT11SMT_WEPDEFAULTKEYSTABLE | \ + P80211DID_MKITEM(_i) | 0x0c000000) +#define DIDMIB_DOT11SMT_PRIVACYTABLE \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(6)) +#define DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(6) | \ + P80211DID_MKITEM(1) | 0x18000000) +#define DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(6) | \ + P80211DID_MKITEM(2) | 0x18000000) +#define DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED \ + (P80211DID_MKSECTION(1) | \ + P80211DID_MKGROUP(6) | \ + P80211DID_MKITEM(4) | 0x18000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1)) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_MACADDRESS \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(1) | 0x18000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(2) | 0x18000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_SHORTRETRYLIMIT \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(3) | 0x10000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_LONGRETRYLIMIT \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(4) | 0x10000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(5) | 0x18000000) +#define DIDMIB_DOT11MAC_OPERATIONTABLE_MAXTRANSMITMSDULIFETIME \ + (P80211DID_MKSECTION(2) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(6) | 0x10000000) +#define DIDMIB_CAT_DOT11PHY \ + P80211DID_MKSECTION(3) +#define DIDMIB_DOT11PHY_OPERATIONTABLE \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(1)) +#define DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(3) | \ + P80211DID_MKITEM(10) | 0x18000000) +#define DIDMIB_DOT11PHY_DSSSTABLE \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(5)) +#define DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL \ + (P80211DID_MKSECTION(3) | \ + P80211DID_MKGROUP(5) | \ + P80211DID_MKITEM(1) | 0x10000000) +#define DIDMIB_CAT_LNX \ + P80211DID_MKSECTION(4) +#define DIDMIB_LNX_CONFIGTABLE \ + (P80211DID_MKSECTION(4) | \ + P80211DID_MKGROUP(1)) +#define DIDMIB_LNX_CONFIGTABLE_RSNAIE \ + (P80211DID_MKSECTION(4) | \ + P80211DID_MKGROUP(1) | \ + P80211DID_MKITEM(1) | 0x18000000) +#define DIDMIB_CAT_P2 \ + P80211DID_MKSECTION(5) +#define DIDMIB_P2_STATIC \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(2)) +#define DIDMIB_P2_STATIC_CNFPORTTYPE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(2) | \ + P80211DID_MKITEM(1) | 0x18000000) +#define DIDMIB_P2_NIC_PRISUPRANGE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(5) | \ + P80211DID_MKITEM(6) | 0x10000000) +#define DIDMIB_P2_MAC \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(6)) +#define DIDMIB_P2_MAC_CURRENTTXRATE \ + (P80211DID_MKSECTION(5) | \ + P80211DID_MKGROUP(6) | \ + P80211DID_MKITEM(12) | 0x10000000) +#endif diff --git a/drivers/staging/wlan-ng/p80211metastruct.h b/drivers/staging/wlan-ng/p80211metastruct.h new file mode 100644 index 0000000000..a52217c9b9 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211metastruct.h @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* -------------------------------------------------------------------- + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +#ifndef _P80211MKMETASTRUCT_H +#define _P80211MKMETASTRUCT_H + +struct p80211msg_dot11req_mibget { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_unk392 mibattribute; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_dot11req_mibset { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_unk392 mibattribute; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_dot11req_scan { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 bsstype; + struct p80211item_pstr6 bssid; + u8 pad_0C[1]; + struct p80211item_pstr32 ssid; + u8 pad_1D[3]; + struct p80211item_uint32 scantype; + struct p80211item_uint32 probedelay; + struct p80211item_pstr14 channellist; + u8 pad_2C[1]; + struct p80211item_uint32 minchanneltime; + struct p80211item_uint32 maxchanneltime; + struct p80211item_uint32 resultcode; + struct p80211item_uint32 numbss; + struct p80211item_uint32 append; +} __packed; + +struct p80211msg_dot11req_scan_results { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 bssindex; + struct p80211item_uint32 resultcode; + struct p80211item_uint32 signal; + struct p80211item_uint32 noise; + struct p80211item_pstr6 bssid; + u8 pad_3C[1]; + struct p80211item_pstr32 ssid; + u8 pad_4D[3]; + struct p80211item_uint32 bsstype; + struct p80211item_uint32 beaconperiod; + struct p80211item_uint32 dtimperiod; + struct p80211item_uint32 timestamp; + struct p80211item_uint32 localtime; + struct p80211item_uint32 fhdwelltime; + struct p80211item_uint32 fhhopset; + struct p80211item_uint32 fhhoppattern; + struct p80211item_uint32 fhhopindex; + struct p80211item_uint32 dschannel; + struct p80211item_uint32 cfpcount; + struct p80211item_uint32 cfpperiod; + struct p80211item_uint32 cfpmaxduration; + struct p80211item_uint32 cfpdurremaining; + struct p80211item_uint32 ibssatimwindow; + struct p80211item_uint32 cfpollable; + struct p80211item_uint32 cfpollreq; + struct p80211item_uint32 privacy; + struct p80211item_uint32 capinfo; + struct p80211item_uint32 basicrate[8]; + struct p80211item_uint32 supprate[8]; +} __packed; + +struct p80211msg_dot11req_start { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_pstr32 ssid; + u8 pad_12D[3]; + struct p80211item_uint32 bsstype; + struct p80211item_uint32 beaconperiod; + struct p80211item_uint32 dtimperiod; + struct p80211item_uint32 cfpperiod; + struct p80211item_uint32 cfpmaxduration; + struct p80211item_uint32 fhdwelltime; + struct p80211item_uint32 fhhopset; + struct p80211item_uint32 fhhoppattern; + struct p80211item_uint32 dschannel; + struct p80211item_uint32 ibssatimwindow; + struct p80211item_uint32 probedelay; + struct p80211item_uint32 cfpollable; + struct p80211item_uint32 cfpollreq; + struct p80211item_uint32 basicrate1; + struct p80211item_uint32 basicrate2; + struct p80211item_uint32 basicrate3; + struct p80211item_uint32 basicrate4; + struct p80211item_uint32 basicrate5; + struct p80211item_uint32 basicrate6; + struct p80211item_uint32 basicrate7; + struct p80211item_uint32 basicrate8; + struct p80211item_uint32 operationalrate1; + struct p80211item_uint32 operationalrate2; + struct p80211item_uint32 operationalrate3; + struct p80211item_uint32 operationalrate4; + struct p80211item_uint32 operationalrate5; + struct p80211item_uint32 operationalrate6; + struct p80211item_uint32 operationalrate7; + struct p80211item_uint32 operationalrate8; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_lnxreq_ifstate { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 ifstate; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_lnxreq_wlansniff { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 enable; + struct p80211item_uint32 channel; + struct p80211item_uint32 prismheader; + struct p80211item_uint32 wlanheader; + struct p80211item_uint32 keepwepflags; + struct p80211item_uint32 stripfcs; + struct p80211item_uint32 packet_trunc; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_lnxreq_hostwep { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 resultcode; + struct p80211item_uint32 decrypt; + struct p80211item_uint32 encrypt; +} __packed; + +struct p80211msg_lnxreq_commsquality { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 resultcode; + struct p80211item_uint32 dbm; + struct p80211item_uint32 link; + struct p80211item_uint32 level; + struct p80211item_uint32 noise; + struct p80211item_uint32 txrate; +} __packed; + +struct p80211msg_lnxreq_autojoin { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_pstr32 ssid; + u8 pad_19D[3]; + struct p80211item_uint32 authtype; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_p2req_readpda { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_unk1024 pda; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_p2req_ramdl_state { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 enable; + struct p80211item_uint32 exeaddr; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_p2req_ramdl_write { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 addr; + struct p80211item_uint32 len; + struct p80211item_unk4096 data; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_p2req_flashdl_state { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 enable; + struct p80211item_uint32 resultcode; +} __packed; + +struct p80211msg_p2req_flashdl_write { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; + struct p80211item_uint32 addr; + struct p80211item_uint32 len; + struct p80211item_unk4096 data; + struct p80211item_uint32 resultcode; +} __packed; + +#endif diff --git a/drivers/staging/wlan-ng/p80211mgmt.h b/drivers/staging/wlan-ng/p80211mgmt.h new file mode 100644 index 0000000000..7ffc202d90 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211mgmt.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Macros, types, and functions to handle 802.11 mgmt frames + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file declares the constants and types used in the interface + * between a wlan driver and the user mode utilities. + * + * Notes: + * - Constant values are always in HOST byte order. To assign + * values to multi-byte fields they _must_ be converted to + * ieee byte order. To retrieve multi-byte values from incoming + * frames, they must be converted to host order. + * + * - The len member of the frame structure does NOT!!! include + * the MAC CRC. Therefore, the len field on rx'd frames should + * have 4 subtracted from it. + * + * All functions declared here are implemented in p80211.c + * + * The types, macros, and functions defined here are primarily + * used for encoding and decoding management frames. They are + * designed to follow these patterns of use: + * + * DECODE: + * 1) a frame of length len is received into buffer b + * 2) using the hdr structure and macros, we determine the type + * 3) an appropriate mgmt frame structure, mf, is allocated and zeroed + * 4) mf.hdr = b + * mf.buf = b + * mf.len = len + * 5) call mgmt_decode( mf ) + * 6) the frame field pointers in mf are now set. Note that any + * multi-byte frame field values accessed using the frame field + * pointers are in ieee byte order and will have to be converted + * to host order. + * + * ENCODE: + * 1) Library client allocates buffer space for maximum length + * frame of the desired type + * 2) Library client allocates a mgmt frame structure, called mf, + * of the desired type + * 3) Set the following: + * mf.type = + * mf.buf = + * 4) call mgmt_encode( mf ) + * 5) all of the fixed field pointers and fixed length information element + * pointers in mf are now set to their respective locations in the + * allocated space (fortunately, all variable length information elements + * fall at the end of their respective frames). + * 5a) The length field is set to include the last of the fixed and fixed + * length fields. It may have to be updated for optional or variable + * length information elements. + * 6) Optional and variable length information elements are special cases + * and must be handled individually by the client code. + * -------------------------------------------------------------------- + */ + +#ifndef _P80211MGMT_H +#define _P80211MGMT_H + +#ifndef _P80211HDR_H +#include "p80211hdr.h" +#endif + +/*-- Information Element IDs --------------------*/ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARMS 2 +#define WLAN_EID_DS_PARMS 3 +#define WLAN_EID_CF_PARMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARMS 6 +/*-- values 7-15 reserved --*/ +#define WLAN_EID_CHALLENGE 16 +/*-- values 17-31 reserved for challenge text extension --*/ +/*-- values 32-255 reserved --*/ + +/*-- Reason Codes -------------------------------*/ +#define WLAN_MGMT_REASON_RSVD 0 +#define WLAN_MGMT_REASON_UNSPEC 1 +#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2 +#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3 +#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4 +#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6 +#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7 +#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8 +#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9 + +/*-- Status Codes -------------------------------*/ +#define WLAN_MGMT_STATUS_SUCCESS 0 +#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1 +#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13 +#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14 +#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15 +#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18 + /* p80211b additions */ +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/*-- Auth Algorithm Field ---------------------------*/ +#define WLAN_AUTH_ALG_OPENSYSTEM 0 +#define WLAN_AUTH_ALG_SHAREDKEY 1 + +/*-- Management Frame Field Offsets -------------*/ +/* Note: Not all fields are listed because of variable lengths, */ +/* see the code in p80211.c to see how we search for fields */ +/* Note: These offsets are from the start of the frame data */ + +#define WLAN_BEACON_OFF_TS 0 +#define WLAN_BEACON_OFF_BCN_int 8 +#define WLAN_BEACON_OFF_CAPINFO 10 +#define WLAN_BEACON_OFF_SSID 12 + +#define WLAN_DISASSOC_OFF_REASON 0 + +#define WLAN_ASSOCREQ_OFF_CAP_INFO 0 +#define WLAN_ASSOCREQ_OFF_LISTEN_int 2 +#define WLAN_ASSOCREQ_OFF_SSID 4 + +#define WLAN_ASSOCRESP_OFF_CAP_INFO 0 +#define WLAN_ASSOCRESP_OFF_STATUS 2 +#define WLAN_ASSOCRESP_OFF_AID 4 +#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6 + +#define WLAN_REASSOCREQ_OFF_CAP_INFO 0 +#define WLAN_REASSOCREQ_OFF_LISTEN_int 2 +#define WLAN_REASSOCREQ_OFF_CURR_AP 4 +#define WLAN_REASSOCREQ_OFF_SSID 10 + +#define WLAN_REASSOCRESP_OFF_CAP_INFO 0 +#define WLAN_REASSOCRESP_OFF_STATUS 2 +#define WLAN_REASSOCRESP_OFF_AID 4 +#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6 + +#define WLAN_PROBEREQ_OFF_SSID 0 + +#define WLAN_PROBERESP_OFF_TS 0 +#define WLAN_PROBERESP_OFF_BCN_int 8 +#define WLAN_PROBERESP_OFF_CAP_INFO 10 +#define WLAN_PROBERESP_OFF_SSID 12 + +#define WLAN_AUTHEN_OFF_AUTH_ALG 0 +#define WLAN_AUTHEN_OFF_AUTH_SEQ 2 +#define WLAN_AUTHEN_OFF_STATUS 4 +#define WLAN_AUTHEN_OFF_CHALLENGE 6 + +#define WLAN_DEAUTHEN_OFF_REASON 0 + +/*-- Capability Field ---------------------------*/ +#define WLAN_GET_MGMT_CAP_INFO_ESS(n) ((n) & BIT(0)) +#define WLAN_GET_MGMT_CAP_INFO_IBSS(n) (((n) & BIT(1)) >> 1) +#define WLAN_GET_MGMT_CAP_INFO_CFPOLLABLE(n) (((n) & BIT(2)) >> 2) +#define WLAN_GET_MGMT_CAP_INFO_CFPOLLREQ(n) (((n) & BIT(3)) >> 3) +#define WLAN_GET_MGMT_CAP_INFO_PRIVACY(n) (((n) & BIT(4)) >> 4) + /* p80211b additions */ +#define WLAN_GET_MGMT_CAP_INFO_SHORT(n) (((n) & BIT(5)) >> 5) +#define WLAN_GET_MGMT_CAP_INFO_PBCC(n) (((n) & BIT(6)) >> 6) +#define WLAN_GET_MGMT_CAP_INFO_AGILITY(n) (((n) & BIT(7)) >> 7) + +#define WLAN_SET_MGMT_CAP_INFO_ESS(n) (n) +#define WLAN_SET_MGMT_CAP_INFO_IBSS(n) ((n) << 1) +#define WLAN_SET_MGMT_CAP_INFO_CFPOLLABLE(n) ((n) << 2) +#define WLAN_SET_MGMT_CAP_INFO_CFPOLLREQ(n) ((n) << 3) +#define WLAN_SET_MGMT_CAP_INFO_PRIVACY(n) ((n) << 4) + /* p80211b additions */ +#define WLAN_SET_MGMT_CAP_INFO_SHORT(n) ((n) << 5) +#define WLAN_SET_MGMT_CAP_INFO_PBCC(n) ((n) << 6) +#define WLAN_SET_MGMT_CAP_INFO_AGILITY(n) ((n) << 7) + +#endif /* _P80211MGMT_H */ diff --git a/drivers/staging/wlan-ng/p80211msg.h b/drivers/staging/wlan-ng/p80211msg.h new file mode 100644 index 0000000000..d56bc6079e --- /dev/null +++ b/drivers/staging/wlan-ng/p80211msg.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Macros, constants, types, and funcs for req and ind messages + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +#ifndef _P80211MSG_H +#define _P80211MSG_H + +#define WLAN_DEVNAMELEN_MAX 16 + +struct p80211msg { + u32 msgcode; + u32 msglen; + u8 devname[WLAN_DEVNAMELEN_MAX]; +} __packed; + +#endif /* _P80211MSG_H */ diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c new file mode 100644 index 0000000000..8634fc89a6 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Linux Kernel net device interface + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * The functions required for a Linux network device are defined here. + * + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SIOCETHTOOL +#include +#endif + +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211conv.h" +#include "p80211mgmt.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211ioctl.h" +#include "p80211req.h" +#include "p80211metastruct.h" +#include "p80211metadef.h" + +#include "cfg80211.c" + +/* netdevice method functions */ +static int p80211knetdev_init(struct net_device *netdev); +static int p80211knetdev_open(struct net_device *netdev); +static int p80211knetdev_stop(struct net_device *netdev); +static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev); +static void p80211knetdev_set_multicast_list(struct net_device *dev); +static int p80211knetdev_siocdevprivate(struct net_device *dev, struct ifreq *ifr, + void __user *data, int cmd); +static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr); +static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue); +static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc); + +int wlan_watchdog = 5000; +module_param(wlan_watchdog, int, 0644); +MODULE_PARM_DESC(wlan_watchdog, "transmit timeout in milliseconds"); + +int wlan_wext_write = 1; +module_param(wlan_wext_write, int, 0644); +MODULE_PARM_DESC(wlan_wext_write, "enable write wireless extensions"); + +/*---------------------------------------------------------------- + * p80211knetdev_init + * + * Init method for a Linux netdevice. Called in response to + * register_netdev. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static int p80211knetdev_init(struct net_device *netdev) +{ + /* Called in response to register_netdev */ + /* This is usually the probe function, but the probe has */ + /* already been done by the MSD and the create_kdev */ + /* function. All we do here is return success */ + return 0; +} + +/*---------------------------------------------------------------- + * p80211knetdev_open + * + * Linux netdevice open method. Following a successful call here, + * the device is supposed to be ready for tx and rx. In our + * situation that may not be entirely true due to the state of the + * MAC below. + * + * Arguments: + * netdev Linux network device structure + * + * Returns: + * zero on success, non-zero otherwise + *---------------------------------------------------------------- + */ +static int p80211knetdev_open(struct net_device *netdev) +{ + int result = 0; /* success */ + struct wlandevice *wlandev = netdev->ml_priv; + + /* Check to make sure the MSD is running */ + if (wlandev->msdstate != WLAN_MSD_RUNNING) + return -ENODEV; + + /* Tell the MSD to open */ + if (wlandev->open) { + result = wlandev->open(wlandev); + if (result == 0) { + netif_start_queue(wlandev->netdev); + wlandev->state = WLAN_DEVICE_OPEN; + } + } else { + result = -EAGAIN; + } + + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_stop + * + * Linux netdevice stop (close) method. Following this call, + * no frames should go up or down through this interface. + * + * Arguments: + * netdev Linux network device structure + * + * Returns: + * zero on success, non-zero otherwise + *---------------------------------------------------------------- + */ +static int p80211knetdev_stop(struct net_device *netdev) +{ + int result = 0; + struct wlandevice *wlandev = netdev->ml_priv; + + if (wlandev->close) + result = wlandev->close(wlandev); + + netif_stop_queue(wlandev->netdev); + wlandev->state = WLAN_DEVICE_CLOSED; + + return result; +} + +/*---------------------------------------------------------------- + * p80211netdev_rx + * + * Frame receive function called by the mac specific driver. + * + * Arguments: + * wlandev WLAN network device structure + * skb skbuff containing a full 802.11 frame. + * Returns: + * nothing + * Side effects: + * + *---------------------------------------------------------------- + */ +void p80211netdev_rx(struct wlandevice *wlandev, struct sk_buff *skb) +{ + /* Enqueue for post-irq processing */ + skb_queue_tail(&wlandev->nsd_rxq, skb); + tasklet_schedule(&wlandev->rx_bh); +} + +#define CONV_TO_ETHER_SKIPPED 0x01 +#define CONV_TO_ETHER_FAILED 0x02 + +/** + * p80211_convert_to_ether - conversion from 802.11 frame to ethernet frame + * @wlandev: pointer to WLAN device + * @skb: pointer to socket buffer + * + * Returns: 0 if conversion succeeded + * CONV_TO_ETHER_FAILED if conversion failed + * CONV_TO_ETHER_SKIPPED if frame is ignored + */ +static int p80211_convert_to_ether(struct wlandevice *wlandev, + struct sk_buff *skb) +{ + struct p80211_hdr *hdr; + + hdr = (struct p80211_hdr *)skb->data; + if (p80211_rx_typedrop(wlandev, le16_to_cpu(hdr->frame_control))) + return CONV_TO_ETHER_SKIPPED; + + /* perform mcast filtering: allow my local address through but reject + * anything else that isn't multicast + */ + if (wlandev->netdev->flags & IFF_ALLMULTI) { + if (!ether_addr_equal_unaligned(wlandev->netdev->dev_addr, + hdr->address1)) { + if (!is_multicast_ether_addr(hdr->address1)) + return CONV_TO_ETHER_SKIPPED; + } + } + + if (skb_p80211_to_ether(wlandev, wlandev->ethconv, skb) == 0) { + wlandev->netdev->stats.rx_packets++; + wlandev->netdev->stats.rx_bytes += skb->len; + netif_rx(skb); + return 0; + } + + netdev_dbg(wlandev->netdev, "%s failed.\n", __func__); + return CONV_TO_ETHER_FAILED; +} + +/** + * p80211netdev_rx_bh - deferred processing of all received frames + * + * @t: pointer to the tasklet associated with this handler + */ +static void p80211netdev_rx_bh(struct tasklet_struct *t) +{ + struct wlandevice *wlandev = from_tasklet(wlandev, t, rx_bh); + struct sk_buff *skb = NULL; + struct net_device *dev = wlandev->netdev; + + /* Let's empty our queue */ + while ((skb = skb_dequeue(&wlandev->nsd_rxq))) { + if (wlandev->state == WLAN_DEVICE_OPEN) { + if (dev->type != ARPHRD_ETHER) { + /* RAW frame; we shouldn't convert it */ + /* XXX Append the Prism Header here instead. */ + + /* set up various data fields */ + skb->dev = dev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + continue; + } else { + if (!p80211_convert_to_ether(wlandev, skb)) + continue; + } + } + dev_kfree_skb(skb); + } +} + +/*---------------------------------------------------------------- + * p80211knetdev_hard_start_xmit + * + * Linux netdevice method for transmitting a frame. + * + * Arguments: + * skb Linux sk_buff containing the frame. + * netdev Linux netdevice. + * + * Side effects: + * If the lower layers report that buffers are full. netdev->tbusy + * will be set to prevent higher layers from sending more traffic. + * + * Note: If this function returns non-zero, higher layers retain + * ownership of the skb. + * + * Returns: + * zero on success, non-zero on failure. + *---------------------------------------------------------------- + */ +static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + int result = 0; + int txresult; + struct wlandevice *wlandev = netdev->ml_priv; + struct p80211_hdr p80211_hdr; + struct p80211_metawep p80211_wep; + + p80211_wep.data = NULL; + + if (!skb) + return NETDEV_TX_OK; + + if (wlandev->state != WLAN_DEVICE_OPEN) { + result = 1; + goto failed; + } + + memset(&p80211_hdr, 0, sizeof(p80211_hdr)); + memset(&p80211_wep, 0, sizeof(p80211_wep)); + + if (netif_queue_stopped(netdev)) { + netdev_dbg(netdev, "called when queue stopped.\n"); + result = 1; + goto failed; + } + + netif_stop_queue(netdev); + + /* Check to see that a valid mode is set */ + switch (wlandev->macmode) { + case WLAN_MACMODE_IBSS_STA: + case WLAN_MACMODE_ESS_STA: + case WLAN_MACMODE_ESS_AP: + break; + default: + /* Mode isn't set yet, just drop the frame + * and return success . + * TODO: we need a saner way to handle this + */ + if (be16_to_cpu(skb->protocol) != ETH_P_80211_RAW) { + netif_start_queue(wlandev->netdev); + netdev_notice(netdev, "Tx attempt prior to association, frame dropped.\n"); + netdev->stats.tx_dropped++; + result = 0; + goto failed; + } + break; + } + + /* Check for raw transmits */ + if (be16_to_cpu(skb->protocol) == ETH_P_80211_RAW) { + if (!capable(CAP_NET_ADMIN)) { + result = 1; + goto failed; + } + /* move the header over */ + memcpy(&p80211_hdr, skb->data, sizeof(p80211_hdr)); + skb_pull(skb, sizeof(p80211_hdr)); + } else { + if (skb_ether_to_p80211 + (wlandev, wlandev->ethconv, skb, &p80211_hdr, + &p80211_wep) != 0) { + /* convert failed */ + netdev_dbg(netdev, "ether_to_80211(%d) failed.\n", + wlandev->ethconv); + result = 1; + goto failed; + } + } + if (!wlandev->txframe) { + result = 1; + goto failed; + } + + netif_trans_update(netdev); + + netdev->stats.tx_packets++; + /* count only the packet payload */ + netdev->stats.tx_bytes += skb->len; + + txresult = wlandev->txframe(wlandev, skb, &p80211_hdr, &p80211_wep); + + if (txresult == 0) { + /* success and more buf */ + /* avail, re: hw_txdata */ + netif_wake_queue(wlandev->netdev); + result = NETDEV_TX_OK; + } else if (txresult == 1) { + /* success, no more avail */ + netdev_dbg(netdev, "txframe success, no more bufs\n"); + /* netdev->tbusy = 1; don't set here, irqhdlr */ + /* may have already cleared it */ + result = NETDEV_TX_OK; + } else if (txresult == 2) { + /* alloc failure, drop frame */ + netdev_dbg(netdev, "txframe returned alloc_fail\n"); + result = NETDEV_TX_BUSY; + } else { + /* buffer full or queue busy, drop frame. */ + netdev_dbg(netdev, "txframe returned full or busy\n"); + result = NETDEV_TX_BUSY; + } + +failed: + /* Free up the WEP buffer if it's not the same as the skb */ + if ((p80211_wep.data) && (p80211_wep.data != skb->data)) + kfree_sensitive(p80211_wep.data); + + /* we always free the skb here, never in a lower level. */ + if (!result) + dev_kfree_skb(skb); + + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_set_multicast_list + * + * Called from higher layers whenever there's a need to set/clear + * promiscuous mode or rewrite the multicast list. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static void p80211knetdev_set_multicast_list(struct net_device *dev) +{ + struct wlandevice *wlandev = dev->ml_priv; + + /* TODO: real multicast support as well */ + + if (wlandev->set_multicast_list) + wlandev->set_multicast_list(wlandev, dev); +} + +/*---------------------------------------------------------------- + * p80211knetdev_siocdevprivate + * + * Handle an ioctl call on one of our devices. Everything Linux + * ioctl specific is done here. Then we pass the contents of the + * ifr->data to the request message handler. + * + * Arguments: + * dev Linux kernel netdevice + * ifr Our private ioctl request structure, typed for the + * generic struct ifreq so we can use ptr to func + * w/o cast. + * + * Returns: + * zero on success, a negative errno on failure. Possible values: + * -ENETDOWN Device isn't up. + * -EBUSY cmd already in progress + * -ETIME p80211 cmd timed out (MSD may have its own timers) + * -EFAULT memory fault copying msg from user buffer + * -ENOMEM unable to allocate kernel msg buffer + * -EINVAL bad magic, it the cmd really for us? + * -EintR sleeping on cmd, awakened by signal, cmd cancelled. + * + * Call Context: + * Process thread (ioctl caller). TODO: SMP support may require + * locks. + *---------------------------------------------------------------- + */ +static int p80211knetdev_siocdevprivate(struct net_device *dev, + struct ifreq *ifr, + void __user *data, int cmd) +{ + int result = 0; + struct p80211ioctl_req *req = (struct p80211ioctl_req *)ifr; + struct wlandevice *wlandev = dev->ml_priv; + u8 *msgbuf; + + netdev_dbg(dev, "rx'd ioctl, cmd=%d, len=%d\n", cmd, req->len); + + if (in_compat_syscall()) + return -EOPNOTSUPP; + + /* Test the magic, assume ifr is good if it's there */ + if (req->magic != P80211_IOCTL_MAGIC) { + result = -EINVAL; + goto bail; + } + + if (cmd == P80211_IFTEST) { + result = 0; + goto bail; + } else if (cmd != P80211_IFREQ) { + result = -EINVAL; + goto bail; + } + + msgbuf = memdup_user(data, req->len); + if (IS_ERR(msgbuf)) { + result = PTR_ERR(msgbuf); + goto bail; + } + + result = p80211req_dorequest(wlandev, msgbuf); + + if (result == 0) { + if (copy_to_user(data, msgbuf, req->len)) + result = -EFAULT; + } + kfree(msgbuf); + +bail: + /* If allocate,copyfrom or copyto fails, return errno */ + return result; +} + +/*---------------------------------------------------------------- + * p80211knetdev_set_mac_address + * + * Handles the ioctl for changing the MACAddress of a netdevice + * + * references: linux/netdevice.h and drivers/net/net_init.c + * + * NOTE: [MSM] We only prevent address changes when the netdev is + * up. We don't control anything based on dot11 state. If the + * address is changed on a STA that's currently associated, you + * will probably lose the ability to send and receive data frames. + * Just be aware. Therefore, this should usually only be done + * prior to scan/join/auth/assoc. + * + * Arguments: + * dev netdevice struct + * addr the new MACAddress (a struct) + * + * Returns: + * zero on success, a negative errno on failure. Possible values: + * -EBUSY device is bussy (cmd not possible) + * -and errors returned by: p80211req_dorequest(..) + * + * by: Collin R. Mulliner + *---------------------------------------------------------------- + */ +static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *new_addr = addr; + struct p80211msg_dot11req_mibset dot11req; + struct p80211item_unk392 *mibattr; + struct p80211item_pstr6 *macaddr; + struct p80211item_uint32 *resultcode; + int result; + + /* If we're running, we don't allow MAC address changes */ + if (netif_running(dev)) + return -EBUSY; + + /* Set up some convenience pointers. */ + mibattr = &dot11req.mibattribute; + macaddr = (struct p80211item_pstr6 *)&mibattr->data; + resultcode = &dot11req.resultcode; + + /* Set up a dot11req_mibset */ + memset(&dot11req, 0, sizeof(dot11req)); + dot11req.msgcode = DIDMSG_DOT11REQ_MIBSET; + dot11req.msglen = sizeof(dot11req); + memcpy(dot11req.devname, + ((struct wlandevice *)dev->ml_priv)->name, + WLAN_DEVNAMELEN_MAX - 1); + + /* Set up the mibattribute argument */ + mibattr->did = DIDMSG_DOT11REQ_MIBSET_MIBATTRIBUTE; + mibattr->status = P80211ENUM_msgitem_status_data_ok; + mibattr->len = sizeof(mibattr->data); + + macaddr->did = DIDMIB_DOT11MAC_OPERATIONTABLE_MACADDRESS; + macaddr->status = P80211ENUM_msgitem_status_data_ok; + macaddr->len = sizeof(macaddr->data); + macaddr->data.len = ETH_ALEN; + memcpy(&macaddr->data.data, new_addr->sa_data, ETH_ALEN); + + /* Set up the resultcode argument */ + resultcode->did = DIDMSG_DOT11REQ_MIBSET_RESULTCODE; + resultcode->status = P80211ENUM_msgitem_status_no_value; + resultcode->len = sizeof(resultcode->data); + resultcode->data = 0; + + /* now fire the request */ + result = p80211req_dorequest(dev->ml_priv, (u8 *)&dot11req); + + /* If the request wasn't successful, report an error and don't + * change the netdev address + */ + if (result != 0 || resultcode->data != P80211ENUM_resultcode_success) { + netdev_err(dev, "Low-level driver failed dot11req_mibset(dot11MACAddress).\n"); + result = -EADDRNOTAVAIL; + } else { + /* everything's ok, change the addr in netdev */ + eth_hw_addr_set(dev, new_addr->sa_data); + } + + return result; +} + +static const struct net_device_ops p80211_netdev_ops = { + .ndo_init = p80211knetdev_init, + .ndo_open = p80211knetdev_open, + .ndo_stop = p80211knetdev_stop, + .ndo_start_xmit = p80211knetdev_hard_start_xmit, + .ndo_set_rx_mode = p80211knetdev_set_multicast_list, + .ndo_siocdevprivate = p80211knetdev_siocdevprivate, + .ndo_set_mac_address = p80211knetdev_set_mac_address, + .ndo_tx_timeout = p80211knetdev_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +/*---------------------------------------------------------------- + * wlan_setup + * + * Roughly matches the functionality of ether_setup. Here + * we set up any members of the wlandevice structure that are common + * to all devices. Additionally, we allocate a linux 'struct device' + * and perform the same setup as ether_setup. + * + * Note: It's important that the caller have setup the wlandev->name + * ptr prior to calling this function. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * physdev ptr to usb device + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Should be process thread. We'll assume it might be + * interrupt though. When we add support for statically + * compiled drivers, this function will be called in the + * context of the kernel startup code. + *---------------------------------------------------------------- + */ +int wlan_setup(struct wlandevice *wlandev, struct device *physdev) +{ + int result = 0; + struct net_device *netdev; + struct wiphy *wiphy; + struct wireless_dev *wdev; + + /* Set up the wlandev */ + wlandev->state = WLAN_DEVICE_CLOSED; + wlandev->ethconv = WLAN_ETHCONV_8021h; + wlandev->macmode = WLAN_MACMODE_NONE; + + /* Set up the rx queue */ + skb_queue_head_init(&wlandev->nsd_rxq); + tasklet_setup(&wlandev->rx_bh, p80211netdev_rx_bh); + + /* Allocate and initialize the wiphy struct */ + wiphy = wlan_create_wiphy(physdev, wlandev); + if (!wiphy) { + dev_err(physdev, "Failed to alloc wiphy.\n"); + return 1; + } + + /* Allocate and initialize the struct device */ + netdev = alloc_netdev(sizeof(struct wireless_dev), "wlan%d", + NET_NAME_UNKNOWN, ether_setup); + if (!netdev) { + dev_err(physdev, "Failed to alloc netdev.\n"); + wlan_free_wiphy(wiphy); + result = 1; + } else { + wlandev->netdev = netdev; + netdev->ml_priv = wlandev; + netdev->netdev_ops = &p80211_netdev_ops; + wdev = netdev_priv(netdev); + wdev->wiphy = wiphy; + wdev->iftype = NL80211_IFTYPE_STATION; + netdev->ieee80211_ptr = wdev; + netdev->min_mtu = 68; + /* 2312 is max 802.11 payload, 20 is overhead, + * (ether + llc + snap) and another 8 for wep. + */ + netdev->max_mtu = (2312 - 20 - 8); + + netif_stop_queue(netdev); + netif_carrier_off(netdev); + } + + return result; +} + +/*---------------------------------------------------------------- + * wlan_unsetup + * + * This function is paired with the wlan_setup routine. It should + * be called after unregister_wlandev. Basically, all it does is + * free the 'struct device' that's associated with the wlandev. + * We do it here because the 'struct device' isn't allocated + * explicitly in the driver code, it's done in wlan_setup. To + * do the free in the driver might seem like 'magic'. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Call Context: + * Should be process thread. We'll assume it might be + * interrupt though. When we add support for statically + * compiled drivers, this function will be called in the + * context of the kernel startup code. + *---------------------------------------------------------------- + */ +void wlan_unsetup(struct wlandevice *wlandev) +{ + struct wireless_dev *wdev; + + tasklet_kill(&wlandev->rx_bh); + + if (wlandev->netdev) { + wdev = netdev_priv(wlandev->netdev); + if (wdev->wiphy) + wlan_free_wiphy(wdev->wiphy); + free_netdev(wlandev->netdev); + wlandev->netdev = NULL; + } +} + +/*---------------------------------------------------------------- + * register_wlandev + * + * Roughly matches the functionality of register_netdev. This function + * is called after the driver has successfully probed and set up the + * resources for the device. It's now ready to become a named device + * in the Linux system. + * + * First we allocate a name for the device (if not already set), then + * we call the Linux function register_netdevice. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Can be either interrupt or not. + *---------------------------------------------------------------- + */ +int register_wlandev(struct wlandevice *wlandev) +{ + return register_netdev(wlandev->netdev); +} + +/*---------------------------------------------------------------- + * unregister_wlandev + * + * Roughly matches the functionality of unregister_netdev. This + * function is called to remove a named device from the system. + * + * First we tell linux that the device should no longer exist. + * Then we remove it from the list of known wlan devices. + * + * Arguments: + * wlandev ptr to the wlandev structure for the + * interface. + * Returns: + * zero on success, non-zero otherwise. + * Call Context: + * Can be either interrupt or not. + *---------------------------------------------------------------- + */ +int unregister_wlandev(struct wlandevice *wlandev) +{ + struct sk_buff *skb; + + unregister_netdev(wlandev->netdev); + + /* Now to clean out the rx queue */ + while ((skb = skb_dequeue(&wlandev->nsd_rxq))) + dev_kfree_skb(skb); + + return 0; +} + +/*---------------------------------------------------------------- + * p80211netdev_hwremoved + * + * Hardware removed notification. This function should be called + * immediately after an MSD has detected that the underlying hardware + * has been yanked out from under us. The primary things we need + * to do are: + * - Mark the wlandev + * - Prevent any further traffic from the knetdev i/f + * - Prevent any further requests from mgmt i/f + * - If there are any waitq'd mgmt requests or mgmt-frame exchanges, + * shut them down. + * - Call the MSD hwremoved function. + * + * The remainder of the cleanup will be handled by unregister(). + * Our primary goal here is to prevent as much tickling of the MSD + * as possible since the MSD is already in a 'wounded' state. + * + * TODO: As new features are added, this function should be + * updated. + * + * Arguments: + * wlandev WLAN network device structure + * Returns: + * nothing + * Side effects: + * + * Call context: + * Usually interrupt. + *---------------------------------------------------------------- + */ +void p80211netdev_hwremoved(struct wlandevice *wlandev) +{ + wlandev->hwremoved = 1; + if (wlandev->state == WLAN_DEVICE_OPEN) + netif_stop_queue(wlandev->netdev); + + netif_device_detach(wlandev->netdev); +} + +/*---------------------------------------------------------------- + * p80211_rx_typedrop + * + * Classifies the frame, increments the appropriate counter, and + * returns 0|1|2 indicating whether the driver should handle, ignore, or + * drop the frame + * + * Arguments: + * wlandev wlan device structure + * fc frame control field + * + * Returns: + * zero if the frame should be handled by the driver, + * one if the frame should be ignored + * anything else means we drop it. + * + * Side effects: + * + * Call context: + * interrupt + *---------------------------------------------------------------- + */ +static int p80211_rx_typedrop(struct wlandevice *wlandev, u16 fc) +{ + u16 ftype; + u16 fstype; + int drop = 0; + /* Classify frame, increment counter */ + ftype = WLAN_GET_FC_FTYPE(fc); + fstype = WLAN_GET_FC_FSTYPE(fc); + switch (ftype) { + case WLAN_FTYPE_MGMT: + if ((wlandev->netdev->flags & IFF_PROMISC) || + (wlandev->netdev->flags & IFF_ALLMULTI)) { + drop = 1; + break; + } + netdev_dbg(wlandev->netdev, "rx'd mgmt:\n"); + wlandev->rx.mgmt++; + switch (fstype) { + case WLAN_FSTYPE_ASSOCREQ: + wlandev->rx.assocreq++; + break; + case WLAN_FSTYPE_ASSOCRESP: + wlandev->rx.assocresp++; + break; + case WLAN_FSTYPE_REASSOCREQ: + wlandev->rx.reassocreq++; + break; + case WLAN_FSTYPE_REASSOCRESP: + wlandev->rx.reassocresp++; + break; + case WLAN_FSTYPE_PROBEREQ: + wlandev->rx.probereq++; + break; + case WLAN_FSTYPE_PROBERESP: + wlandev->rx.proberesp++; + break; + case WLAN_FSTYPE_BEACON: + wlandev->rx.beacon++; + break; + case WLAN_FSTYPE_ATIM: + wlandev->rx.atim++; + break; + case WLAN_FSTYPE_DISASSOC: + wlandev->rx.disassoc++; + break; + case WLAN_FSTYPE_AUTHEN: + wlandev->rx.authen++; + break; + case WLAN_FSTYPE_DEAUTHEN: + wlandev->rx.deauthen++; + break; + default: + wlandev->rx.mgmt_unknown++; + break; + } + drop = 2; + break; + + case WLAN_FTYPE_CTL: + if ((wlandev->netdev->flags & IFF_PROMISC) || + (wlandev->netdev->flags & IFF_ALLMULTI)) { + drop = 1; + break; + } + netdev_dbg(wlandev->netdev, "rx'd ctl:\n"); + wlandev->rx.ctl++; + switch (fstype) { + case WLAN_FSTYPE_PSPOLL: + wlandev->rx.pspoll++; + break; + case WLAN_FSTYPE_RTS: + wlandev->rx.rts++; + break; + case WLAN_FSTYPE_CTS: + wlandev->rx.cts++; + break; + case WLAN_FSTYPE_ACK: + wlandev->rx.ack++; + break; + case WLAN_FSTYPE_CFEND: + wlandev->rx.cfend++; + break; + case WLAN_FSTYPE_CFENDCFACK: + wlandev->rx.cfendcfack++; + break; + default: + wlandev->rx.ctl_unknown++; + break; + } + drop = 2; + break; + + case WLAN_FTYPE_DATA: + wlandev->rx.data++; + switch (fstype) { + case WLAN_FSTYPE_DATAONLY: + wlandev->rx.dataonly++; + break; + case WLAN_FSTYPE_DATA_CFACK: + wlandev->rx.data_cfack++; + break; + case WLAN_FSTYPE_DATA_CFPOLL: + wlandev->rx.data_cfpoll++; + break; + case WLAN_FSTYPE_DATA_CFACK_CFPOLL: + wlandev->rx.data__cfack_cfpoll++; + break; + case WLAN_FSTYPE_NULL: + netdev_dbg(wlandev->netdev, "rx'd data:null\n"); + wlandev->rx.null++; + break; + case WLAN_FSTYPE_CFACK: + netdev_dbg(wlandev->netdev, "rx'd data:cfack\n"); + wlandev->rx.cfack++; + break; + case WLAN_FSTYPE_CFPOLL: + netdev_dbg(wlandev->netdev, "rx'd data:cfpoll\n"); + wlandev->rx.cfpoll++; + break; + case WLAN_FSTYPE_CFACK_CFPOLL: + netdev_dbg(wlandev->netdev, "rx'd data:cfack_cfpoll\n"); + wlandev->rx.cfack_cfpoll++; + break; + default: + wlandev->rx.data_unknown++; + break; + } + + break; + } + return drop; +} + +static void p80211knetdev_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct wlandevice *wlandev = netdev->ml_priv; + + if (wlandev->tx_timeout) { + wlandev->tx_timeout(wlandev); + } else { + netdev_warn(netdev, "Implement tx_timeout for %s\n", + wlandev->nsdname); + netif_wake_queue(wlandev->netdev); + } +} diff --git a/drivers/staging/wlan-ng/p80211netdev.h b/drivers/staging/wlan-ng/p80211netdev.h new file mode 100644 index 0000000000..f5186380b6 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211netdev.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * WLAN net device structure and functions + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file declares the structure type that represents each wlan + * interface. + * + * -------------------------------------------------------------------- + */ + +#ifndef _LINUX_P80211NETDEV_H +#define _LINUX_P80211NETDEV_H + +#include +#include +#include + +#define WLAN_RELEASE "0.3.0-staging" + +#define WLAN_DEVICE_CLOSED 0 +#define WLAN_DEVICE_OPEN 1 + +#define WLAN_MACMODE_NONE 0 +#define WLAN_MACMODE_IBSS_STA 1 +#define WLAN_MACMODE_ESS_STA 2 +#define WLAN_MACMODE_ESS_AP 3 + +/* MSD States */ +#define WLAN_MSD_HWPRESENT_PENDING 1 +#define WLAN_MSD_HWFAIL 2 +#define WLAN_MSD_HWPRESENT 3 +#define WLAN_MSD_FWLOAD_PENDING 4 +#define WLAN_MSD_FWLOAD 5 +#define WLAN_MSD_RUNNING_PENDING 6 +#define WLAN_MSD_RUNNING 7 + +#ifndef ETH_P_ECONET +#define ETH_P_ECONET 0x0018 /* needed for 2.2.x kernels */ +#endif + +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) + +#ifndef ARPHRD_IEEE80211 +#define ARPHRD_IEEE80211 801 /* kernel 2.4.6 */ +#endif + +#ifndef ARPHRD_IEEE80211_PRISM /* kernel 2.4.18 */ +#define ARPHRD_IEEE80211_PRISM 802 +#endif + +/*--- NSD Capabilities Flags ------------------------------*/ +#define P80211_NSDCAP_HARDWAREWEP 0x01 /* hardware wep engine */ +#define P80211_NSDCAP_SHORT_PREAMBLE 0x10 /* hardware supports */ +#define P80211_NSDCAP_HWFRAGMENT 0x80 /* nsd handles frag/defrag */ +#define P80211_NSDCAP_AUTOJOIN 0x100 /* nsd does autojoin */ +#define P80211_NSDCAP_NOSCAN 0x200 /* nsd can scan */ + +/* Received frame statistics */ +struct p80211_frmrx { + u32 mgmt; + u32 assocreq; + u32 assocresp; + u32 reassocreq; + u32 reassocresp; + u32 probereq; + u32 proberesp; + u32 beacon; + u32 atim; + u32 disassoc; + u32 authen; + u32 deauthen; + u32 mgmt_unknown; + u32 ctl; + u32 pspoll; + u32 rts; + u32 cts; + u32 ack; + u32 cfend; + u32 cfendcfack; + u32 ctl_unknown; + u32 data; + u32 dataonly; + u32 data_cfack; + u32 data_cfpoll; + u32 data__cfack_cfpoll; + u32 null; + u32 cfack; + u32 cfpoll; + u32 cfack_cfpoll; + u32 data_unknown; + u32 decrypt; + u32 decrypt_err; +}; + +/* called by /proc/net/wireless */ +struct iw_statistics *p80211wext_get_wireless_stats(struct net_device *dev); + +/* WEP stuff */ +#define NUM_WEPKEYS 4 +#define MAX_KEYLEN 32 + +#define HOSTWEP_DEFAULTKEY_MASK GENMASK(1, 0) +#define HOSTWEP_SHAREDKEY BIT(3) +#define HOSTWEP_DECRYPT BIT(4) +#define HOSTWEP_ENCRYPT BIT(5) +#define HOSTWEP_PRIVACYINVOKED BIT(6) +#define HOSTWEP_EXCLUDEUNENCRYPTED BIT(7) + +extern int wlan_watchdog; +extern int wlan_wext_write; + +/* WLAN device type */ +struct wlandevice { + void *priv; /* private data for MSD */ + + /* Subsystem State */ + char name[WLAN_DEVNAMELEN_MAX]; /* Dev name, from register_wlandev() */ + char *nsdname; + + u32 state; /* Device I/F state (open/closed) */ + u32 msdstate; /* state of underlying driver */ + u32 hwremoved; /* Has the hw been yanked out? */ + + /* Hardware config */ + unsigned int irq; + unsigned int iobase; + unsigned int membase; + u32 nsdcaps; /* NSD Capabilities flags */ + + /* Config vars */ + unsigned int ethconv; + + /* device methods (init by MSD, used by p80211 */ + int (*open)(struct wlandevice *wlandev); + int (*close)(struct wlandevice *wlandev); + void (*reset)(struct wlandevice *wlandev); + int (*txframe)(struct wlandevice *wlandev, struct sk_buff *skb, + struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep); + int (*mlmerequest)(struct wlandevice *wlandev, struct p80211msg *msg); + int (*set_multicast_list)(struct wlandevice *wlandev, + struct net_device *dev); + void (*tx_timeout)(struct wlandevice *wlandev); + + /* 802.11 State */ + u8 bssid[WLAN_BSSID_LEN]; + struct p80211pstr32 ssid; + u32 macmode; + int linkstatus; + + /* WEP State */ + u8 wep_keys[NUM_WEPKEYS][MAX_KEYLEN]; + u8 wep_keylens[NUM_WEPKEYS]; + int hostwep; + + /* Request/Confirm i/f state (used by p80211) */ + unsigned long request_pending; /* flag, access atomically */ + + /* netlink socket */ + /* queue for indications waiting for cmd completion */ + /* Linux netdevice and support */ + struct net_device *netdev; /* ptr to linux netdevice */ + + /* Rx bottom half */ + struct tasklet_struct rx_bh; + + struct sk_buff_head nsd_rxq; + + /* 802.11 device statistics */ + struct p80211_frmrx rx; + + struct iw_statistics wstats; + + /* jkriegl: iwspy fields */ + u8 spy_number; + char spy_address[IW_MAX_SPY][ETH_ALEN]; + struct iw_quality spy_stat[IW_MAX_SPY]; +}; + +/* WEP stuff */ +int wep_change_key(struct wlandevice *wlandev, int keynum, u8 *key, int keylen); +int wep_decrypt(struct wlandevice *wlandev, u8 *buf, u32 len, int key_override, + u8 *iv, u8 *icv); +int wep_encrypt(struct wlandevice *wlandev, u8 *buf, u8 *dst, u32 len, + int keynum, u8 *iv, u8 *icv); + +int wlan_setup(struct wlandevice *wlandev, struct device *physdev); +void wlan_unsetup(struct wlandevice *wlandev); +int register_wlandev(struct wlandevice *wlandev); +int unregister_wlandev(struct wlandevice *wlandev); +void p80211netdev_rx(struct wlandevice *wlandev, struct sk_buff *skb); +void p80211netdev_hwremoved(struct wlandevice *wlandev); +#endif diff --git a/drivers/staging/wlan-ng/p80211req.c b/drivers/staging/wlan-ng/p80211req.c new file mode 100644 index 0000000000..6ec559ffd2 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211req.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Request/Indication/MacMgmt interface handling functions + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file contains the functions, types, and macros to support the + * MLME request interface that's implemented via the device ioctls. + * + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211mgmt.h" +#include "p80211conv.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211ioctl.h" +#include "p80211metadef.h" +#include "p80211metastruct.h" +#include "p80211req.h" + +static void p80211req_handlemsg(struct wlandevice *wlandev, + struct p80211msg *msg); +static void p80211req_mibset_mibget(struct wlandevice *wlandev, + struct p80211msg_dot11req_mibget *mib_msg, + int isget); + +static void p80211req_handle_action(struct wlandevice *wlandev, u32 *data, + int isget, u32 flag) +{ + if (isget) { + if (wlandev->hostwep & flag) + *data = P80211ENUM_truth_true; + else + *data = P80211ENUM_truth_false; + } else { + wlandev->hostwep &= ~flag; + if (*data == P80211ENUM_truth_true) + wlandev->hostwep |= flag; + } +} + +/*---------------------------------------------------------------- + * p80211req_dorequest + * + * Handles an MLME request/confirm message. + * + * Arguments: + * wlandev WLAN device struct + * msgbuf Buffer containing a request message + * + * Returns: + * 0 on success, an errno otherwise + * + * Call context: + * Potentially blocks the caller, so it's a good idea to + * not call this function from an interrupt context. + *---------------------------------------------------------------- + */ +int p80211req_dorequest(struct wlandevice *wlandev, u8 *msgbuf) +{ + struct p80211msg *msg = (struct p80211msg *)msgbuf; + + /* Check to make sure the MSD is running */ + if (!((wlandev->msdstate == WLAN_MSD_HWPRESENT && + msg->msgcode == DIDMSG_LNXREQ_IFSTATE) || + wlandev->msdstate == WLAN_MSD_RUNNING || + wlandev->msdstate == WLAN_MSD_FWLOAD)) { + return -ENODEV; + } + + /* Check Permissions */ + if (!capable(CAP_NET_ADMIN) && + (msg->msgcode != DIDMSG_DOT11REQ_MIBGET)) { + netdev_err(wlandev->netdev, + "%s: only dot11req_mibget allowed for non-root.\n", + wlandev->name); + return -EPERM; + } + + /* Check for busy status */ + if (test_and_set_bit(1, &wlandev->request_pending)) + return -EBUSY; + + /* Allow p80211 to look at msg and handle if desired. */ + /* So far, all p80211 msgs are immediate, no waitq/timer necessary */ + /* This may change. */ + p80211req_handlemsg(wlandev, msg); + + /* Pass it down to wlandev via wlandev->mlmerequest */ + if (wlandev->mlmerequest) + wlandev->mlmerequest(wlandev, msg); + + clear_bit(1, &wlandev->request_pending); + return 0; /* if result==0, msg->status still may contain an err */ +} + +/*---------------------------------------------------------------- + * p80211req_handlemsg + * + * p80211 message handler. Primarily looks for messages that + * belong to p80211 and then dispatches the appropriate response. + * TODO: we don't do anything yet. Once the linuxMIB is better + * defined we'll need a get/set handler. + * + * Arguments: + * wlandev WLAN device struct + * msg message structure + * + * Returns: + * nothing (any results are set in the status field of the msg) + * + * Call context: + * Process thread + *---------------------------------------------------------------- + */ +static void p80211req_handlemsg(struct wlandevice *wlandev, + struct p80211msg *msg) +{ + switch (msg->msgcode) { + case DIDMSG_LNXREQ_HOSTWEP: { + struct p80211msg_lnxreq_hostwep *req = + (struct p80211msg_lnxreq_hostwep *)msg; + wlandev->hostwep &= + ~(HOSTWEP_DECRYPT | HOSTWEP_ENCRYPT); + if (req->decrypt.data == P80211ENUM_truth_true) + wlandev->hostwep |= HOSTWEP_DECRYPT; + if (req->encrypt.data == P80211ENUM_truth_true) + wlandev->hostwep |= HOSTWEP_ENCRYPT; + + break; + } + case DIDMSG_DOT11REQ_MIBGET: + case DIDMSG_DOT11REQ_MIBSET: { + int isget = (msg->msgcode == DIDMSG_DOT11REQ_MIBGET); + struct p80211msg_dot11req_mibget *mib_msg = + (struct p80211msg_dot11req_mibget *)msg; + p80211req_mibset_mibget(wlandev, mib_msg, isget); + break; + } + } /* switch msg->msgcode */ +} + +static void p80211req_mibset_mibget(struct wlandevice *wlandev, + struct p80211msg_dot11req_mibget *mib_msg, + int isget) +{ + struct p80211itemd *mibitem = + (struct p80211itemd *)mib_msg->mibattribute.data; + struct p80211pstrd *pstr = (struct p80211pstrd *)mibitem->data; + u8 *key = mibitem->data + sizeof(struct p80211pstrd); + + switch (mibitem->did) { + case didmib_dot11smt_wepdefaultkeystable_key(1): + case didmib_dot11smt_wepdefaultkeystable_key(2): + case didmib_dot11smt_wepdefaultkeystable_key(3): + case didmib_dot11smt_wepdefaultkeystable_key(4): + if (!isget) + wep_change_key(wlandev, + P80211DID_ITEM(mibitem->did) - 1, + key, pstr->len); + break; + + case DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID: { + u32 *data = (u32 *)mibitem->data; + + if (isget) { + *data = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK; + } else { + wlandev->hostwep &= ~(HOSTWEP_DEFAULTKEY_MASK); + wlandev->hostwep |= (*data & HOSTWEP_DEFAULTKEY_MASK); + } + break; + } + case DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED: { + u32 *data = (u32 *)mibitem->data; + + p80211req_handle_action(wlandev, data, isget, + HOSTWEP_PRIVACYINVOKED); + break; + } + case DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED: { + u32 *data = (u32 *)mibitem->data; + + p80211req_handle_action(wlandev, data, isget, + HOSTWEP_EXCLUDEUNENCRYPTED); + break; + } + } +} diff --git a/drivers/staging/wlan-ng/p80211req.h b/drivers/staging/wlan-ng/p80211req.h new file mode 100644 index 0000000000..39213f7391 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211req.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Request handling functions + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +#ifndef _LINUX_P80211REQ_H +#define _LINUX_P80211REQ_H + +int p80211req_dorequest(struct wlandevice *wlandev, u8 *msgbuf); + +#endif diff --git a/drivers/staging/wlan-ng/p80211types.h b/drivers/staging/wlan-ng/p80211types.h new file mode 100644 index 0000000000..5e4ea5f920 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211types.h @@ -0,0 +1,292 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * + * Macros, constants, types, and funcs for p80211 data types + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file declares some of the constants and types used in various + * parts of the linux-wlan system. + * + * Notes: + * - Constant values are always in HOST byte order. + * + * All functions and statics declared here are implemented in p80211types.c + * -------------------------------------------------------------------- + */ + +#ifndef _P80211TYPES_H +#define _P80211TYPES_H + +/*----------------------------------------------------------------*/ +/* The following constants are indexes into the Mib Category List */ +/* and the Message Category List */ + +/* Mib Category List */ +#define P80211_MIB_CAT_DOT11SMT 1 +#define P80211_MIB_CAT_DOT11MAC 2 +#define P80211_MIB_CAT_DOT11PHY 3 + +#define P80211SEC_DOT11SMT P80211_MIB_CAT_DOT11SMT +#define P80211SEC_DOT11MAC P80211_MIB_CAT_DOT11MAC +#define P80211SEC_DOT11PHY P80211_MIB_CAT_DOT11PHY + +/* Message Category List */ +#define P80211_MSG_CAT_DOT11REQ 1 +#define P80211_MSG_CAT_DOT11IND 2 + +/*----------------------------------------------------------------*/ +/* p80211 enumeration constants. The value to text mappings for */ +/* these is in p80211types.c. These defines were generated */ +/* from the mappings. */ + +/* error codes for lookups */ + +#define P80211ENUM_truth_false 0 +#define P80211ENUM_truth_true 1 +#define P80211ENUM_ifstate_disable 0 +#define P80211ENUM_ifstate_fwload 1 +#define P80211ENUM_ifstate_enable 2 +#define P80211ENUM_bsstype_infrastructure 1 +#define P80211ENUM_bsstype_independent 2 +#define P80211ENUM_bsstype_any 3 +#define P80211ENUM_authalg_opensystem 1 +#define P80211ENUM_authalg_sharedkey 2 +#define P80211ENUM_scantype_active 1 +#define P80211ENUM_resultcode_success 1 +#define P80211ENUM_resultcode_invalid_parameters 2 +#define P80211ENUM_resultcode_not_supported 3 +#define P80211ENUM_resultcode_refused 6 +#define P80211ENUM_resultcode_cant_set_readonly_mib 10 +#define P80211ENUM_resultcode_implementation_failure 11 +#define P80211ENUM_resultcode_cant_get_writeonly_mib 12 +#define P80211ENUM_status_successful 0 +#define P80211ENUM_status_unspec_failure 1 +#define P80211ENUM_status_ap_full 17 +#define P80211ENUM_msgitem_status_data_ok 0 +#define P80211ENUM_msgitem_status_no_value 1 + +/*----------------------------------------------------------------*/ +/* p80211 max length constants for the different pascal strings. */ + +#define MAXLEN_PSTR6 (6) /* pascal array of 6 bytes */ +#define MAXLEN_PSTR14 (14) /* pascal array of 14 bytes */ +#define MAXLEN_PSTR32 (32) /* pascal array of 32 bytes */ +#define MAXLEN_PSTR255 (255) /* pascal array of 255 bytes */ +#define MAXLEN_MIBATTRIBUTE (392) /* maximum mibattribute */ + /* where the size of the DATA itself */ + /* is a DID-LEN-DATA triple */ + /* with a max size of 4+4+384 */ + +/*---------------------------------------------------------------- + * The following constants and macros are used to construct and + * deconstruct the Data ID codes. The coding is as follows: + * + * ...rwtnnnnnnnniiiiiiggggggssssss s - Section + * g - Group + * i - Item + * n - Index + * t - Table flag + * w - Write flag + * r - Read flag + * . - Unused + */ + +#define P80211DID_LSB_SECTION (0) +#define P80211DID_LSB_GROUP (6) +#define P80211DID_LSB_ITEM (12) +#define P80211DID_LSB_INDEX (18) +#define P80211DID_LSB_ISTABLE (26) +#define P80211DID_LSB_ACCESS (27) + +#define P80211DID_MASK_SECTION (0x0000003fUL) +#define P80211DID_MASK_GROUP (0x0000003fUL) +#define P80211DID_MASK_ITEM (0x0000003fUL) +#define P80211DID_MASK_INDEX (0x000000ffUL) +#define P80211DID_MASK_ISTABLE (0x00000001UL) +#define P80211DID_MASK_ACCESS (0x00000003UL) + +#define P80211DID_MK(a, m, l) ((((u32)(a)) & (m)) << (l)) + +#define P80211DID_MKSECTION(a) P80211DID_MK(a, \ + P80211DID_MASK_SECTION, \ + P80211DID_LSB_SECTION) +#define P80211DID_MKGROUP(a) P80211DID_MK(a, \ + P80211DID_MASK_GROUP, \ + P80211DID_LSB_GROUP) +#define P80211DID_MKITEM(a) P80211DID_MK(a, \ + P80211DID_MASK_ITEM, \ + P80211DID_LSB_ITEM) +#define P80211DID_MKINDEX(a) P80211DID_MK(a, \ + P80211DID_MASK_INDEX, \ + P80211DID_LSB_INDEX) +#define P80211DID_MKISTABLE(a) P80211DID_MK(a, \ + P80211DID_MASK_ISTABLE, \ + P80211DID_LSB_ISTABLE) + +#define P80211DID_MKID(s, g, i, n, t, a) (P80211DID_MKSECTION(s) | \ + P80211DID_MKGROUP(g) | \ + P80211DID_MKITEM(i) | \ + P80211DID_MKINDEX(n) | \ + P80211DID_MKISTABLE(t) | \ + (a)) + +#define P80211DID_GET(a, m, l) ((((u32)(a)) >> (l)) & (m)) + +#define P80211DID_SECTION(a) P80211DID_GET(a, \ + P80211DID_MASK_SECTION, \ + P80211DID_LSB_SECTION) +#define P80211DID_GROUP(a) P80211DID_GET(a, \ + P80211DID_MASK_GROUP, \ + P80211DID_LSB_GROUP) +#define P80211DID_ITEM(a) P80211DID_GET(a, \ + P80211DID_MASK_ITEM, \ + P80211DID_LSB_ITEM) +#define P80211DID_INDEX(a) P80211DID_GET(a, \ + P80211DID_MASK_INDEX, \ + P80211DID_LSB_INDEX) +#define P80211DID_ISTABLE(a) P80211DID_GET(a, \ + P80211DID_MASK_ISTABLE, \ + P80211DID_LSB_ISTABLE) +#define P80211DID_ACCESS(a) P80211DID_GET(a, \ + P80211DID_MASK_ACCESS, \ + P80211DID_LSB_ACCESS) + +/*----------------------------------------------------------------*/ +/* The following structure types are used to store data items in */ +/* messages. */ + +/* Template pascal string */ +struct p80211pstr { + u8 len; +} __packed; + +struct p80211pstrd { + u8 len; + u8 data[]; +} __packed; + +/* Maximum pascal string */ +struct p80211pstr255 { + u8 len; + u8 data[MAXLEN_PSTR255]; +} __packed; + +/* pascal string for macaddress and bssid */ +struct p80211pstr6 { + u8 len; + u8 data[MAXLEN_PSTR6]; +} __packed; + +/* pascal string for channel list */ +struct p80211pstr14 { + u8 len; + u8 data[MAXLEN_PSTR14]; +} __packed; + +/* pascal string for ssid */ +struct p80211pstr32 { + u8 len; + u8 data[MAXLEN_PSTR32]; +} __packed; + +/* prototype template */ +struct p80211item { + u32 did; + u16 status; + u16 len; +} __packed; + +/* prototype template w/ data item */ +struct p80211itemd { + u32 did; + u16 status; + u16 len; + u8 data[]; +} __packed; + +/* message data item for int, BOUNDEDINT, ENUMINT */ +struct p80211item_uint32 { + u32 did; + u16 status; + u16 len; + u32 data; +} __packed; + +/* message data item for OCTETSTR, DISPLAYSTR */ +struct p80211item_pstr6 { + u32 did; + u16 status; + u16 len; + struct p80211pstr6 data; +} __packed; + +/* message data item for OCTETSTR, DISPLAYSTR */ +struct p80211item_pstr14 { + u32 did; + u16 status; + u16 len; + struct p80211pstr14 data; +} __packed; + +/* message data item for OCTETSTR, DISPLAYSTR */ +struct p80211item_pstr32 { + u32 did; + u16 status; + u16 len; + struct p80211pstr32 data; +} __packed; + +/* message data item for OCTETSTR, DISPLAYSTR */ +struct p80211item_pstr255 { + u32 did; + u16 status; + u16 len; + struct p80211pstr255 data; +} __packed; + +/* message data item for UNK 392, namely mib items */ +struct p80211item_unk392 { + u32 did; + u16 status; + u16 len; + u8 data[MAXLEN_MIBATTRIBUTE]; +} __packed; + +/* message data item for UNK 1025, namely p2 pdas */ +struct p80211item_unk1024 { + u32 did; + u16 status; + u16 len; + u8 data[1024]; +} __packed; + +/* message data item for UNK 4096, namely p2 download chunks */ +struct p80211item_unk4096 { + u32 did; + u16 status; + u16 len; + u8 data[4096]; +} __packed; + +#endif /* _P80211TYPES_H */ diff --git a/drivers/staging/wlan-ng/p80211wep.c b/drivers/staging/wlan-ng/p80211wep.c new file mode 100644 index 0000000000..e7b26b0571 --- /dev/null +++ b/drivers/staging/wlan-ng/p80211wep.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * WEP encode/decode for P80211. + * + * Copyright (C) 2002 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +/*================================================================*/ +/* System Includes */ + +#include +#include +#include +#include +#include +#include "p80211hdr.h" +#include "p80211types.h" +#include "p80211msg.h" +#include "p80211conv.h" +#include "p80211netdev.h" + +#define WEP_KEY(x) (((x) & 0xC0) >> 6) + +/* keylen in bytes! */ + +int wep_change_key(struct wlandevice *wlandev, int keynum, u8 *key, int keylen) +{ + if (keylen < 0) + return -1; + if (keylen >= MAX_KEYLEN) + return -1; + if (!key) + return -1; + if (keynum < 0) + return -1; + if (keynum >= NUM_WEPKEYS) + return -1; + + wlandev->wep_keylens[keynum] = keylen; + memcpy(wlandev->wep_keys[keynum], key, keylen); + + return 0; +} + +/* + * 4-byte IV at start of buffer, 4-byte ICV at end of buffer. + * if successful, buf start is payload begin, length -= 8; + */ +int wep_decrypt(struct wlandevice *wlandev, u8 *buf, u32 len, int key_override, + u8 *iv, u8 *icv) +{ + u32 i, j, k, crc, keylen; + u8 s[256], key[64], c_crc[4]; + u8 keyidx; + + /* Needs to be at least 8 bytes of payload */ + if (len <= 0) + return -1; + + /* initialize the first bytes of the key from the IV */ + key[0] = iv[0]; + key[1] = iv[1]; + key[2] = iv[2]; + keyidx = WEP_KEY(iv[3]); + + if (key_override >= 0) + keyidx = key_override; + + if (keyidx >= NUM_WEPKEYS) + return -2; + + keylen = wlandev->wep_keylens[keyidx]; + + if (keylen == 0) + return -3; + + /* copy the rest of the key over from the designated key */ + memcpy(key + 3, wlandev->wep_keys[keyidx], keylen); + + keylen += 3; /* add in IV bytes */ + + /* set up the RC4 state */ + for (i = 0; i < 256; i++) + s[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + s[i] + key[i % keylen]) & 0xff; + swap(i, j); + } + + /* Apply the RC4 to the data, update the CRC32 */ + i = 0; + j = 0; + for (k = 0; k < len; k++) { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + swap(i, j); + buf[k] ^= s[(s[i] + s[j]) & 0xff]; + } + crc = ~crc32_le(~0, buf, len); + + /* now let's check the crc */ + c_crc[0] = crc; + c_crc[1] = crc >> 8; + c_crc[2] = crc >> 16; + c_crc[3] = crc >> 24; + + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + swap(i, j); + if ((c_crc[k] ^ s[(s[i] + s[j]) & 0xff]) != icv[k]) + return -(4 | (k << 4)); /* ICV mismatch */ + } + + return 0; +} + +/* encrypts in-place. */ +int wep_encrypt(struct wlandevice *wlandev, u8 *buf, + u8 *dst, u32 len, int keynum, u8 *iv, u8 *icv) +{ + u32 i, j, k, crc, keylen; + u8 s[256], key[64]; + + /* no point in WEPping an empty frame */ + if (len <= 0) + return -1; + + /* we need to have a real key.. */ + if (keynum >= NUM_WEPKEYS) + return -2; + keylen = wlandev->wep_keylens[keynum]; + if (keylen <= 0) + return -3; + + /* use a random IV. And skip known weak ones. */ + get_random_bytes(iv, 3); + while ((iv[1] == 0xff) && (iv[0] >= 3) && (iv[0] < keylen)) + get_random_bytes(iv, 3); + + iv[3] = (keynum & 0x03) << 6; + + key[0] = iv[0]; + key[1] = iv[1]; + key[2] = iv[2]; + + /* copy the rest of the key over from the designated key */ + memcpy(key + 3, wlandev->wep_keys[keynum], keylen); + + keylen += 3; /* add in IV bytes */ + + /* set up the RC4 state */ + for (i = 0; i < 256; i++) + s[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + s[i] + key[i % keylen]) & 0xff; + swap(i, j); + } + + /* Update CRC32 then apply RC4 to the data */ + i = 0; + j = 0; + for (k = 0; k < len; k++) { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + swap(i, j); + dst[k] = buf[k] ^ s[(s[i] + s[j]) & 0xff]; + } + crc = ~crc32_le(~0, buf, len); + + /* now let's encrypt the crc */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + swap(i, j); + icv[k] ^= s[(s[i] + s[j]) & 0xff]; + } + + return 0; +} diff --git a/drivers/staging/wlan-ng/prism2fw.c b/drivers/staging/wlan-ng/prism2fw.c new file mode 100644 index 0000000000..5d03b2b9aa --- /dev/null +++ b/drivers/staging/wlan-ng/prism2fw.c @@ -0,0 +1,1213 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* from src/prism2/download/prism2dl.c + * + * utility for downloading prism2 images moved into kernelspace + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + */ + +/*================================================================*/ +/* System Includes */ +#include +#include + +/*================================================================*/ +/* Local Constants */ + +#define PRISM2_USB_FWFILE "prism2_ru.fw" +MODULE_FIRMWARE(PRISM2_USB_FWFILE); + +#define S3DATA_MAX 5000 +#define S3PLUG_MAX 200 +#define S3CRC_MAX 200 +#define S3INFO_MAX 50 + +#define S3ADDR_PLUG (0xff000000UL) +#define S3ADDR_CRC (0xff100000UL) +#define S3ADDR_INFO (0xff200000UL) +#define S3ADDR_START (0xff400000UL) + +#define CHUNKS_MAX 100 + +#define WRITESIZE_MAX 4096 + +/*================================================================*/ +/* Local Types */ + +struct s3datarec { + u32 len; + u32 addr; + u8 checksum; + u8 *data; +}; + +struct s3plugrec { + u32 itemcode; + u32 addr; + u32 len; +}; + +struct s3crcrec { + u32 addr; + u32 len; + unsigned int dowrite; +}; + +struct s3inforec { + u16 len; + u16 type; + union { + struct hfa384x_compident version; + struct hfa384x_caplevel compat; + u16 buildseq; + struct hfa384x_compident platform; + } info; +}; + +struct pda { + u8 buf[HFA384x_PDA_LEN_MAX]; + struct hfa384x_pdrec *rec[HFA384x_PDA_RECS_MAX]; + unsigned int nrec; +}; + +struct imgchunk { + u32 addr; /* start address */ + u32 len; /* in bytes */ + u16 crc; /* CRC value (if it falls at a chunk boundary) */ + u8 *data; +}; + +/*================================================================*/ +/* Local Static Definitions */ + +/*----------------------------------------------------------------*/ +/* s-record image processing */ + +/* Data records */ +static unsigned int ns3data; +static struct s3datarec *s3data; + +/* Plug records */ +static unsigned int ns3plug; +static struct s3plugrec s3plug[S3PLUG_MAX]; + +/* CRC records */ +static unsigned int ns3crc; +static struct s3crcrec s3crc[S3CRC_MAX]; + +/* Info records */ +static unsigned int ns3info; +static struct s3inforec s3info[S3INFO_MAX]; + +/* S7 record (there _better_ be only one) */ +static u32 startaddr; + +/* Load image chunks */ +static unsigned int nfchunks; +static struct imgchunk fchunk[CHUNKS_MAX]; + +/* Note that for the following pdrec_t arrays, the len and code */ +/* fields are stored in HOST byte order. The mkpdrlist() function */ +/* does the conversion. */ +/*----------------------------------------------------------------*/ +/* PDA, built from [card|newfile]+[addfile1+addfile2...] */ + +static struct pda pda; +static struct hfa384x_compident nicid; +static struct hfa384x_caplevel rfid; +static struct hfa384x_caplevel macid; +static struct hfa384x_caplevel priid; + +/*================================================================*/ +/* Local Function Declarations */ + +static int prism2_fwapply(const struct ihex_binrec *rfptr, + struct wlandevice *wlandev); + +static int read_fwfile(const struct ihex_binrec *rfptr); + +static int mkimage(struct imgchunk *clist, unsigned int *ccnt); + +static int read_cardpda(struct pda *pda, struct wlandevice *wlandev); + +static int mkpdrlist(struct pda *pda); + +static int plugimage(struct imgchunk *fchunk, unsigned int nfchunks, + struct s3plugrec *s3plug, unsigned int ns3plug, + struct pda *pda); + +static int crcimage(struct imgchunk *fchunk, unsigned int nfchunks, + struct s3crcrec *s3crc, unsigned int ns3crc); + +static int writeimage(struct wlandevice *wlandev, struct imgchunk *fchunk, + unsigned int nfchunks); + +static void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks); + +static void free_srecs(void); + +static int validate_identity(void); + +/*================================================================*/ +/* Function Definitions */ + +/*---------------------------------------------------------------- + * prism2_fwtry + * + * Try and get firmware into memory + * + * Arguments: + * udev usb device structure + * wlandev wlan device structure + * + * Returns: + * 0 - success + * ~0 - failure + *---------------------------------------------------------------- + */ +static int prism2_fwtry(struct usb_device *udev, struct wlandevice *wlandev) +{ + const struct firmware *fw_entry = NULL; + + netdev_info(wlandev->netdev, "prism2_usb: Checking for firmware %s\n", + PRISM2_USB_FWFILE); + if (request_ihex_firmware(&fw_entry, + PRISM2_USB_FWFILE, &udev->dev) != 0) { + netdev_info(wlandev->netdev, + "prism2_usb: Firmware not available, but not essential\n"); + netdev_info(wlandev->netdev, + "prism2_usb: can continue to use card anyway.\n"); + return 1; + } + + netdev_info(wlandev->netdev, + "prism2_usb: %s will be processed, size %zu\n", + PRISM2_USB_FWFILE, fw_entry->size); + prism2_fwapply((const struct ihex_binrec *)fw_entry->data, wlandev); + + release_firmware(fw_entry); + return 0; +} + +/*---------------------------------------------------------------- + * prism2_fwapply + * + * Apply the firmware loaded into memory + * + * Arguments: + * rfptr firmware image in kernel memory + * wlandev device + * + * Returns: + * 0 - success + * ~0 - failure + *---------------------------------------------------------------- + */ +static int prism2_fwapply(const struct ihex_binrec *rfptr, + struct wlandevice *wlandev) +{ + signed int result = 0; + struct p80211msg_dot11req_mibget getmsg; + struct p80211itemd *item; + u32 *data; + + /* Initialize the data structures */ + ns3data = 0; + s3data = kcalloc(S3DATA_MAX, sizeof(*s3data), GFP_KERNEL); + if (!s3data) { + result = -ENOMEM; + goto out; + } + + ns3plug = 0; + memset(s3plug, 0, sizeof(s3plug)); + ns3crc = 0; + memset(s3crc, 0, sizeof(s3crc)); + ns3info = 0; + memset(s3info, 0, sizeof(s3info)); + startaddr = 0; + + nfchunks = 0; + memset(fchunk, 0, sizeof(fchunk)); + memset(&nicid, 0, sizeof(nicid)); + memset(&rfid, 0, sizeof(rfid)); + memset(&macid, 0, sizeof(macid)); + memset(&priid, 0, sizeof(priid)); + + /* clear the pda and add an initial END record */ + memset(&pda, 0, sizeof(pda)); + pda.rec[0] = (struct hfa384x_pdrec *)pda.buf; + pda.rec[0]->len = cpu_to_le16(2); /* len in words */ + pda.rec[0]->code = cpu_to_le16(HFA384x_PDR_END_OF_PDA); + pda.nrec = 1; + + /*-----------------------------------------------------*/ + /* Put card into fwload state */ + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload); + + /* Build the PDA we're going to use. */ + if (read_cardpda(&pda, wlandev)) { + netdev_err(wlandev->netdev, "load_cardpda failed, exiting.\n"); + result = 1; + goto out; + } + + /* read the card's PRI-SUP */ + memset(&getmsg, 0, sizeof(getmsg)); + getmsg.msgcode = DIDMSG_DOT11REQ_MIBGET; + getmsg.msglen = sizeof(getmsg); + strscpy(getmsg.devname, wlandev->name, sizeof(getmsg.devname)); + + getmsg.mibattribute.did = DIDMSG_DOT11REQ_MIBGET_MIBATTRIBUTE; + getmsg.mibattribute.status = P80211ENUM_msgitem_status_data_ok; + getmsg.resultcode.did = DIDMSG_DOT11REQ_MIBGET_RESULTCODE; + getmsg.resultcode.status = P80211ENUM_msgitem_status_no_value; + + item = (struct p80211itemd *)getmsg.mibattribute.data; + item->did = DIDMIB_P2_NIC_PRISUPRANGE; + item->status = P80211ENUM_msgitem_status_no_value; + + data = (u32 *)item->data; + + /* DIDmsg_dot11req_mibget */ + prism2mgmt_mibset_mibget(wlandev, &getmsg); + if (getmsg.resultcode.data != P80211ENUM_resultcode_success) + netdev_err(wlandev->netdev, "Couldn't fetch PRI-SUP info\n"); + + /* Already in host order */ + priid.role = *data++; + priid.id = *data++; + priid.variant = *data++; + priid.bottom = *data++; + priid.top = *data++; + + /* Read the S3 file */ + result = read_fwfile(rfptr); + if (result) { + netdev_err(wlandev->netdev, + "Failed to read the data exiting.\n"); + goto out; + } + + result = validate_identity(); + if (result) { + netdev_err(wlandev->netdev, "Incompatible firmware image.\n"); + goto out; + } + + if (startaddr == 0x00000000) { + netdev_err(wlandev->netdev, + "Can't RAM download a Flash image!\n"); + result = 1; + goto out; + } + + /* Make the image chunks */ + result = mkimage(fchunk, &nfchunks); + if (result) { + netdev_err(wlandev->netdev, "Failed to make image chunk.\n"); + goto free_chunks; + } + + /* Do any plugging */ + result = plugimage(fchunk, nfchunks, s3plug, ns3plug, &pda); + if (result) { + netdev_err(wlandev->netdev, "Failed to plug data.\n"); + goto free_chunks; + } + + /* Insert any CRCs */ + result = crcimage(fchunk, nfchunks, s3crc, ns3crc); + if (result) { + netdev_err(wlandev->netdev, "Failed to insert all CRCs\n"); + goto free_chunks; + } + + /* Write the image */ + result = writeimage(wlandev, fchunk, nfchunks); + if (result) { + netdev_err(wlandev->netdev, "Failed to ramwrite image data.\n"); + goto free_chunks; + } + + netdev_info(wlandev->netdev, "prism2_usb: firmware loading finished.\n"); + +free_chunks: + /* clear any allocated memory */ + free_chunks(fchunk, &nfchunks); + free_srecs(); + +out: + return result; +} + +/*---------------------------------------------------------------- + * crcimage + * + * Adds a CRC16 in the two bytes prior to each block identified by + * an S3 CRC record. Currently, we don't actually do a CRC we just + * insert the value 0xC0DE in hfa384x order. + * + * Arguments: + * fchunk Array of image chunks + * nfchunks Number of image chunks + * s3crc Array of crc records + * ns3crc Number of crc records + * + * Returns: + * 0 success + * ~0 failure + *---------------------------------------------------------------- + */ +static int crcimage(struct imgchunk *fchunk, unsigned int nfchunks, + struct s3crcrec *s3crc, unsigned int ns3crc) +{ + int result = 0; + int i; + int c; + u32 crcstart; + u32 cstart = 0; + u32 cend; + u8 *dest; + u32 chunkoff; + + for (i = 0; i < ns3crc; i++) { + if (!s3crc[i].dowrite) + continue; + crcstart = s3crc[i].addr; + /* Find chunk */ + for (c = 0; c < nfchunks; c++) { + cstart = fchunk[c].addr; + cend = fchunk[c].addr + fchunk[c].len; + /* the line below does an address & len match search */ + /* unfortunately, I've found that the len fields of */ + /* some crc records don't match with the length of */ + /* the actual data, so we're not checking right now */ + /* if (crcstart-2 >= cstart && crcend <= cend) break; */ + + /* note the -2 below, it's to make sure the chunk has */ + /* space for the CRC value */ + if (crcstart - 2 >= cstart && crcstart < cend) + break; + } + if (c >= nfchunks) { + pr_err("Failed to find chunk for crcrec[%d], addr=0x%06x len=%d , aborting crc.\n", + i, s3crc[i].addr, s3crc[i].len); + return 1; + } + + /* Insert crc */ + pr_debug("Adding crc @ 0x%06x\n", s3crc[i].addr - 2); + chunkoff = crcstart - cstart - 2; + dest = fchunk[c].data + chunkoff; + *dest = 0xde; + *(dest + 1) = 0xc0; + } + return result; +} + +/*---------------------------------------------------------------- + * free_chunks + * + * Clears the chunklist data structures in preparation for a new file. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks) +{ + int i; + + for (i = 0; i < *nfchunks; i++) + kfree(fchunk[i].data); + + *nfchunks = 0; + memset(fchunk, 0, sizeof(*fchunk)); +} + +/*---------------------------------------------------------------- + * free_srecs + * + * Clears the srec data structures in preparation for a new file. + * + * Arguments: + * none + * + * Returns: + * nothing + *---------------------------------------------------------------- + */ +static void free_srecs(void) +{ + ns3data = 0; + kfree(s3data); + ns3plug = 0; + memset(s3plug, 0, sizeof(s3plug)); + ns3crc = 0; + memset(s3crc, 0, sizeof(s3crc)); + ns3info = 0; + memset(s3info, 0, sizeof(s3info)); + startaddr = 0; +} + +/*---------------------------------------------------------------- + * mkimage + * + * Scans the currently loaded set of S records for data residing + * in contiguous memory regions. Each contiguous region is then + * made into a 'chunk'. This function assumes that we're building + * a new chunk list. Assumes the s3data items are in sorted order. + * + * Arguments: none + * + * Returns: + * 0 - success + * ~0 - failure (probably an errno) + *---------------------------------------------------------------- + */ +static int mkimage(struct imgchunk *clist, unsigned int *ccnt) +{ + int result = 0; + int i; + int j; + int currchunk = 0; + u32 nextaddr = 0; + u32 s3start; + u32 s3end; + u32 cstart = 0; + u32 cend; + u32 coffset; + + /* There may already be data in the chunklist */ + *ccnt = 0; + + /* Establish the location and size of each chunk */ + for (i = 0; i < ns3data; i++) { + if (s3data[i].addr == nextaddr) { + /* existing chunk, grow it */ + clist[currchunk].len += s3data[i].len; + nextaddr += s3data[i].len; + } else { + /* New chunk */ + (*ccnt)++; + currchunk = *ccnt - 1; + clist[currchunk].addr = s3data[i].addr; + clist[currchunk].len = s3data[i].len; + nextaddr = s3data[i].addr + s3data[i].len; + /* Expand the chunk if there is a CRC record at */ + /* their beginning bound */ + for (j = 0; j < ns3crc; j++) { + if (s3crc[j].dowrite && + s3crc[j].addr == clist[currchunk].addr) { + clist[currchunk].addr -= 2; + clist[currchunk].len += 2; + } + } + } + } + + /* We're currently assuming there aren't any overlapping chunks */ + /* if this proves false, we'll need to add code to coalesce. */ + + /* Allocate buffer space for chunks */ + for (i = 0; i < *ccnt; i++) { + clist[i].data = kzalloc(clist[i].len, GFP_KERNEL); + if (!clist[i].data) + return 1; + + pr_debug("chunk[%d]: addr=0x%06x len=%d\n", + i, clist[i].addr, clist[i].len); + } + + /* Copy srec data to chunks */ + for (i = 0; i < ns3data; i++) { + s3start = s3data[i].addr; + s3end = s3start + s3data[i].len - 1; + for (j = 0; j < *ccnt; j++) { + cstart = clist[j].addr; + cend = cstart + clist[j].len - 1; + if (s3start >= cstart && s3end <= cend) + break; + } + if (((unsigned int)j) >= (*ccnt)) { + pr_err("s3rec(a=0x%06x,l=%d), no chunk match, exiting.\n", + s3start, s3data[i].len); + return 1; + } + coffset = s3start - cstart; + memcpy(clist[j].data + coffset, s3data[i].data, s3data[i].len); + } + + return result; +} + +/*---------------------------------------------------------------- + * mkpdrlist + * + * Reads a raw PDA and builds an array of pdrec_t structures. + * + * Arguments: + * pda buffer containing raw PDA bytes + * pdrec ptr to an array of pdrec_t's. Will be filled on exit. + * nrec ptr to a variable that will contain the count of PDRs + * + * Returns: + * 0 - success + * ~0 - failure (probably an errno) + *---------------------------------------------------------------- + */ +static int mkpdrlist(struct pda *pda) +{ + __le16 *pda16 = (__le16 *)pda->buf; + int curroff; /* in 'words' */ + + pda->nrec = 0; + curroff = 0; + while (curroff < (HFA384x_PDA_LEN_MAX / 2 - 1) && + le16_to_cpu(pda16[curroff + 1]) != HFA384x_PDR_END_OF_PDA) { + pda->rec[pda->nrec] = (struct hfa384x_pdrec *)&pda16[curroff]; + + if (le16_to_cpu(pda->rec[pda->nrec]->code) == + HFA384x_PDR_NICID) { + memcpy(&nicid, &pda->rec[pda->nrec]->data.nicid, + sizeof(nicid)); + le16_to_cpus(&nicid.id); + le16_to_cpus(&nicid.variant); + le16_to_cpus(&nicid.major); + le16_to_cpus(&nicid.minor); + } + if (le16_to_cpu(pda->rec[pda->nrec]->code) == + HFA384x_PDR_MFISUPRANGE) { + memcpy(&rfid, &pda->rec[pda->nrec]->data.mfisuprange, + sizeof(rfid)); + le16_to_cpus(&rfid.id); + le16_to_cpus(&rfid.variant); + le16_to_cpus(&rfid.bottom); + le16_to_cpus(&rfid.top); + } + if (le16_to_cpu(pda->rec[pda->nrec]->code) == + HFA384x_PDR_CFISUPRANGE) { + memcpy(&macid, &pda->rec[pda->nrec]->data.cfisuprange, + sizeof(macid)); + le16_to_cpus(&macid.id); + le16_to_cpus(&macid.variant); + le16_to_cpus(&macid.bottom); + le16_to_cpus(&macid.top); + } + + (pda->nrec)++; + curroff += le16_to_cpu(pda16[curroff]) + 1; + } + if (curroff >= (HFA384x_PDA_LEN_MAX / 2 - 1)) { + pr_err("no end record found or invalid lengths in PDR data, exiting. %x %d\n", + curroff, pda->nrec); + return 1; + } + pda->rec[pda->nrec] = (struct hfa384x_pdrec *)&pda16[curroff]; + (pda->nrec)++; + return 0; +} + +/*---------------------------------------------------------------- + * plugimage + * + * Plugs the given image using the given plug records from the given + * PDA and filename. + * + * Arguments: + * fchunk Array of image chunks + * nfchunks Number of image chunks + * s3plug Array of plug records + * ns3plug Number of plug records + * pda Current pda data + * + * Returns: + * 0 success + * ~0 failure + *---------------------------------------------------------------- + */ +static int plugimage(struct imgchunk *fchunk, unsigned int nfchunks, + struct s3plugrec *s3plug, unsigned int ns3plug, + struct pda *pda) +{ + int result = 0; + int i; /* plug index */ + int j; /* index of PDR or -1 if fname plug */ + int c; /* chunk index */ + u32 pstart; + u32 pend; + u32 cstart = 0; + u32 cend; + u32 chunkoff; + u8 *dest; + + /* for each plug record */ + for (i = 0; i < ns3plug; i++) { + pstart = s3plug[i].addr; + pend = s3plug[i].addr + s3plug[i].len; + j = -1; + /* find the matching PDR (or filename) */ + if (s3plug[i].itemcode != 0xffffffffUL) { /* not filename */ + for (j = 0; j < pda->nrec; j++) { + if (s3plug[i].itemcode == + le16_to_cpu(pda->rec[j]->code)) + break; + } + } + if (j >= pda->nrec && j != -1) { /* if no matching PDR, fail */ + pr_warn("warning: Failed to find PDR for plugrec 0x%04x.\n", + s3plug[i].itemcode); + continue; /* and move on to the next PDR */ + + /* MSM: They swear that unless it's the MAC address, + * the serial number, or the TX calibration records, + * then there's reasonable defaults in the f/w + * image. Therefore, missing PDRs in the card + * should only be a warning, not fatal. + * TODO: add fatals for the PDRs mentioned above. + */ + } + + /* Validate plug len against PDR len */ + if (j != -1 && s3plug[i].len < le16_to_cpu(pda->rec[j]->len)) { + pr_err("error: Plug vs. PDR len mismatch for plugrec 0x%04x, abort plugging.\n", + s3plug[i].itemcode); + result = 1; + continue; + } + + /* + * Validate plug address against + * chunk data and identify chunk + */ + for (c = 0; c < nfchunks; c++) { + cstart = fchunk[c].addr; + cend = fchunk[c].addr + fchunk[c].len; + if (pstart >= cstart && pend <= cend) + break; + } + if (c >= nfchunks) { + pr_err("error: Failed to find image chunk for plugrec 0x%04x.\n", + s3plug[i].itemcode); + result = 1; + continue; + } + + /* Plug data */ + chunkoff = pstart - cstart; + dest = fchunk[c].data + chunkoff; + pr_debug("Plugging item 0x%04x @ 0x%06x, len=%d, cnum=%d coff=0x%06x\n", + s3plug[i].itemcode, pstart, s3plug[i].len, + c, chunkoff); + + if (j == -1) { /* plug the filename */ + memset(dest, 0, s3plug[i].len); + strncpy(dest, PRISM2_USB_FWFILE, s3plug[i].len - 1); + } else { /* plug a PDR */ + memcpy(dest, &pda->rec[j]->data, s3plug[i].len); + } + } + return result; +} + +/*---------------------------------------------------------------- + * read_cardpda + * + * Sends the command for the driver to read the pda from the card + * named in the device variable. Upon success, the card pda is + * stored in the "cardpda" variables. Note that the pda structure + * is considered 'well formed' after this function. That means + * that the nrecs is valid, the rec array has been set up, and there's + * a valid PDAEND record in the raw PDA data. + * + * Arguments: + * pda pda structure + * wlandev device + * + * Returns: + * 0 - success + * ~0 - failure (probably an errno) + *---------------------------------------------------------------- + */ +static int read_cardpda(struct pda *pda, struct wlandevice *wlandev) +{ + int result = 0; + struct p80211msg_p2req_readpda *msg; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* set up the msg */ + msg->msgcode = DIDMSG_P2REQ_READPDA; + msg->msglen = sizeof(msg); + strscpy(msg->devname, wlandev->name, sizeof(msg->devname)); + msg->pda.did = DIDMSG_P2REQ_READPDA_PDA; + msg->pda.len = HFA384x_PDA_LEN_MAX; + msg->pda.status = P80211ENUM_msgitem_status_no_value; + msg->resultcode.did = DIDMSG_P2REQ_READPDA_RESULTCODE; + msg->resultcode.len = sizeof(u32); + msg->resultcode.status = P80211ENUM_msgitem_status_no_value; + + if (prism2mgmt_readpda(wlandev, msg) != 0) { + /* prism2mgmt_readpda prints an errno if appropriate */ + result = -1; + } else if (msg->resultcode.data == P80211ENUM_resultcode_success) { + memcpy(pda->buf, msg->pda.data, HFA384x_PDA_LEN_MAX); + result = mkpdrlist(pda); + } else { + /* resultcode must've been something other than success */ + result = -1; + } + + kfree(msg); + return result; +} + +/*---------------------------------------------------------------- + * read_fwfile + * + * Reads the given fw file which should have been compiled from an srec + * file. Each record in the fw file will either be a plain data record, + * a start address record, or other records used for plugging. + * + * Note that data records are expected to be sorted into + * ascending address order in the fw file. + * + * Note also that the start address record, originally an S7 record in + * the srec file, is expected in the fw file to be like a data record but + * with a certain address to make it identifiable. + * + * Here's the SREC format that the fw should have come from: + * S[37]nnaaaaaaaaddd...dddcc + * + * nn - number of bytes starting with the address field + * aaaaaaaa - address in readable (or big endian) format + * dd....dd - 0-245 data bytes (two chars per byte) + * cc - checksum + * + * The S7 record's (there should be only one) address value gets + * converted to an S3 record with address of 0xff400000, with the + * start address being stored as a 4 byte data word. That address is + * the start execution address used for RAM downloads. + * + * The S3 records have a collection of subformats indicated by the + * value of aaaaaaaa: + * 0xff000000 - Plug record, data field format: + * xxxxxxxxaaaaaaaassssssss + * x - PDR code number (little endian) + * a - Address in load image to plug (little endian) + * s - Length of plug data area (little endian) + * + * 0xff100000 - CRC16 generation record, data field format: + * aaaaaaaassssssssbbbbbbbb + * a - Start address for CRC calculation (little endian) + * s - Length of data to calculate over (little endian) + * b - Boolean, true=write crc, false=don't write + * + * 0xff200000 - Info record, data field format: + * ssssttttdd..dd + * s - Size in words (little endian) + * t - Info type (little endian), see #defines and + * struct s3inforec for details about types. + * d - (s - 1) little endian words giving the contents of + * the given info type. + * + * 0xff400000 - Start address record, data field format: + * aaaaaaaa + * a - Address in load image to plug (little endian) + * + * Arguments: + * record firmware image (ihex record structure) in kernel memory + * + * Returns: + * 0 - success + * ~0 - failure (probably an errno) + *---------------------------------------------------------------- + */ +static int read_fwfile(const struct ihex_binrec *record) +{ + int i; + int rcnt = 0; + u16 *tmpinfo; + u16 *ptr16; + u32 *ptr32, len, addr; + + pr_debug("Reading fw file ...\n"); + + while (record) { + rcnt++; + + len = be16_to_cpu(record->len); + addr = be32_to_cpu(record->addr); + + /* Point into data for different word lengths */ + ptr32 = (u32 *)record->data; + ptr16 = (u16 *)record->data; + + /* parse what was an S3 srec and put it in the right array */ + switch (addr) { + case S3ADDR_START: + startaddr = *ptr32; + pr_debug(" S7 start addr, record=%d addr=0x%08x\n", + rcnt, + startaddr); + break; + case S3ADDR_PLUG: + s3plug[ns3plug].itemcode = *ptr32; + s3plug[ns3plug].addr = *(ptr32 + 1); + s3plug[ns3plug].len = *(ptr32 + 2); + + pr_debug(" S3 plugrec, record=%d itemcode=0x%08x addr=0x%08x len=%d\n", + rcnt, + s3plug[ns3plug].itemcode, + s3plug[ns3plug].addr, + s3plug[ns3plug].len); + + ns3plug++; + if (ns3plug == S3PLUG_MAX) { + pr_err("S3 plugrec limit reached - aborting\n"); + return 1; + } + break; + case S3ADDR_CRC: + s3crc[ns3crc].addr = *ptr32; + s3crc[ns3crc].len = *(ptr32 + 1); + s3crc[ns3crc].dowrite = *(ptr32 + 2); + + pr_debug(" S3 crcrec, record=%d addr=0x%08x len=%d write=0x%08x\n", + rcnt, + s3crc[ns3crc].addr, + s3crc[ns3crc].len, + s3crc[ns3crc].dowrite); + ns3crc++; + if (ns3crc == S3CRC_MAX) { + pr_err("S3 crcrec limit reached - aborting\n"); + return 1; + } + break; + case S3ADDR_INFO: + s3info[ns3info].len = *ptr16; + s3info[ns3info].type = *(ptr16 + 1); + + pr_debug(" S3 inforec, record=%d len=0x%04x type=0x%04x\n", + rcnt, + s3info[ns3info].len, + s3info[ns3info].type); + if (((s3info[ns3info].len - 1) * sizeof(u16)) > + sizeof(s3info[ns3info].info)) { + pr_err("S3 inforec length too long - aborting\n"); + return 1; + } + + tmpinfo = (u16 *)&s3info[ns3info].info.version; + pr_debug(" info="); + for (i = 0; i < s3info[ns3info].len - 1; i++) { + tmpinfo[i] = *(ptr16 + 2 + i); + pr_debug("%04x ", tmpinfo[i]); + } + pr_debug("\n"); + + ns3info++; + if (ns3info == S3INFO_MAX) { + pr_err("S3 inforec limit reached - aborting\n"); + return 1; + } + break; + default: /* Data record */ + s3data[ns3data].addr = addr; + s3data[ns3data].len = len; + s3data[ns3data].data = (uint8_t *)record->data; + ns3data++; + if (ns3data == S3DATA_MAX) { + pr_err("S3 datarec limit reached - aborting\n"); + return 1; + } + break; + } + record = ihex_next_binrec(record); + } + return 0; +} + +/*---------------------------------------------------------------- + * writeimage + * + * Takes the chunks, builds p80211 messages and sends them down + * to the driver for writing to the card. + * + * Arguments: + * wlandev device + * fchunk Array of image chunks + * nfchunks Number of image chunks + * + * Returns: + * 0 success + * ~0 failure + *---------------------------------------------------------------- + */ +static int writeimage(struct wlandevice *wlandev, struct imgchunk *fchunk, + unsigned int nfchunks) +{ + int result = 0; + struct p80211msg_p2req_ramdl_state *rstmsg; + struct p80211msg_p2req_ramdl_write *rwrmsg; + u32 resultcode; + int i; + int j; + unsigned int nwrites; + u32 curroff; + u32 currlen; + u32 currdaddr; + + rstmsg = kzalloc(sizeof(*rstmsg), GFP_KERNEL); + rwrmsg = kzalloc(sizeof(*rwrmsg), GFP_KERNEL); + if (!rstmsg || !rwrmsg) { + netdev_err(wlandev->netdev, + "%s: no memory for firmware download, aborting download\n", + __func__); + result = -ENOMEM; + goto free_result; + } + + /* Initialize the messages */ + strscpy(rstmsg->devname, wlandev->name, sizeof(rstmsg->devname)); + rstmsg->msgcode = DIDMSG_P2REQ_RAMDL_STATE; + rstmsg->msglen = sizeof(*rstmsg); + rstmsg->enable.did = DIDMSG_P2REQ_RAMDL_STATE_ENABLE; + rstmsg->exeaddr.did = DIDMSG_P2REQ_RAMDL_STATE_EXEADDR; + rstmsg->resultcode.did = DIDMSG_P2REQ_RAMDL_STATE_RESULTCODE; + rstmsg->enable.status = P80211ENUM_msgitem_status_data_ok; + rstmsg->exeaddr.status = P80211ENUM_msgitem_status_data_ok; + rstmsg->resultcode.status = P80211ENUM_msgitem_status_no_value; + rstmsg->enable.len = sizeof(u32); + rstmsg->exeaddr.len = sizeof(u32); + rstmsg->resultcode.len = sizeof(u32); + + strscpy(rwrmsg->devname, wlandev->name, sizeof(rwrmsg->devname)); + rwrmsg->msgcode = DIDMSG_P2REQ_RAMDL_WRITE; + rwrmsg->msglen = sizeof(*rwrmsg); + rwrmsg->addr.did = DIDMSG_P2REQ_RAMDL_WRITE_ADDR; + rwrmsg->len.did = DIDMSG_P2REQ_RAMDL_WRITE_LEN; + rwrmsg->data.did = DIDMSG_P2REQ_RAMDL_WRITE_DATA; + rwrmsg->resultcode.did = DIDMSG_P2REQ_RAMDL_WRITE_RESULTCODE; + rwrmsg->addr.status = P80211ENUM_msgitem_status_data_ok; + rwrmsg->len.status = P80211ENUM_msgitem_status_data_ok; + rwrmsg->data.status = P80211ENUM_msgitem_status_data_ok; + rwrmsg->resultcode.status = P80211ENUM_msgitem_status_no_value; + rwrmsg->addr.len = sizeof(u32); + rwrmsg->len.len = sizeof(u32); + rwrmsg->data.len = WRITESIZE_MAX; + rwrmsg->resultcode.len = sizeof(u32); + + /* Send xxx_state(enable) */ + pr_debug("Sending dl_state(enable) message.\n"); + rstmsg->enable.data = P80211ENUM_truth_true; + rstmsg->exeaddr.data = startaddr; + + result = prism2mgmt_ramdl_state(wlandev, rstmsg); + if (result) { + netdev_err(wlandev->netdev, + "%s state enable failed w/ result=%d, aborting download\n", + __func__, result); + goto free_result; + } + resultcode = rstmsg->resultcode.data; + if (resultcode != P80211ENUM_resultcode_success) { + netdev_err(wlandev->netdev, + "%s()->xxxdl_state msg indicates failure, w/ resultcode=%d, aborting download.\n", + __func__, resultcode); + result = 1; + goto free_result; + } + + /* Now, loop through the data chunks and send WRITESIZE_MAX data */ + for (i = 0; i < nfchunks; i++) { + nwrites = fchunk[i].len / WRITESIZE_MAX; + nwrites += (fchunk[i].len % WRITESIZE_MAX) ? 1 : 0; + curroff = 0; + for (j = 0; j < nwrites; j++) { + /* TODO Move this to a separate function */ + int lenleft = fchunk[i].len - (WRITESIZE_MAX * j); + + if (fchunk[i].len > WRITESIZE_MAX) + currlen = WRITESIZE_MAX; + else + currlen = lenleft; + curroff = j * WRITESIZE_MAX; + currdaddr = fchunk[i].addr + curroff; + /* Setup the message */ + rwrmsg->addr.data = currdaddr; + rwrmsg->len.data = currlen; + memcpy(rwrmsg->data.data, + fchunk[i].data + curroff, currlen); + + /* Send flashdl_write(pda) */ + pr_debug + ("Sending xxxdl_write message addr=%06x len=%d.\n", + currdaddr, currlen); + + result = prism2mgmt_ramdl_write(wlandev, rwrmsg); + + /* Check the results */ + if (result) { + netdev_err(wlandev->netdev, + "%s chunk write failed w/ result=%d, aborting download\n", + __func__, result); + goto free_result; + } + resultcode = rstmsg->resultcode.data; + if (resultcode != P80211ENUM_resultcode_success) { + pr_err("%s()->xxxdl_write msg indicates failure, w/ resultcode=%d, aborting download.\n", + __func__, resultcode); + result = 1; + goto free_result; + } + } + } + + /* Send xxx_state(disable) */ + pr_debug("Sending dl_state(disable) message.\n"); + rstmsg->enable.data = P80211ENUM_truth_false; + rstmsg->exeaddr.data = 0; + + result = prism2mgmt_ramdl_state(wlandev, rstmsg); + if (result) { + netdev_err(wlandev->netdev, + "%s state disable failed w/ result=%d, aborting download\n", + __func__, result); + goto free_result; + } + resultcode = rstmsg->resultcode.data; + if (resultcode != P80211ENUM_resultcode_success) { + netdev_err(wlandev->netdev, + "%s()->xxxdl_state msg indicates failure, w/ resultcode=%d, aborting download.\n", + __func__, resultcode); + result = 1; + goto free_result; + } + +free_result: + kfree(rstmsg); + kfree(rwrmsg); + return result; +} + +static int validate_identity(void) +{ + int i; + int result = 1; + int trump = 0; + + pr_debug("NIC ID: %#x v%d.%d.%d\n", + nicid.id, nicid.major, nicid.minor, nicid.variant); + pr_debug("MFI ID: %#x v%d %d->%d\n", + rfid.id, rfid.variant, rfid.bottom, rfid.top); + pr_debug("CFI ID: %#x v%d %d->%d\n", + macid.id, macid.variant, macid.bottom, macid.top); + pr_debug("PRI ID: %#x v%d %d->%d\n", + priid.id, priid.variant, priid.bottom, priid.top); + + for (i = 0; i < ns3info; i++) { + switch (s3info[i].type) { + case 1: + pr_debug("Version: ID %#x %d.%d.%d\n", + s3info[i].info.version.id, + s3info[i].info.version.major, + s3info[i].info.version.minor, + s3info[i].info.version.variant); + break; + case 2: + pr_debug("Compat: Role %#x Id %#x v%d %d->%d\n", + s3info[i].info.compat.role, + s3info[i].info.compat.id, + s3info[i].info.compat.variant, + s3info[i].info.compat.bottom, + s3info[i].info.compat.top); + + /* MAC compat range */ + if ((s3info[i].info.compat.role == 1) && + (s3info[i].info.compat.id == 2)) { + if (s3info[i].info.compat.variant != + macid.variant) { + result = 2; + } + } + + /* PRI compat range */ + if ((s3info[i].info.compat.role == 1) && + (s3info[i].info.compat.id == 3)) { + if ((s3info[i].info.compat.bottom > + priid.top) || + (s3info[i].info.compat.top < + priid.bottom)) { + result = 3; + } + } + /* SEC compat range */ + if ((s3info[i].info.compat.role == 1) && + (s3info[i].info.compat.id == 4)) { + /* FIXME: isn't something missing here? */ + } + + break; + case 3: + pr_debug("Seq: %#x\n", s3info[i].info.buildseq); + + break; + case 4: + pr_debug("Platform: ID %#x %d.%d.%d\n", + s3info[i].info.version.id, + s3info[i].info.version.major, + s3info[i].info.version.minor, + s3info[i].info.version.variant); + + if (nicid.id != s3info[i].info.version.id) + continue; + if (nicid.major != s3info[i].info.version.major) + continue; + if (nicid.minor != s3info[i].info.version.minor) + continue; + if ((nicid.variant != s3info[i].info.version.variant) && + (nicid.id != 0x8008)) + continue; + + trump = 1; + break; + case 0x8001: + pr_debug("name inforec len %d\n", s3info[i].len); + + break; + default: + pr_debug("Unknown inforec type %d\n", s3info[i].type); + } + } + /* walk through */ + + if (trump && (result != 2)) + result = 0; + return result; +} diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c new file mode 100644 index 0000000000..d573716656 --- /dev/null +++ b/drivers/staging/wlan-ng/prism2mgmt.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Management request handler functions. + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * The functions in this file handle management requests sent from + * user mode. + * + * Most of these functions have two separate blocks of code that are + * conditional on whether this is a station or an AP. This is used + * to separate out the STA and AP responses to these management primitives. + * It's a choice (good, bad, indifferent?) to have the code in the same + * place so it's clear that the same primitive is implemented in both + * cases but has different behavior. + * + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211mgmt.h" +#include "p80211conv.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211metadef.h" +#include "p80211metastruct.h" +#include "hfa384x.h" +#include "prism2mgmt.h" + +/* Converts 802.11 format rate specifications to prism2 */ +static inline u16 p80211rate_to_p2bit(u32 rate) +{ + switch (rate & ~BIT(7)) { + case 2: + return BIT(0); + case 4: + return BIT(1); + case 11: + return BIT(2); + case 22: + return BIT(3); + default: + return 0; + } +} + +/*---------------------------------------------------------------- + * prism2mgmt_scan + * + * Initiate a scan for BSSs. + * + * This function corresponds to MLME-scan.request and part of + * MLME-scan.confirm. As far as I can tell in the standard, there + * are no restrictions on when a scan.request may be issued. We have + * to handle in whatever state the driver/MAC happen to be. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + *---------------------------------------------------------------- + */ +int prism2mgmt_scan(struct wlandevice *wlandev, void *msgp) +{ + int result = 0; + struct hfa384x *hw = wlandev->priv; + struct p80211msg_dot11req_scan *msg = msgp; + u16 roamingmode, word; + int i, timeout; + int istmpenable = 0; + + struct hfa384x_host_scan_request_data scanreq; + + /* gatekeeper check */ + if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, + hw->ident_sta_fw.variant) < + HFA384x_FIRMWARE_VERSION(1, 3, 2)) { + netdev_err(wlandev->netdev, + "HostScan not supported with current firmware (<1.3.2).\n"); + result = 1; + msg->resultcode.data = P80211ENUM_resultcode_not_supported; + goto exit; + } + + memset(&scanreq, 0, sizeof(scanreq)); + + /* save current roaming mode */ + result = hfa384x_drvr_getconfig16(hw, + HFA384x_RID_CNFROAMINGMODE, + &roamingmode); + if (result) { + netdev_err(wlandev->netdev, + "getconfig(ROAMMODE) failed. result=%d\n", result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + + /* drop into mode 3 for the scan */ + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFROAMINGMODE, + HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM); + if (result) { + netdev_err(wlandev->netdev, + "setconfig(ROAMINGMODE) failed. result=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + + /* active or passive? */ + if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, + hw->ident_sta_fw.variant) > + HFA384x_FIRMWARE_VERSION(1, 5, 0)) { + if (msg->scantype.data != P80211ENUM_scantype_active) + word = msg->maxchanneltime.data; + else + word = 0; + + result = + hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPASSIVESCANCTRL, + word); + if (result) { + netdev_warn(wlandev->netdev, + "Passive scan not supported with current firmware. (<1.5.1)\n"); + } + } + + /* set up the txrate to be 2MBPS. Should be fastest basicrate... */ + word = HFA384x_RATEBIT_2; + scanreq.tx_rate = cpu_to_le16(word); + + /* set up the channel list */ + word = 0; + for (i = 0; i < msg->channellist.data.len; i++) { + u8 channel = msg->channellist.data.data[i]; + + if (channel > 14) + continue; + /* channel 1 is BIT 0 ... channel 14 is BIT 13 */ + word |= (1 << (channel - 1)); + } + scanreq.channel_list = cpu_to_le16(word); + + /* set up the ssid, if present. */ + scanreq.ssid.len = cpu_to_le16(msg->ssid.data.len); + memcpy(scanreq.ssid.data, msg->ssid.data.data, msg->ssid.data.len); + + /* Enable the MAC port if it's not already enabled */ + result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_PORTSTATUS, &word); + if (result) { + netdev_err(wlandev->netdev, + "getconfig(PORTSTATUS) failed. result=%d\n", result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + if (word == HFA384x_PORTSTATUS_DISABLED) { + __le16 wordbuf[17]; + + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFROAMINGMODE, + HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM); + if (result) { + netdev_err(wlandev->netdev, + "setconfig(ROAMINGMODE) failed. result=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + /* Construct a bogus SSID and assign it to OwnSSID and + * DesiredSSID + */ + wordbuf[0] = cpu_to_le16(WLAN_SSID_MAXLEN); + get_random_bytes(&wordbuf[1], WLAN_SSID_MAXLEN); + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFOWNSSID, + wordbuf, + HFA384x_RID_CNFOWNSSID_LEN); + if (result) { + netdev_err(wlandev->netdev, "Failed to set OwnSSID.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, + wordbuf, + HFA384x_RID_CNFDESIREDSSID_LEN); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set DesiredSSID.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + /* bsstype */ + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFPORTTYPE, + HFA384x_PORTTYPE_IBSS); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set CNFPORTTYPE.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + /* ibss options */ + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CREATEIBSS, + HFA384x_CREATEIBSS_JOINCREATEIBSS); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set CREATEIBSS.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + result = hfa384x_drvr_enable(hw, 0); + if (result) { + netdev_err(wlandev->netdev, + "drvr_enable(0) failed. result=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + istmpenable = 1; + } + + /* Figure out our timeout first Kus, then HZ */ + timeout = msg->channellist.data.len * msg->maxchanneltime.data; + timeout = (timeout * HZ) / 1000; + + /* Issue the scan request */ + hw->scanflag = 0; + + result = hfa384x_drvr_setconfig(hw, + HFA384x_RID_HOSTSCAN, &scanreq, + sizeof(scanreq)); + if (result) { + netdev_err(wlandev->netdev, + "setconfig(SCANREQUEST) failed. result=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + + /* sleep until info frame arrives */ + wait_event_interruptible_timeout(hw->cmdq, hw->scanflag, timeout); + + msg->numbss.status = P80211ENUM_msgitem_status_data_ok; + if (hw->scanflag == -1) + hw->scanflag = 0; + + msg->numbss.data = hw->scanflag; + + hw->scanflag = 0; + + /* Disable port if we temporarily enabled it. */ + if (istmpenable) { + result = hfa384x_drvr_disable(hw, 0); + if (result) { + netdev_err(wlandev->netdev, + "drvr_disable(0) failed. result=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + } + + /* restore original roaming mode */ + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFROAMINGMODE, + roamingmode); + if (result) { + netdev_err(wlandev->netdev, + "setconfig(ROAMMODE) failed. result=%d\n", result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + goto exit; + } + + result = 0; + msg->resultcode.data = P80211ENUM_resultcode_success; + +exit: + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + + return result; +} + +/*---------------------------------------------------------------- + * prism2mgmt_scan_results + * + * Retrieve the BSS description for one of the BSSs identified in + * a scan. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + *---------------------------------------------------------------- + */ +int prism2mgmt_scan_results(struct wlandevice *wlandev, void *msgp) +{ + int result = 0; + struct p80211msg_dot11req_scan_results *req; + struct hfa384x *hw = wlandev->priv; + struct hfa384x_hscan_result_sub *item = NULL; + + int count; + + req = msgp; + + req->resultcode.status = P80211ENUM_msgitem_status_data_ok; + + if (!hw->scanresults) { + netdev_err(wlandev->netdev, + "dot11req_scan_results can only be used after a successful dot11req_scan.\n"); + result = 2; + req->resultcode.data = P80211ENUM_resultcode_invalid_parameters; + goto exit; + } + + count = (hw->scanresults->framelen - 3) / 32; + if (count > HFA384x_SCANRESULT_MAX) + count = HFA384x_SCANRESULT_MAX; + + if (req->bssindex.data >= count) { + netdev_dbg(wlandev->netdev, + "requested index (%d) out of range (%d)\n", + req->bssindex.data, count); + result = 2; + req->resultcode.data = P80211ENUM_resultcode_invalid_parameters; + goto exit; + } + + item = &hw->scanresults->info.hscanresult.result[req->bssindex.data]; + /* signal and noise */ + req->signal.status = P80211ENUM_msgitem_status_data_ok; + req->noise.status = P80211ENUM_msgitem_status_data_ok; + req->signal.data = le16_to_cpu(item->sl); + req->noise.data = le16_to_cpu(item->anl); + + /* BSSID */ + req->bssid.status = P80211ENUM_msgitem_status_data_ok; + req->bssid.data.len = WLAN_BSSID_LEN; + memcpy(req->bssid.data.data, item->bssid, WLAN_BSSID_LEN); + + /* SSID */ + req->ssid.status = P80211ENUM_msgitem_status_data_ok; + req->ssid.data.len = le16_to_cpu(item->ssid.len); + req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_SSID_MAXLEN); + memcpy(req->ssid.data.data, item->ssid.data, req->ssid.data.len); + + /* supported rates */ + for (count = 0; count < 10; count++) + if (item->supprates[count] == 0) + break; + + for (int i = 0; i < 8; i++) { + if (count > i && + DOT11_RATE5_ISBASIC_GET(item->supprates[i])) { + req->basicrate[i].data = item->supprates[i]; + req->basicrate[i].status = + P80211ENUM_msgitem_status_data_ok; + } + } + + for (int i = 0; i < 8; i++) { + if (count > i) { + req->supprate[i].data = item->supprates[i]; + req->supprate[i].status = + P80211ENUM_msgitem_status_data_ok; + } + } + + /* beacon period */ + req->beaconperiod.status = P80211ENUM_msgitem_status_data_ok; + req->beaconperiod.data = le16_to_cpu(item->bcnint); + + /* timestamps */ + req->timestamp.status = P80211ENUM_msgitem_status_data_ok; + req->timestamp.data = jiffies; + req->localtime.status = P80211ENUM_msgitem_status_data_ok; + req->localtime.data = jiffies; + + /* atim window */ + req->ibssatimwindow.status = P80211ENUM_msgitem_status_data_ok; + req->ibssatimwindow.data = le16_to_cpu(item->atim); + + /* Channel */ + req->dschannel.status = P80211ENUM_msgitem_status_data_ok; + req->dschannel.data = le16_to_cpu(item->chid); + + /* capinfo bits */ + count = le16_to_cpu(item->capinfo); + req->capinfo.status = P80211ENUM_msgitem_status_data_ok; + req->capinfo.data = count; + + /* privacy flag */ + req->privacy.status = P80211ENUM_msgitem_status_data_ok; + req->privacy.data = WLAN_GET_MGMT_CAP_INFO_PRIVACY(count); + + /* cfpollable */ + req->cfpollable.status = P80211ENUM_msgitem_status_data_ok; + req->cfpollable.data = WLAN_GET_MGMT_CAP_INFO_CFPOLLABLE(count); + + /* cfpollreq */ + req->cfpollreq.status = P80211ENUM_msgitem_status_data_ok; + req->cfpollreq.data = WLAN_GET_MGMT_CAP_INFO_CFPOLLREQ(count); + + /* bsstype */ + req->bsstype.status = P80211ENUM_msgitem_status_data_ok; + req->bsstype.data = (WLAN_GET_MGMT_CAP_INFO_ESS(count)) ? + P80211ENUM_bsstype_infrastructure : P80211ENUM_bsstype_independent; + + result = 0; + req->resultcode.data = P80211ENUM_resultcode_success; + +exit: + return result; +} + +/*---------------------------------------------------------------- + * prism2mgmt_start + * + * Start a BSS. Any station can do this for IBSS, only AP for ESS. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + *---------------------------------------------------------------- + */ +int prism2mgmt_start(struct wlandevice *wlandev, void *msgp) +{ + int result = 0; + struct hfa384x *hw = wlandev->priv; + struct p80211msg_dot11req_start *msg = msgp; + + struct p80211pstrd *pstr; + u8 bytebuf[80]; + struct hfa384x_bytestr *p2bytestr = (struct hfa384x_bytestr *)bytebuf; + u16 word; + + wlandev->macmode = WLAN_MACMODE_NONE; + + /* Set the SSID */ + memcpy(&wlandev->ssid, &msg->ssid.data, sizeof(msg->ssid.data)); + + /*** ADHOC IBSS ***/ + /* see if current f/w is less than 8c3 */ + if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, + hw->ident_sta_fw.variant) < + HFA384x_FIRMWARE_VERSION(0, 8, 3)) { + /* Ad-Hoc not quite supported on Prism2 */ + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + msg->resultcode.data = P80211ENUM_resultcode_not_supported; + goto done; + } + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + + /*** STATION ***/ + /* Set the REQUIRED config items */ + /* SSID */ + pstr = (struct p80211pstrd *)&msg->ssid.data; + prism2mgmt_pstr2bytestr(p2bytestr, pstr); + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFOWNSSID, + bytebuf, HFA384x_RID_CNFOWNSSID_LEN); + if (result) { + netdev_err(wlandev->netdev, "Failed to set CnfOwnSSID\n"); + goto failed; + } + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, + bytebuf, + HFA384x_RID_CNFDESIREDSSID_LEN); + if (result) { + netdev_err(wlandev->netdev, "Failed to set CnfDesiredSSID\n"); + goto failed; + } + + /* bsstype - we use the default in the ap firmware */ + /* IBSS port */ + hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, 0); + + /* beacon period */ + word = msg->beaconperiod.data; + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAPBCNINT, word); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set beacon period=%d.\n", word); + goto failed; + } + + /* dschannel */ + word = msg->dschannel.data; + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFOWNCHANNEL, word); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set channel=%d.\n", word); + goto failed; + } + /* Basic rates */ + word = p80211rate_to_p2bit(msg->basicrate1.data); + if (msg->basicrate2.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate2.data); + + if (msg->basicrate3.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate3.data); + + if (msg->basicrate4.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate4.data); + + if (msg->basicrate5.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate5.data); + + if (msg->basicrate6.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate6.data); + + if (msg->basicrate7.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate7.data); + + if (msg->basicrate8.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->basicrate8.data); + + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFBASICRATES, word); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set basicrates=%d.\n", word); + goto failed; + } + + /* Operational rates (supprates and txratecontrol) */ + word = p80211rate_to_p2bit(msg->operationalrate1.data); + if (msg->operationalrate2.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate2.data); + + if (msg->operationalrate3.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate3.data); + + if (msg->operationalrate4.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate4.data); + + if (msg->operationalrate5.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate5.data); + + if (msg->operationalrate6.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate6.data); + + if (msg->operationalrate7.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate7.data); + + if (msg->operationalrate8.status == P80211ENUM_msgitem_status_data_ok) + word |= p80211rate_to_p2bit(msg->operationalrate8.data); + + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFSUPPRATES, word); + if (result) { + netdev_err(wlandev->netdev, + "Failed to set supprates=%d.\n", word); + goto failed; + } + + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, word); + if (result) { + netdev_err(wlandev->netdev, "Failed to set txrates=%d.\n", + word); + goto failed; + } + + /* Set the macmode so the frame setup code knows what to do */ + if (msg->bsstype.data == P80211ENUM_bsstype_independent) { + wlandev->macmode = WLAN_MACMODE_IBSS_STA; + /* lets extend the data length a bit */ + hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, 2304); + } + + /* Enable the Port */ + result = hfa384x_drvr_enable(hw, 0); + if (result) { + netdev_err(wlandev->netdev, + "Enable macport failed, result=%d.\n", result); + goto failed; + } + + msg->resultcode.data = P80211ENUM_resultcode_success; + + goto done; +failed: + netdev_dbg(wlandev->netdev, + "Failed to set a config option, result=%d\n", result); + msg->resultcode.data = P80211ENUM_resultcode_invalid_parameters; + +done: + return 0; +} + +/*---------------------------------------------------------------- + * prism2mgmt_readpda + * + * Collect the PDA data and put it in the message. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + *---------------------------------------------------------------- + */ +int prism2mgmt_readpda(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + struct p80211msg_p2req_readpda *msg = msgp; + int result; + + /* We only support collecting the PDA when in the FWLOAD + * state. + */ + if (wlandev->msdstate != WLAN_MSD_FWLOAD) { + netdev_err(wlandev->netdev, + "PDA may only be read in the fwload state.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + } else { + /* Call drvr_readpda(), it handles the auxport enable + * and validating the returned PDA. + */ + result = hfa384x_drvr_readpda(hw, + msg->pda.data, + HFA384x_PDA_LEN_MAX); + if (result) { + netdev_err(wlandev->netdev, + "hfa384x_drvr_readpda() failed, result=%d\n", + result); + + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = + P80211ENUM_msgitem_status_data_ok; + return 0; + } + msg->pda.status = P80211ENUM_msgitem_status_data_ok; + msg->resultcode.data = P80211ENUM_resultcode_success; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + } + + return 0; +} + +/*---------------------------------------------------------------- + * prism2mgmt_ramdl_state + * + * Establishes the beginning/end of a card RAM download session. + * + * It is expected that the ramdl_write() function will be called + * one or more times between the 'enable' and 'disable' calls to + * this function. + * + * Note: This function should not be called when a mac comm port + * is active. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + *---------------------------------------------------------------- + */ +int prism2mgmt_ramdl_state(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + struct p80211msg_p2req_ramdl_state *msg = msgp; + + if (wlandev->msdstate != WLAN_MSD_FWLOAD) { + netdev_err(wlandev->netdev, + "ramdl_state(): may only be called in the fwload state.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + return 0; + } + + /* + ** Note: Interrupts are locked out if this is an AP and are NOT + ** locked out if this is a station. + */ + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + if (msg->enable.data == P80211ENUM_truth_true) { + if (hfa384x_drvr_ramdl_enable(hw, msg->exeaddr.data)) { + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + } else { + msg->resultcode.data = P80211ENUM_resultcode_success; + } + } else { + hfa384x_drvr_ramdl_disable(hw); + msg->resultcode.data = P80211ENUM_resultcode_success; + } + + return 0; +} + +/*---------------------------------------------------------------- + * prism2mgmt_ramdl_write + * + * Writes a buffer to the card RAM using the download state. This + * is for writing code to card RAM. To just read or write raw data + * use the aux functions. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + *---------------------------------------------------------------- + */ +int prism2mgmt_ramdl_write(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + struct p80211msg_p2req_ramdl_write *msg = msgp; + u32 addr; + u32 len; + u8 *buf; + + if (wlandev->msdstate != WLAN_MSD_FWLOAD) { + netdev_err(wlandev->netdev, + "ramdl_write(): may only be called in the fwload state.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + return 0; + } + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + /* first validate the length */ + if (msg->len.data > sizeof(msg->data.data)) { + msg->resultcode.status = + P80211ENUM_resultcode_invalid_parameters; + return 0; + } + /* call the hfa384x function to do the write */ + addr = msg->addr.data; + len = msg->len.data; + buf = msg->data.data; + if (hfa384x_drvr_ramdl_write(hw, addr, buf, len)) + msg->resultcode.data = P80211ENUM_resultcode_refused; + + msg->resultcode.data = P80211ENUM_resultcode_success; + + return 0; +} + +/*---------------------------------------------------------------- + * prism2mgmt_flashdl_state + * + * Establishes the beginning/end of a card Flash download session. + * + * It is expected that the flashdl_write() function will be called + * one or more times between the 'enable' and 'disable' calls to + * this function. + * + * Note: This function should not be called when a mac comm port + * is active. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + *---------------------------------------------------------------- + */ +int prism2mgmt_flashdl_state(struct wlandevice *wlandev, void *msgp) +{ + int result = 0; + struct hfa384x *hw = wlandev->priv; + struct p80211msg_p2req_flashdl_state *msg = msgp; + + if (wlandev->msdstate != WLAN_MSD_FWLOAD) { + netdev_err(wlandev->netdev, + "flashdl_state(): may only be called in the fwload state.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + return 0; + } + + /* + ** Note: Interrupts are locked out if this is an AP and are NOT + ** locked out if this is a station. + */ + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + if (msg->enable.data == P80211ENUM_truth_true) { + if (hfa384x_drvr_flashdl_enable(hw)) { + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + } else { + msg->resultcode.data = P80211ENUM_resultcode_success; + } + } else { + hfa384x_drvr_flashdl_disable(hw); + msg->resultcode.data = P80211ENUM_resultcode_success; + /* NOTE: At this point, the MAC is in the post-reset + * state and the driver is in the fwload state. + * We need to get the MAC back into the fwload + * state. To do this, we set the nsdstate to HWPRESENT + * and then call the ifstate function to redo everything + * that got us into the fwload state. + */ + wlandev->msdstate = WLAN_MSD_HWPRESENT; + result = prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload); + if (result != P80211ENUM_resultcode_success) { + netdev_err(wlandev->netdev, + "prism2sta_ifstate(fwload) failed, P80211ENUM_resultcode=%d\n", + result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + result = -1; + } + } + + return result; +} + +/*---------------------------------------------------------------- + * prism2mgmt_flashdl_write + * + * + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + *---------------------------------------------------------------- + */ +int prism2mgmt_flashdl_write(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + struct p80211msg_p2req_flashdl_write *msg = msgp; + u32 addr; + u32 len; + u8 *buf; + + if (wlandev->msdstate != WLAN_MSD_FWLOAD) { + netdev_err(wlandev->netdev, + "flashdl_write(): may only be called in the fwload state.\n"); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + return 0; + } + + /* + ** Note: Interrupts are locked out if this is an AP and are NOT + ** locked out if this is a station. + */ + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + /* first validate the length */ + if (msg->len.data > sizeof(msg->data.data)) { + msg->resultcode.status = + P80211ENUM_resultcode_invalid_parameters; + return 0; + } + /* call the hfa384x function to do the write */ + addr = msg->addr.data; + len = msg->len.data; + buf = msg->data.data; + if (hfa384x_drvr_flashdl_write(hw, addr, buf, len)) + msg->resultcode.data = P80211ENUM_resultcode_refused; + + msg->resultcode.data = P80211ENUM_resultcode_success; + + return 0; +} + +/*---------------------------------------------------------------- + * prism2mgmt_autojoin + * + * Associate with an ESS. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + *---------------------------------------------------------------- + */ +int prism2mgmt_autojoin(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + int result = 0; + u16 reg; + u16 port_type; + struct p80211msg_lnxreq_autojoin *msg = msgp; + struct p80211pstrd *pstr; + u8 bytebuf[256]; + struct hfa384x_bytestr *p2bytestr = (struct hfa384x_bytestr *)bytebuf; + + wlandev->macmode = WLAN_MACMODE_NONE; + + /* Set the SSID */ + memcpy(&wlandev->ssid, &msg->ssid.data, sizeof(msg->ssid.data)); + + /* Disable the Port */ + hfa384x_drvr_disable(hw, 0); + + /*** STATION ***/ + /* Set the TxRates */ + hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, 0x000f); + + /* Set the auth type */ + if (msg->authtype.data == P80211ENUM_authalg_sharedkey) + reg = HFA384x_CNFAUTHENTICATION_SHAREDKEY; + else + reg = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; + + hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, reg); + + /* Set the ssid */ + memset(bytebuf, 0, 256); + pstr = (struct p80211pstrd *)&msg->ssid.data; + prism2mgmt_pstr2bytestr(p2bytestr, pstr); + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, + bytebuf, + HFA384x_RID_CNFDESIREDSSID_LEN); + port_type = HFA384x_PORTTYPE_BSS; + /* Set the PortType */ + hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, port_type); + + /* Enable the Port */ + hfa384x_drvr_enable(hw, 0); + + /* Set the resultcode */ + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + msg->resultcode.data = P80211ENUM_resultcode_success; + + return result; +} + +/*---------------------------------------------------------------- + * prism2mgmt_wlansniff + * + * Start or stop sniffing. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + *---------------------------------------------------------------- + */ +int prism2mgmt_wlansniff(struct wlandevice *wlandev, void *msgp) +{ + int result = 0; + struct p80211msg_lnxreq_wlansniff *msg = msgp; + + struct hfa384x *hw = wlandev->priv; + u16 word; + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + switch (msg->enable.data) { + case P80211ENUM_truth_false: + /* Confirm that we're in monitor mode */ + if (wlandev->netdev->type == ARPHRD_ETHER) { + msg->resultcode.data = + P80211ENUM_resultcode_invalid_parameters; + return 0; + } + /* Disable monitor mode */ + result = hfa384x_cmd_monitor(hw, HFA384x_MONITOR_DISABLE); + if (result) { + netdev_dbg(wlandev->netdev, + "failed to disable monitor mode, result=%d\n", + result); + goto failed; + } + /* Disable port 0 */ + result = hfa384x_drvr_disable(hw, 0); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to disable port 0 after sniffing, result=%d\n", + result); + goto failed; + } + /* Clear the driver state */ + wlandev->netdev->type = ARPHRD_ETHER; + + /* Restore the wepflags */ + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFWEPFLAGS, + hw->presniff_wepflags); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to restore wepflags=0x%04x, result=%d\n", + hw->presniff_wepflags, result); + goto failed; + } + + /* Set the port to its prior type and enable (if necessary) */ + if (hw->presniff_port_type != 0) { + word = hw->presniff_port_type; + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFPORTTYPE, + word); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to restore porttype, result=%d\n", + result); + goto failed; + } + + /* Enable the port */ + result = hfa384x_drvr_enable(hw, 0); + if (result) { + netdev_dbg(wlandev->netdev, + "failed to enable port to presniff setting, result=%d\n", + result); + goto failed; + } + } else { + result = hfa384x_drvr_disable(hw, 0); + } + + netdev_info(wlandev->netdev, "monitor mode disabled\n"); + msg->resultcode.data = P80211ENUM_resultcode_success; + return 0; + case P80211ENUM_truth_true: + /* Disable the port (if enabled), only check Port 0 */ + if (hw->port_enabled[0]) { + if (wlandev->netdev->type == ARPHRD_ETHER) { + /* Save macport 0 state */ + result = hfa384x_drvr_getconfig16(hw, + HFA384x_RID_CNFPORTTYPE, + &hw->presniff_port_type); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to read porttype, result=%d\n", + result); + goto failed; + } + /* Save the wepflags state */ + result = hfa384x_drvr_getconfig16(hw, + HFA384x_RID_CNFWEPFLAGS, + &hw->presniff_wepflags); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to read wepflags, result=%d\n", + result); + goto failed; + } + hfa384x_drvr_stop(hw); + result = hfa384x_drvr_start(hw); + if (result) { + netdev_dbg(wlandev->netdev, + "failed to restart the card for sniffing, result=%d\n", + result); + goto failed; + } + } else { + /* Disable the port */ + result = hfa384x_drvr_disable(hw, 0); + if (result) { + netdev_dbg(wlandev->netdev, + "failed to enable port for sniffing, result=%d\n", + result); + goto failed; + } + } + } else { + hw->presniff_port_type = 0; + } + + /* Set the channel we wish to sniff */ + word = msg->channel.data; + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFOWNCHANNEL, + word); + hw->sniff_channel = word; + + if (result) { + netdev_dbg(wlandev->netdev, + "failed to set channel %d, result=%d\n", + word, result); + goto failed; + } + + /* Now if we're already sniffing, we can skip the rest */ + if (wlandev->netdev->type != ARPHRD_ETHER) { + /* Set the port type to pIbss */ + word = HFA384x_PORTTYPE_PSUEDOIBSS; + result = hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFPORTTYPE, + word); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to set porttype %d, result=%d\n", + word, result); + goto failed; + } + if ((msg->keepwepflags.status == + P80211ENUM_msgitem_status_data_ok) && + (msg->keepwepflags.data != P80211ENUM_truth_true)) { + /* Set the wepflags for no decryption */ + word = HFA384x_WEPFLAGS_DISABLE_TXCRYPT | + HFA384x_WEPFLAGS_DISABLE_RXCRYPT; + result = + hfa384x_drvr_setconfig16(hw, + HFA384x_RID_CNFWEPFLAGS, + word); + } + + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to set wepflags=0x%04x, result=%d\n", + word, result); + goto failed; + } + } + + /* Do we want to strip the FCS in monitor mode? */ + if ((msg->stripfcs.status == + P80211ENUM_msgitem_status_data_ok) && + (msg->stripfcs.data == P80211ENUM_truth_true)) { + hw->sniff_fcs = 0; + } else { + hw->sniff_fcs = 1; + } + + /* Do we want to truncate the packets? */ + if (msg->packet_trunc.status == + P80211ENUM_msgitem_status_data_ok) { + hw->sniff_truncate = msg->packet_trunc.data; + } else { + hw->sniff_truncate = 0; + } + + /* Enable the port */ + result = hfa384x_drvr_enable(hw, 0); + if (result) { + netdev_dbg + (wlandev->netdev, + "failed to enable port for sniffing, result=%d\n", + result); + goto failed; + } + /* Enable monitor mode */ + result = hfa384x_cmd_monitor(hw, HFA384x_MONITOR_ENABLE); + if (result) { + netdev_dbg(wlandev->netdev, + "failed to enable monitor mode, result=%d\n", + result); + goto failed; + } + + if (wlandev->netdev->type == ARPHRD_ETHER) + netdev_info(wlandev->netdev, "monitor mode enabled\n"); + + /* Set the driver state */ + /* Do we want the prism2 header? */ + if ((msg->prismheader.status == + P80211ENUM_msgitem_status_data_ok) && + (msg->prismheader.data == P80211ENUM_truth_true)) { + hw->sniffhdr = 0; + wlandev->netdev->type = ARPHRD_IEEE80211_PRISM; + } else if ((msg->wlanheader.status == + P80211ENUM_msgitem_status_data_ok) && + (msg->wlanheader.data == P80211ENUM_truth_true)) { + hw->sniffhdr = 1; + wlandev->netdev->type = ARPHRD_IEEE80211_PRISM; + } else { + wlandev->netdev->type = ARPHRD_IEEE80211; + } + + msg->resultcode.data = P80211ENUM_resultcode_success; + return 0; + default: + msg->resultcode.data = P80211ENUM_resultcode_invalid_parameters; + return 0; + } + +failed: + msg->resultcode.data = P80211ENUM_resultcode_refused; + return 0; +} diff --git a/drivers/staging/wlan-ng/prism2mgmt.h b/drivers/staging/wlan-ng/prism2mgmt.h new file mode 100644 index 0000000000..083a055ee9 --- /dev/null +++ b/drivers/staging/wlan-ng/prism2mgmt.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) */ +/* + * + * Declares the mgmt command handler functions + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file contains the constants and data structures for interaction + * with the hfa384x Wireless LAN (WLAN) Media Access Controller (MAC). + * The hfa384x is a portion of the Harris PRISM(tm) WLAN chipset. + * + * [Implementation and usage notes] + * + * [References] + * CW10 Programmer's Manual v1.5 + * IEEE 802.11 D10.0 + * + * -------------------------------------------------------------------- + */ + +#ifndef _PRISM2MGMT_H +#define _PRISM2MGMT_H + +extern int prism2_reset_holdtime; +extern int prism2_reset_settletime; + +u32 prism2sta_ifstate(struct wlandevice *wlandev, u32 ifstate); + +void prism2sta_ev_info(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +void prism2sta_ev_txexc(struct wlandevice *wlandev, u16 status); +void prism2sta_ev_tx(struct wlandevice *wlandev, u16 status); +void prism2sta_ev_alloc(struct wlandevice *wlandev); + +int prism2mgmt_mibset_mibget(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_scan(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_scan_results(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_start(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_wlansniff(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_readpda(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_ramdl_state(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_ramdl_write(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_flashdl_state(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_flashdl_write(struct wlandevice *wlandev, void *msgp); +int prism2mgmt_autojoin(struct wlandevice *wlandev, void *msgp); + +/*--------------------------------------------------------------- + * conversion functions going between wlan message data types and + * Prism2 data types + *--------------------------------------------------------------- + */ + +/* byte area conversion functions*/ +void prism2mgmt_bytearea2pstr(u8 *bytearea, struct p80211pstrd *pstr, int len); + +/* byte string conversion functions*/ +void prism2mgmt_pstr2bytestr(struct hfa384x_bytestr *bytestr, + struct p80211pstrd *pstr); +void prism2mgmt_bytestr2pstr(struct hfa384x_bytestr *bytestr, + struct p80211pstrd *pstr); + +/* functions to convert Group Addresses */ +void prism2mgmt_get_grpaddr(u32 did, struct p80211pstrd *pstr, + struct hfa384x *priv); +int prism2mgmt_set_grpaddr(u32 did, + u8 *prism2buf, struct p80211pstrd *pstr, + struct hfa384x *priv); +int prism2mgmt_get_grpaddr_index(u32 did); + +void prism2sta_processing_defer(struct work_struct *data); + +void prism2sta_commsqual_defer(struct work_struct *data); +void prism2sta_commsqual_timer(struct timer_list *t); + +/* Interface callback functions, passing data back up to the cfg80211 layer */ +void prism2_connect_result(struct wlandevice *wlandev, u8 failed); +void prism2_disconnected(struct wlandevice *wlandev); +void prism2_roamed(struct wlandevice *wlandev); + +#endif diff --git a/drivers/staging/wlan-ng/prism2mib.c b/drivers/staging/wlan-ng/prism2mib.c new file mode 100644 index 0000000000..4346b90c1a --- /dev/null +++ b/drivers/staging/wlan-ng/prism2mib.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Management request for mibset/mibget + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * The functions in this file handle the mibset/mibget management + * functions. + * + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211mgmt.h" +#include "p80211conv.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211metadef.h" +#include "p80211metastruct.h" +#include "hfa384x.h" +#include "prism2mgmt.h" + +#define MIB_TMP_MAXLEN 200 /* Max length of RID record (in bytes). */ + +#define F_STA 0x1 /* MIB is supported on stations. */ +#define F_READ 0x2 /* MIB may be read. */ +#define F_WRITE 0x4 /* MIB may be written. */ + +struct mibrec { + u32 did; + u16 flag; + u16 parm1; + u16 parm2; + u16 parm3; + int (*func)(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data); +}; + +static int prism2mib_bytearea2pstr(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data); + +static int prism2mib_uint32(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data); + +static int prism2mib_flag(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data); + +static int prism2mib_wepdefaultkey(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data); + +static int prism2mib_privacyinvoked(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data); + +static int +prism2mib_fragmentationthreshold(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data); + +static int prism2mib_priv(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data); + +static struct mibrec mibtab[] = { + /* dot11smt MIB's */ + {didmib_dot11smt_wepdefaultkeystable_key(1), + F_STA | F_WRITE, + HFA384x_RID_CNFWEPDEFAULTKEY0, 0, 0, + prism2mib_wepdefaultkey}, + {didmib_dot11smt_wepdefaultkeystable_key(2), + F_STA | F_WRITE, + HFA384x_RID_CNFWEPDEFAULTKEY1, 0, 0, + prism2mib_wepdefaultkey}, + {didmib_dot11smt_wepdefaultkeystable_key(3), + F_STA | F_WRITE, + HFA384x_RID_CNFWEPDEFAULTKEY2, 0, 0, + prism2mib_wepdefaultkey}, + {didmib_dot11smt_wepdefaultkeystable_key(4), + F_STA | F_WRITE, + HFA384x_RID_CNFWEPDEFAULTKEY3, 0, 0, + prism2mib_wepdefaultkey}, + {DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFWEPFLAGS, HFA384x_WEPFLAGS_PRIVINVOKED, 0, + prism2mib_privacyinvoked}, + {DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFWEPDEFAULTKEYID, 0, 0, + prism2mib_uint32}, + {DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFWEPFLAGS, HFA384x_WEPFLAGS_EXCLUDE, 0, + prism2mib_flag}, + + /* dot11mac MIB's */ + + {DIDMIB_DOT11MAC_OPERATIONTABLE_MACADDRESS, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFOWNMACADDR, HFA384x_RID_CNFOWNMACADDR_LEN, 0, + prism2mib_bytearea2pstr}, + {DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD, + F_STA | F_READ | F_WRITE, + HFA384x_RID_RTSTHRESH, 0, 0, + prism2mib_uint32}, + {DIDMIB_DOT11MAC_OPERATIONTABLE_SHORTRETRYLIMIT, + F_STA | F_READ, + HFA384x_RID_SHORTRETRYLIMIT, 0, 0, + prism2mib_uint32}, + {DIDMIB_DOT11MAC_OPERATIONTABLE_LONGRETRYLIMIT, + F_STA | F_READ, + HFA384x_RID_LONGRETRYLIMIT, 0, 0, + prism2mib_uint32}, + {DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD, + F_STA | F_READ | F_WRITE, + HFA384x_RID_FRAGTHRESH, 0, 0, + prism2mib_fragmentationthreshold}, + {DIDMIB_DOT11MAC_OPERATIONTABLE_MAXTRANSMITMSDULIFETIME, + F_STA | F_READ, + HFA384x_RID_MAXTXLIFETIME, 0, 0, + prism2mib_uint32}, + + /* dot11phy MIB's */ + + {DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL, + F_STA | F_READ, + HFA384x_RID_CURRENTCHANNEL, 0, 0, + prism2mib_uint32}, + {DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL, + F_STA | F_READ | F_WRITE, + HFA384x_RID_TXPOWERMAX, 0, 0, + prism2mib_uint32}, + + /* p2Static MIB's */ + + {DIDMIB_P2_STATIC_CNFPORTTYPE, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFPORTTYPE, 0, 0, + prism2mib_uint32}, + + /* p2MAC MIB's */ + + {DIDMIB_P2_MAC_CURRENTTXRATE, + F_STA | F_READ, + HFA384x_RID_CURRENTTXRATE, 0, 0, + prism2mib_uint32}, + + /* And finally, lnx mibs */ + {DIDMIB_LNX_CONFIGTABLE_RSNAIE, + F_STA | F_READ | F_WRITE, + HFA384x_RID_CNFWPADATA, 0, 0, + prism2mib_priv}, + {0, 0, 0, 0, 0, NULL} +}; + +/* + * prism2mgmt_mibset_mibget + * + * Set the value of a mib item. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * 0 success and done + * <0 success, but we're waiting for something to finish. + * >0 an error occurred while handling the message. + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + */ + +int prism2mgmt_mibset_mibget(struct wlandevice *wlandev, void *msgp) +{ + struct hfa384x *hw = wlandev->priv; + int result, isget; + struct mibrec *mib; + + u16 which; + + struct p80211msg_dot11req_mibset *msg = msgp; + struct p80211itemd *mibitem; + + msg->resultcode.status = P80211ENUM_msgitem_status_data_ok; + msg->resultcode.data = P80211ENUM_resultcode_success; + + /* + ** Determine if this is an Access Point or a station. + */ + + which = F_STA; + + /* + ** Find the MIB in the MIB table. Note that a MIB may be in the + ** table twice...once for an AP and once for a station. Make sure + ** to get the correct one. Note that DID=0 marks the end of the + ** MIB table. + */ + + mibitem = (struct p80211itemd *)msg->mibattribute.data; + + for (mib = mibtab; mib->did != 0; mib++) + if (mib->did == mibitem->did && (mib->flag & which)) + break; + + if (mib->did == 0) { + msg->resultcode.data = P80211ENUM_resultcode_not_supported; + goto done; + } + + /* + ** Determine if this is a "mibget" or a "mibset". If this is a + ** "mibget", then make sure that the MIB may be read. Otherwise, + ** this is a "mibset" so make sure that the MIB may be written. + */ + + isget = (msg->msgcode == DIDMSG_DOT11REQ_MIBGET); + + if (isget) { + if (!(mib->flag & F_READ)) { + msg->resultcode.data = + P80211ENUM_resultcode_cant_get_writeonly_mib; + goto done; + } + } else { + if (!(mib->flag & F_WRITE)) { + msg->resultcode.data = + P80211ENUM_resultcode_cant_set_readonly_mib; + goto done; + } + } + + /* + ** Execute the MIB function. If things worked okay, then make + ** sure that the MIB function also worked okay. If so, and this + ** is a "mibget", then the status value must be set for both the + ** "mibattribute" parameter and the mib item within the data + ** portion of the "mibattribute". + */ + + result = mib->func(mib, isget, wlandev, hw, msg, (void *)mibitem->data); + + if (msg->resultcode.data == P80211ENUM_resultcode_success) { + if (result != 0) { + pr_debug("get/set failure, result=%d\n", result); + msg->resultcode.data = + P80211ENUM_resultcode_implementation_failure; + } else { + if (isget) { + msg->mibattribute.status = + P80211ENUM_msgitem_status_data_ok; + mibitem->status = + P80211ENUM_msgitem_status_data_ok; + } + } + } + +done: + return 0; +} + +/* + * prism2mib_bytearea2pstr + * + * Get/set pstr data to/from a byte area. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Number of bytes of RID data. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_bytearea2pstr(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data) +{ + int result; + struct p80211pstrd *pstr = data; + u8 bytebuf[MIB_TMP_MAXLEN]; + + if (isget) { + result = + hfa384x_drvr_getconfig(hw, mib->parm1, bytebuf, mib->parm2); + prism2mgmt_bytearea2pstr(bytebuf, pstr, mib->parm2); + } else { + memset(bytebuf, 0, mib->parm2); + memcpy(bytebuf, pstr->data, pstr->len); + result = + hfa384x_drvr_setconfig(hw, mib->parm1, bytebuf, mib->parm2); + } + + return result; +} + +/* + * prism2mib_uint32 + * + * Get/set uint32 data. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Not used. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_uint32(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data) +{ + int result; + u32 *uint32 = data; + u8 bytebuf[MIB_TMP_MAXLEN]; + u16 *wordbuf = (u16 *)bytebuf; + + if (isget) { + result = hfa384x_drvr_getconfig16(hw, mib->parm1, wordbuf); + *uint32 = *wordbuf; + } else { + *wordbuf = *uint32; + result = hfa384x_drvr_setconfig16(hw, mib->parm1, *wordbuf); + } + + return result; +} + +/* + * prism2mib_flag + * + * Get/set a flag. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Bit to get/set. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_flag(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data) +{ + int result; + u32 *uint32 = data; + u8 bytebuf[MIB_TMP_MAXLEN]; + u16 *wordbuf = (u16 *)bytebuf; + u32 flags; + + result = hfa384x_drvr_getconfig16(hw, mib->parm1, wordbuf); + if (result == 0) { + flags = *wordbuf; + if (isget) { + *uint32 = (flags & mib->parm2) ? + P80211ENUM_truth_true : P80211ENUM_truth_false; + } else { + if ((*uint32) == P80211ENUM_truth_true) + flags |= mib->parm2; + else + flags &= ~mib->parm2; + *wordbuf = flags; + result = + hfa384x_drvr_setconfig16(hw, mib->parm1, *wordbuf); + } + } + + return result; +} + +/* + * prism2mib_wepdefaultkey + * + * Get/set WEP default keys. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Number of bytes of RID data. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_wepdefaultkey(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data) +{ + int result; + struct p80211pstrd *pstr = data; + u8 bytebuf[MIB_TMP_MAXLEN]; + u16 len; + + if (isget) { + result = 0; /* Should never happen. */ + } else { + len = (pstr->len > 5) ? HFA384x_RID_CNFWEP128DEFAULTKEY_LEN : + HFA384x_RID_CNFWEPDEFAULTKEY_LEN; + memset(bytebuf, 0, len); + memcpy(bytebuf, pstr->data, pstr->len); + result = hfa384x_drvr_setconfig(hw, mib->parm1, bytebuf, len); + } + + return result; +} + +/* + * prism2mib_privacyinvoked + * + * Get/set the dot11PrivacyInvoked value. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Bit value for PrivacyInvoked flag. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_privacyinvoked(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data) +{ + if (wlandev->hostwep & HOSTWEP_DECRYPT) { + if (wlandev->hostwep & HOSTWEP_DECRYPT) + mib->parm2 |= HFA384x_WEPFLAGS_DISABLE_RXCRYPT; + if (wlandev->hostwep & HOSTWEP_ENCRYPT) + mib->parm2 |= HFA384x_WEPFLAGS_DISABLE_TXCRYPT; + } + + return prism2mib_flag(mib, isget, wlandev, hw, msg, data); +} + +/* + * prism2mib_fragmentationthreshold + * + * Get/set the fragmentation threshold. + * + * MIB record parameters: + * parm1 Prism2 RID value. + * parm2 Not used. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int +prism2mib_fragmentationthreshold(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, + void *data) +{ + u32 *uint32 = data; + + if (!isget) + if ((*uint32) % 2) { + netdev_warn(wlandev->netdev, + "Attempt to set odd number FragmentationThreshold\n"); + msg->resultcode.data = + P80211ENUM_resultcode_not_supported; + return 0; + } + + return prism2mib_uint32(mib, isget, wlandev, hw, msg, data); +} + +/* + * prism2mib_priv + * + * Get/set values in the "priv" data structure. + * + * MIB record parameters: + * parm1 Not used. + * parm2 Not used. + * parm3 Not used. + * + * Arguments: + * mib MIB record. + * isget MIBGET/MIBSET flag. + * wlandev wlan device structure. + * priv "priv" structure. + * hw "hw" structure. + * msg Message structure. + * data Data buffer. + * + * Returns: + * 0 - Success. + * ~0 - Error. + * + */ + +static int prism2mib_priv(struct mibrec *mib, + int isget, + struct wlandevice *wlandev, + struct hfa384x *hw, + struct p80211msg_dot11req_mibset *msg, void *data) +{ + struct p80211pstrd *pstr = data; + + switch (mib->did) { + case DIDMIB_LNX_CONFIGTABLE_RSNAIE: { + /* + * This can never work: wpa is on the stack + * and has no bytes allocated in wpa.data. + */ + struct hfa384x_wpa_data wpa; + + if (isget) { + hfa384x_drvr_getconfig(hw, + HFA384x_RID_CNFWPADATA, + (u8 *)&wpa, + sizeof(wpa)); + pstr->len = 0; + } else { + wpa.datalen = 0; + + hfa384x_drvr_setconfig(hw, + HFA384x_RID_CNFWPADATA, + (u8 *)&wpa, + sizeof(wpa)); + } + break; + } + default: + netdev_err(wlandev->netdev, "Unhandled DID 0x%08x\n", mib->did); + } + + return 0; +} + +/* + * prism2mgmt_pstr2bytestr + * + * Convert the pstr data in the WLAN message structure into an hfa384x + * byte string format. + * + * Arguments: + * bytestr hfa384x byte string data type + * pstr wlan message data + * + * Returns: + * Nothing + * + */ + +void prism2mgmt_pstr2bytestr(struct hfa384x_bytestr *bytestr, + struct p80211pstrd *pstr) +{ + bytestr->len = cpu_to_le16((u16)(pstr->len)); + memcpy(bytestr->data, pstr->data, pstr->len); +} + +/* + * prism2mgmt_bytestr2pstr + * + * Convert the data in an hfa384x byte string format into a + * pstr in the WLAN message. + * + * Arguments: + * bytestr hfa384x byte string data type + * msg wlan message + * + * Returns: + * Nothing + * + */ + +void prism2mgmt_bytestr2pstr(struct hfa384x_bytestr *bytestr, + struct p80211pstrd *pstr) +{ + pstr->len = (u8)(le16_to_cpu(bytestr->len)); + memcpy(pstr->data, bytestr->data, pstr->len); +} + +/* + * prism2mgmt_bytearea2pstr + * + * Convert the data in an hfa384x byte area format into a pstr + * in the WLAN message. + * + * Arguments: + * bytearea hfa384x byte area data type + * msg wlan message + * + * Returns: + * Nothing + * + */ + +void prism2mgmt_bytearea2pstr(u8 *bytearea, struct p80211pstrd *pstr, int len) +{ + pstr->len = (u8)len; + memcpy(pstr->data, bytearea, len); +} diff --git a/drivers/staging/wlan-ng/prism2sta.c b/drivers/staging/wlan-ng/prism2sta.c new file mode 100644 index 0000000000..57180bb716 --- /dev/null +++ b/drivers/staging/wlan-ng/prism2sta.c @@ -0,0 +1,1984 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/* + * + * Implements the station functionality for prism2 + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * -------------------------------------------------------------------- + * + * linux-wlan + * + * -------------------------------------------------------------------- + * + * Inquiries regarding the linux-wlan Open Source project can be + * made directly to: + * + * AbsoluteValue Systems Inc. + * info@linux-wlan.com + * http://www.linux-wlan.com + * + * -------------------------------------------------------------------- + * + * Portions of the development of this software were funded by + * Intersil Corporation as part of PRISM(R) chipset product development. + * + * -------------------------------------------------------------------- + * + * This file implements the module and linux pcmcia routines for the + * prism2 driver. + * + * -------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "p80211types.h" +#include "p80211hdr.h" +#include "p80211mgmt.h" +#include "p80211conv.h" +#include "p80211msg.h" +#include "p80211netdev.h" +#include "p80211req.h" +#include "p80211metadef.h" +#include "p80211metastruct.h" +#include "hfa384x.h" +#include "prism2mgmt.h" + +static char *dev_info = "prism2_usb"; +static struct wlandevice *create_wlan(void); + +int prism2_reset_holdtime = 30; /* Reset hold time in ms */ +int prism2_reset_settletime = 100; /* Reset settle time in ms */ + +static int prism2_doreset; /* Do a reset at init? */ + +module_param(prism2_doreset, int, 0644); +MODULE_PARM_DESC(prism2_doreset, "Issue a reset on initialization"); + +module_param(prism2_reset_holdtime, int, 0644); +MODULE_PARM_DESC(prism2_reset_holdtime, "reset hold time in ms"); +module_param(prism2_reset_settletime, int, 0644); +MODULE_PARM_DESC(prism2_reset_settletime, "reset settle time in ms"); + +MODULE_LICENSE("Dual MPL/GPL"); + +static int prism2sta_open(struct wlandevice *wlandev); +static int prism2sta_close(struct wlandevice *wlandev); +static void prism2sta_reset(struct wlandevice *wlandev); +static int prism2sta_txframe(struct wlandevice *wlandev, struct sk_buff *skb, + struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep); +static int prism2sta_mlmerequest(struct wlandevice *wlandev, + struct p80211msg *msg); +static int prism2sta_getcardinfo(struct wlandevice *wlandev); +static int prism2sta_globalsetup(struct wlandevice *wlandev); +static int prism2sta_setmulticast(struct wlandevice *wlandev, + struct net_device *dev); + +static void prism2sta_inf_handover(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_tallies(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_hostscanresults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_scanresults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_chinforesults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_linkstatus(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_assocstatus(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_authreq(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_authreq_defer(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); +static void prism2sta_inf_psusercnt(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf); + +/* + * prism2sta_open + * + * WLAN device open method. Called from p80211netdev when kernel + * device open (start) method is called in response to the + * SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP + * from clear to set. + * + * Arguments: + * wlandev wlan device structure + * + * Returns: + * 0 success + * >0 f/w reported error + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process thread + */ +static int prism2sta_open(struct wlandevice *wlandev) +{ + /* We don't currently have to do anything else. + * The setup of the MAC should be subsequently completed via + * the mlme commands. + * Higher layers know we're ready from dev->start==1 and + * dev->tbusy==0. Our rx path knows to pass up received/ + * frames because of dev->flags&IFF_UP is true. + */ + + return 0; +} + +/* + * prism2sta_close + * + * WLAN device close method. Called from p80211netdev when kernel + * device close method is called in response to the + * SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP + * from set to clear. + * + * Arguments: + * wlandev wlan device structure + * + * Returns: + * 0 success + * >0 f/w reported error + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process thread + */ +static int prism2sta_close(struct wlandevice *wlandev) +{ + /* We don't currently have to do anything else. + * Higher layers know we're not ready from dev->start==0 and + * dev->tbusy==1. Our rx path knows to not pass up received + * frames because of dev->flags&IFF_UP is false. + */ + + return 0; +} + +/* + * prism2sta_reset + * + * Currently not implemented. + * + * Arguments: + * wlandev wlan device structure + * none + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * process thread + */ +static void prism2sta_reset(struct wlandevice *wlandev) +{ +} + +/* + * prism2sta_txframe + * + * Takes a frame from p80211 and queues it for transmission. + * + * Arguments: + * wlandev wlan device structure + * pb packet buffer struct. Contains an 802.11 + * data frame. + * p80211_hdr points to the 802.11 header for the packet. + * Returns: + * 0 Success and more buffs available + * 1 Success but no more buffs + * 2 Allocation failure + * 4 Buffer full or queue busy + * + * Side effects: + * + * Call context: + * process thread + */ +static int prism2sta_txframe(struct wlandevice *wlandev, struct sk_buff *skb, + struct p80211_hdr *p80211_hdr, + struct p80211_metawep *p80211_wep) +{ + struct hfa384x *hw = wlandev->priv; + + /* If necessary, set the 802.11 WEP bit */ + if ((wlandev->hostwep & (HOSTWEP_PRIVACYINVOKED | HOSTWEP_ENCRYPT)) == + HOSTWEP_PRIVACYINVOKED) { + p80211_hdr->frame_control |= cpu_to_le16(WLAN_SET_FC_ISWEP(1)); + } + + return hfa384x_drvr_txframe(hw, skb, p80211_hdr, p80211_wep); +} + +/* + * prism2sta_mlmerequest + * + * wlan command message handler. All we do here is pass the message + * over to the prism2sta_mgmt_handler. + * + * Arguments: + * wlandev wlan device structure + * msg wlan command message + * Returns: + * 0 success + * <0 successful acceptance of message, but we're + * waiting for an async process to finish before + * we're done with the msg. When the asynch + * process is done, we'll call the p80211 + * function p80211req_confirm() . + * >0 An error occurred while we were handling + * the message. + * + * Side effects: + * + * Call context: + * process thread + */ +static int prism2sta_mlmerequest(struct wlandevice *wlandev, + struct p80211msg *msg) +{ + struct hfa384x *hw = wlandev->priv; + + int result = 0; + + switch (msg->msgcode) { + case DIDMSG_DOT11REQ_MIBGET: + pr_debug("Received mibget request\n"); + result = prism2mgmt_mibset_mibget(wlandev, msg); + break; + case DIDMSG_DOT11REQ_MIBSET: + pr_debug("Received mibset request\n"); + result = prism2mgmt_mibset_mibget(wlandev, msg); + break; + case DIDMSG_DOT11REQ_SCAN: + pr_debug("Received scan request\n"); + result = prism2mgmt_scan(wlandev, msg); + break; + case DIDMSG_DOT11REQ_SCAN_RESULTS: + pr_debug("Received scan_results request\n"); + result = prism2mgmt_scan_results(wlandev, msg); + break; + case DIDMSG_DOT11REQ_START: + pr_debug("Received mlme start request\n"); + result = prism2mgmt_start(wlandev, msg); + break; + /* + * Prism2 specific messages + */ + case DIDMSG_P2REQ_READPDA: + pr_debug("Received mlme readpda request\n"); + result = prism2mgmt_readpda(wlandev, msg); + break; + case DIDMSG_P2REQ_RAMDL_STATE: + pr_debug("Received mlme ramdl_state request\n"); + result = prism2mgmt_ramdl_state(wlandev, msg); + break; + case DIDMSG_P2REQ_RAMDL_WRITE: + pr_debug("Received mlme ramdl_write request\n"); + result = prism2mgmt_ramdl_write(wlandev, msg); + break; + case DIDMSG_P2REQ_FLASHDL_STATE: + pr_debug("Received mlme flashdl_state request\n"); + result = prism2mgmt_flashdl_state(wlandev, msg); + break; + case DIDMSG_P2REQ_FLASHDL_WRITE: + pr_debug("Received mlme flashdl_write request\n"); + result = prism2mgmt_flashdl_write(wlandev, msg); + break; + /* + * Linux specific messages + */ + case DIDMSG_LNXREQ_HOSTWEP: + break; /* ignore me. */ + case DIDMSG_LNXREQ_IFSTATE: { + struct p80211msg_lnxreq_ifstate *ifstatemsg; + + pr_debug("Received mlme ifstate request\n"); + ifstatemsg = (struct p80211msg_lnxreq_ifstate *)msg; + result = prism2sta_ifstate(wlandev, + ifstatemsg->ifstate.data); + ifstatemsg->resultcode.status = + P80211ENUM_msgitem_status_data_ok; + ifstatemsg->resultcode.data = result; + result = 0; + break; + } + case DIDMSG_LNXREQ_WLANSNIFF: + pr_debug("Received mlme wlansniff request\n"); + result = prism2mgmt_wlansniff(wlandev, msg); + break; + case DIDMSG_LNXREQ_AUTOJOIN: + pr_debug("Received mlme autojoin request\n"); + result = prism2mgmt_autojoin(wlandev, msg); + break; + case DIDMSG_LNXREQ_COMMSQUALITY: { + struct p80211msg_lnxreq_commsquality *qualmsg; + + pr_debug("Received commsquality request\n"); + + qualmsg = (struct p80211msg_lnxreq_commsquality *)msg; + + qualmsg->link.status = P80211ENUM_msgitem_status_data_ok; + qualmsg->level.status = P80211ENUM_msgitem_status_data_ok; + qualmsg->noise.status = P80211ENUM_msgitem_status_data_ok; + + qualmsg->link.data = le16_to_cpu(hw->qual.cq_curr_bss); + qualmsg->level.data = le16_to_cpu(hw->qual.asl_curr_bss); + qualmsg->noise.data = le16_to_cpu(hw->qual.anl_curr_fc); + qualmsg->txrate.data = hw->txrate; + + break; + } + default: + netdev_warn(wlandev->netdev, + "Unknown mgmt request message 0x%08x", + msg->msgcode); + break; + } + + return result; +} + +/* + * prism2sta_ifstate + * + * Interface state. This is the primary WLAN interface enable/disable + * handler. Following the driver/load/deviceprobe sequence, this + * function must be called with a state of "enable" before any other + * commands will be accepted. + * + * Arguments: + * wlandev wlan device structure + * msgp ptr to msg buffer + * + * Returns: + * A p80211 message resultcode value. + * + * Side effects: + * + * Call context: + * process thread (usually) + * interrupt + */ +u32 prism2sta_ifstate(struct wlandevice *wlandev, u32 ifstate) +{ + struct hfa384x *hw = wlandev->priv; + u32 result; + + result = P80211ENUM_resultcode_implementation_failure; + + pr_debug("Current MSD state(%d), requesting(%d)\n", + wlandev->msdstate, ifstate); + switch (ifstate) { + case P80211ENUM_ifstate_fwload: + switch (wlandev->msdstate) { + case WLAN_MSD_HWPRESENT: + wlandev->msdstate = WLAN_MSD_FWLOAD_PENDING; + /* + * Initialize the device+driver sufficiently + * for firmware loading. + */ + result = hfa384x_drvr_start(hw); + if (result) { + netdev_err(wlandev->netdev, + "hfa384x_drvr_start() failed,result=%d\n", + (int)result); + result = + P80211ENUM_resultcode_implementation_failure; + wlandev->msdstate = WLAN_MSD_HWPRESENT; + break; + } + wlandev->msdstate = WLAN_MSD_FWLOAD; + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_FWLOAD: + hfa384x_cmd_initialize(hw); + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_RUNNING: + netdev_warn(wlandev->netdev, + "Cannot enter fwload state from enable state, you must disable first.\n"); + result = P80211ENUM_resultcode_invalid_parameters; + break; + case WLAN_MSD_HWFAIL: + default: + /* probe() had a problem or the msdstate contains + * an unrecognized value, there's nothing we can do. + */ + result = P80211ENUM_resultcode_implementation_failure; + break; + } + break; + case P80211ENUM_ifstate_enable: + switch (wlandev->msdstate) { + case WLAN_MSD_HWPRESENT: + case WLAN_MSD_FWLOAD: + wlandev->msdstate = WLAN_MSD_RUNNING_PENDING; + /* Initialize the device+driver for full + * operation. Note that this might me an FWLOAD + * to RUNNING transition so we must not do a chip + * or board level reset. Note that on failure, + * the MSD state is set to HWPRESENT because we + * can't make any assumptions about the state + * of the hardware or a previous firmware load. + */ + result = hfa384x_drvr_start(hw); + if (result) { + netdev_err(wlandev->netdev, + "hfa384x_drvr_start() failed,result=%d\n", + (int)result); + result = + P80211ENUM_resultcode_implementation_failure; + wlandev->msdstate = WLAN_MSD_HWPRESENT; + break; + } + + result = prism2sta_getcardinfo(wlandev); + if (result) { + netdev_err(wlandev->netdev, + "prism2sta_getcardinfo() failed,result=%d\n", + (int)result); + result = + P80211ENUM_resultcode_implementation_failure; + hfa384x_drvr_stop(hw); + wlandev->msdstate = WLAN_MSD_HWPRESENT; + break; + } + result = prism2sta_globalsetup(wlandev); + if (result) { + netdev_err(wlandev->netdev, + "prism2sta_globalsetup() failed,result=%d\n", + (int)result); + result = + P80211ENUM_resultcode_implementation_failure; + hfa384x_drvr_stop(hw); + wlandev->msdstate = WLAN_MSD_HWPRESENT; + break; + } + wlandev->msdstate = WLAN_MSD_RUNNING; + hw->join_ap = 0; + hw->join_retries = 60; + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_RUNNING: + /* Do nothing, we're already in this state. */ + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_HWFAIL: + default: + /* probe() had a problem or the msdstate contains + * an unrecognized value, there's nothing we can do. + */ + result = P80211ENUM_resultcode_implementation_failure; + break; + } + break; + case P80211ENUM_ifstate_disable: + switch (wlandev->msdstate) { + case WLAN_MSD_HWPRESENT: + /* Do nothing, we're already in this state. */ + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_FWLOAD: + case WLAN_MSD_RUNNING: + wlandev->msdstate = WLAN_MSD_HWPRESENT_PENDING; + /* + * TODO: Shut down the MAC completely. Here a chip + * or board level reset is probably called for. + * After a "disable" _all_ results are lost, even + * those from a fwload. + */ + if (!wlandev->hwremoved) + netif_carrier_off(wlandev->netdev); + + hfa384x_drvr_stop(hw); + + wlandev->macmode = WLAN_MACMODE_NONE; + wlandev->msdstate = WLAN_MSD_HWPRESENT; + result = P80211ENUM_resultcode_success; + break; + case WLAN_MSD_HWFAIL: + default: + /* probe() had a problem or the msdstate contains + * an unrecognized value, there's nothing we can do. + */ + result = P80211ENUM_resultcode_implementation_failure; + break; + } + break; + default: + result = P80211ENUM_resultcode_invalid_parameters; + break; + } + + return result; +} + +/* + * prism2sta_getcardinfo + * + * Collect the NICID, firmware version and any other identifiers + * we'd like to have in host-side data structures. + * + * Arguments: + * wlandev wlan device structure + * + * Returns: + * 0 success + * >0 f/w reported error + * <0 driver reported error + * + * Side effects: + * + * Call context: + * Either. + */ +static int prism2sta_getcardinfo(struct wlandevice *wlandev) +{ + int result = 0; + struct hfa384x *hw = wlandev->priv; + u16 temp; + u8 snum[HFA384x_RID_NICSERIALNUMBER_LEN]; + u8 addr[ETH_ALEN]; + + /* Collect version and compatibility info */ + /* Some are critical, some are not */ + /* NIC identity */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICIDENTITY, + &hw->ident_nic, + sizeof(struct hfa384x_compident)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve NICIDENTITY\n"); + goto failed; + } + + /* get all the nic id fields in host byte order */ + le16_to_cpus(&hw->ident_nic.id); + le16_to_cpus(&hw->ident_nic.variant); + le16_to_cpus(&hw->ident_nic.major); + le16_to_cpus(&hw->ident_nic.minor); + + netdev_info(wlandev->netdev, "ident: nic h/w: id=0x%02x %d.%d.%d\n", + hw->ident_nic.id, hw->ident_nic.major, + hw->ident_nic.minor, hw->ident_nic.variant); + + /* Primary f/w identity */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRIIDENTITY, + &hw->ident_pri_fw, + sizeof(struct hfa384x_compident)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve PRIIDENTITY\n"); + goto failed; + } + + /* get all the private fw id fields in host byte order */ + le16_to_cpus(&hw->ident_pri_fw.id); + le16_to_cpus(&hw->ident_pri_fw.variant); + le16_to_cpus(&hw->ident_pri_fw.major); + le16_to_cpus(&hw->ident_pri_fw.minor); + + netdev_info(wlandev->netdev, "ident: pri f/w: id=0x%02x %d.%d.%d\n", + hw->ident_pri_fw.id, hw->ident_pri_fw.major, + hw->ident_pri_fw.minor, hw->ident_pri_fw.variant); + + /* Station (Secondary?) f/w identity */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STAIDENTITY, + &hw->ident_sta_fw, + sizeof(struct hfa384x_compident)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve STAIDENTITY\n"); + goto failed; + } + + if (hw->ident_nic.id < 0x8000) { + netdev_err(wlandev->netdev, + "FATAL: Card is not an Intersil Prism2/2.5/3\n"); + result = -1; + goto failed; + } + + /* get all the station fw id fields in host byte order */ + le16_to_cpus(&hw->ident_sta_fw.id); + le16_to_cpus(&hw->ident_sta_fw.variant); + le16_to_cpus(&hw->ident_sta_fw.major); + le16_to_cpus(&hw->ident_sta_fw.minor); + + /* strip out the 'special' variant bits */ + hw->mm_mods = hw->ident_sta_fw.variant & GENMASK(15, 14); + hw->ident_sta_fw.variant &= ~((u16)GENMASK(15, 14)); + + if (hw->ident_sta_fw.id == 0x1f) { + netdev_info(wlandev->netdev, + "ident: sta f/w: id=0x%02x %d.%d.%d\n", + hw->ident_sta_fw.id, hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, hw->ident_sta_fw.variant); + } else { + netdev_info(wlandev->netdev, + "ident: ap f/w: id=0x%02x %d.%d.%d\n", + hw->ident_sta_fw.id, hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, hw->ident_sta_fw.variant); + netdev_err(wlandev->netdev, "Unsupported Tertiary AP firmware loaded!\n"); + goto failed; + } + + /* Compatibility range, Modem supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_MFISUPRANGE, + &hw->cap_sup_mfi, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve MFISUPRANGE\n"); + goto failed; + } + + /* get all the Compatibility range, modem interface supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_sup_mfi.role); + le16_to_cpus(&hw->cap_sup_mfi.id); + le16_to_cpus(&hw->cap_sup_mfi.variant); + le16_to_cpus(&hw->cap_sup_mfi.bottom); + le16_to_cpus(&hw->cap_sup_mfi.top); + + netdev_info(wlandev->netdev, + "MFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_sup_mfi.role, hw->cap_sup_mfi.id, + hw->cap_sup_mfi.variant, hw->cap_sup_mfi.bottom, + hw->cap_sup_mfi.top); + + /* Compatibility range, Controller supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CFISUPRANGE, + &hw->cap_sup_cfi, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve CFISUPRANGE\n"); + goto failed; + } + + /* get all the Compatibility range, controller interface supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_sup_cfi.role); + le16_to_cpus(&hw->cap_sup_cfi.id); + le16_to_cpus(&hw->cap_sup_cfi.variant); + le16_to_cpus(&hw->cap_sup_cfi.bottom); + le16_to_cpus(&hw->cap_sup_cfi.top); + + netdev_info(wlandev->netdev, + "CFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_sup_cfi.role, hw->cap_sup_cfi.id, + hw->cap_sup_cfi.variant, hw->cap_sup_cfi.bottom, + hw->cap_sup_cfi.top); + + /* Compatibility range, Primary f/w supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRISUPRANGE, + &hw->cap_sup_pri, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve PRISUPRANGE\n"); + goto failed; + } + + /* get all the Compatibility range, primary firmware supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_sup_pri.role); + le16_to_cpus(&hw->cap_sup_pri.id); + le16_to_cpus(&hw->cap_sup_pri.variant); + le16_to_cpus(&hw->cap_sup_pri.bottom); + le16_to_cpus(&hw->cap_sup_pri.top); + + netdev_info(wlandev->netdev, + "PRI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_sup_pri.role, hw->cap_sup_pri.id, + hw->cap_sup_pri.variant, hw->cap_sup_pri.bottom, + hw->cap_sup_pri.top); + + /* Compatibility range, Station f/w supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STASUPRANGE, + &hw->cap_sup_sta, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve STASUPRANGE\n"); + goto failed; + } + + /* get all the Compatibility range, station firmware supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_sup_sta.role); + le16_to_cpus(&hw->cap_sup_sta.id); + le16_to_cpus(&hw->cap_sup_sta.variant); + le16_to_cpus(&hw->cap_sup_sta.bottom); + le16_to_cpus(&hw->cap_sup_sta.top); + + if (hw->cap_sup_sta.id == 0x04) { + netdev_info(wlandev->netdev, + "STA:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_sup_sta.role, hw->cap_sup_sta.id, + hw->cap_sup_sta.variant, hw->cap_sup_sta.bottom, + hw->cap_sup_sta.top); + } else { + netdev_info(wlandev->netdev, + "AP:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_sup_sta.role, hw->cap_sup_sta.id, + hw->cap_sup_sta.variant, hw->cap_sup_sta.bottom, + hw->cap_sup_sta.top); + } + + /* Compatibility range, primary f/w actor, CFI supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRI_CFIACTRANGES, + &hw->cap_act_pri_cfi, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve PRI_CFIACTRANGES\n"); + goto failed; + } + + /* get all the Compatibility range, primary f/w actor, CFI supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_act_pri_cfi.role); + le16_to_cpus(&hw->cap_act_pri_cfi.id); + le16_to_cpus(&hw->cap_act_pri_cfi.variant); + le16_to_cpus(&hw->cap_act_pri_cfi.bottom); + le16_to_cpus(&hw->cap_act_pri_cfi.top); + + netdev_info(wlandev->netdev, + "PRI-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_act_pri_cfi.role, hw->cap_act_pri_cfi.id, + hw->cap_act_pri_cfi.variant, hw->cap_act_pri_cfi.bottom, + hw->cap_act_pri_cfi.top); + + /* Compatibility range, sta f/w actor, CFI supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_CFIACTRANGES, + &hw->cap_act_sta_cfi, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve STA_CFIACTRANGES\n"); + goto failed; + } + + /* get all the Compatibility range, station f/w actor, CFI supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_act_sta_cfi.role); + le16_to_cpus(&hw->cap_act_sta_cfi.id); + le16_to_cpus(&hw->cap_act_sta_cfi.variant); + le16_to_cpus(&hw->cap_act_sta_cfi.bottom); + le16_to_cpus(&hw->cap_act_sta_cfi.top); + + netdev_info(wlandev->netdev, + "STA-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_act_sta_cfi.role, hw->cap_act_sta_cfi.id, + hw->cap_act_sta_cfi.variant, hw->cap_act_sta_cfi.bottom, + hw->cap_act_sta_cfi.top); + + /* Compatibility range, sta f/w actor, MFI supplier */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_MFIACTRANGES, + &hw->cap_act_sta_mfi, + sizeof(struct hfa384x_caplevel)); + if (result) { + netdev_err(wlandev->netdev, "Failed to retrieve STA_MFIACTRANGES\n"); + goto failed; + } + + /* get all the Compatibility range, station f/w actor, MFI supplier + * fields in byte order + */ + le16_to_cpus(&hw->cap_act_sta_mfi.role); + le16_to_cpus(&hw->cap_act_sta_mfi.id); + le16_to_cpus(&hw->cap_act_sta_mfi.variant); + le16_to_cpus(&hw->cap_act_sta_mfi.bottom); + le16_to_cpus(&hw->cap_act_sta_mfi.top); + + netdev_info(wlandev->netdev, + "STA-MFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", + hw->cap_act_sta_mfi.role, hw->cap_act_sta_mfi.id, + hw->cap_act_sta_mfi.variant, hw->cap_act_sta_mfi.bottom, + hw->cap_act_sta_mfi.top); + + /* Serial Number */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICSERIALNUMBER, + snum, HFA384x_RID_NICSERIALNUMBER_LEN); + if (!result) { + netdev_info(wlandev->netdev, "Prism2 card SN: %*pE\n", + HFA384x_RID_NICSERIALNUMBER_LEN, snum); + } else { + netdev_err(wlandev->netdev, "Failed to retrieve Prism2 Card SN\n"); + goto failed; + } + + /* Collect the MAC address */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CNFOWNMACADDR, + addr, ETH_ALEN); + if (result != 0) { + netdev_err(wlandev->netdev, "Failed to retrieve mac address\n"); + goto failed; + } + eth_hw_addr_set(wlandev->netdev, addr); + + /* short preamble is always implemented */ + wlandev->nsdcaps |= P80211_NSDCAP_SHORT_PREAMBLE; + + /* find out if hardware wep is implemented */ + hfa384x_drvr_getconfig16(hw, HFA384x_RID_PRIVACYOPTIMP, &temp); + if (temp) + wlandev->nsdcaps |= P80211_NSDCAP_HARDWAREWEP; + + /* get the dBm Scaling constant */ + hfa384x_drvr_getconfig16(hw, HFA384x_RID_CNFDBMADJUST, &temp); + hw->dbmadjust = temp; + + /* Only enable scan by default on newer firmware */ + if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major, + hw->ident_sta_fw.minor, + hw->ident_sta_fw.variant) < + HFA384x_FIRMWARE_VERSION(1, 5, 5)) { + wlandev->nsdcaps |= P80211_NSDCAP_NOSCAN; + } + + /* TODO: Set any internally managed config items */ + + goto done; +failed: + netdev_err(wlandev->netdev, "Failed, result=%d\n", result); +done: + return result; +} + +/* + * prism2sta_globalsetup + * + * Set any global RIDs that we want to set at device activation. + * + * Arguments: + * wlandev wlan device structure + * + * Returns: + * 0 success + * >0 f/w reported error + * <0 driver reported error + * + * Side effects: + * + * Call context: + * process thread + */ +static int prism2sta_globalsetup(struct wlandevice *wlandev) +{ + struct hfa384x *hw = wlandev->priv; + + /* Set the maximum frame size */ + return hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, + WLAN_DATA_MAXLEN); +} + +static int prism2sta_setmulticast(struct wlandevice *wlandev, + struct net_device *dev) +{ + int result = 0; + struct hfa384x *hw = wlandev->priv; + + u16 promisc; + + /* If we're not ready, what's the point? */ + if (hw->state != HFA384x_STATE_RUNNING) + goto exit; + + if ((dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) + promisc = P80211ENUM_truth_true; + else + promisc = P80211ENUM_truth_false; + + result = + hfa384x_drvr_setconfig16_async(hw, HFA384x_RID_PROMISCMODE, + promisc); +exit: + return result; +} + +/* + * prism2sta_inf_handover + * + * Handles the receipt of a Handover info frame. Should only be present + * in APs only. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_handover(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + pr_debug("received infoframe:HANDOVER (unhandled)\n"); +} + +/* + * prism2sta_inf_tallies + * + * Handles the receipt of a CommTallies info frame. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_tallies(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + __le16 *src16; + u32 *dst; + __le32 *src32; + int i; + int cnt; + + /* + * Determine if these are 16-bit or 32-bit tallies, based on the + * record length of the info record. + */ + + cnt = sizeof(struct hfa384x_comm_tallies_32) / sizeof(u32); + if (inf->framelen > 22) { + dst = (u32 *)&hw->tallies; + src32 = (__le32 *)&inf->info.commtallies32; + for (i = 0; i < cnt; i++, dst++, src32++) + *dst += le32_to_cpu(*src32); + } else { + dst = (u32 *)&hw->tallies; + src16 = (__le16 *)&inf->info.commtallies16; + for (i = 0; i < cnt; i++, dst++, src16++) + *dst += le16_to_cpu(*src16); + } +} + +/* + * prism2sta_inf_scanresults + * + * Handles the receipt of a Scan Results info frame. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_scanresults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + int nbss; + struct hfa384x_scan_result *sr = &inf->info.scanresult; + int i; + struct hfa384x_join_request_data joinreq; + int result; + + /* Get the number of results, first in bytes, then in results */ + nbss = (inf->framelen * sizeof(u16)) - + sizeof(inf->infotype) - sizeof(inf->info.scanresult.scanreason); + nbss /= sizeof(struct hfa384x_scan_result_sub); + + /* Print em */ + pr_debug("rx scanresults, reason=%d, nbss=%d:\n", + inf->info.scanresult.scanreason, nbss); + for (i = 0; i < nbss; i++) { + pr_debug("chid=%d anl=%d sl=%d bcnint=%d\n", + sr->result[i].chid, + sr->result[i].anl, + sr->result[i].sl, sr->result[i].bcnint); + pr_debug(" capinfo=0x%04x proberesp_rate=%d\n", + sr->result[i].capinfo, sr->result[i].proberesp_rate); + } + /* issue a join request */ + joinreq.channel = sr->result[0].chid; + memcpy(joinreq.bssid, sr->result[0].bssid, WLAN_BSSID_LEN); + result = hfa384x_drvr_setconfig(hw, + HFA384x_RID_JOINREQUEST, + &joinreq, HFA384x_RID_JOINREQUEST_LEN); + if (result) { + netdev_err(wlandev->netdev, "setconfig(joinreq) failed, result=%d\n", + result); + } +} + +/* + * prism2sta_inf_hostscanresults + * + * Handles the receipt of a Scan Results info frame. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_hostscanresults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + int nbss; + + nbss = (inf->framelen - 3) / 32; + pr_debug("Received %d hostscan results\n", nbss); + + if (nbss > 32) + nbss = 32; + + kfree(hw->scanresults); + + hw->scanresults = kmemdup(inf, sizeof(*inf), GFP_ATOMIC); + + if (nbss == 0) + nbss = -1; + + /* Notify/wake the sleeping caller. */ + hw->scanflag = nbss; + wake_up_interruptible(&hw->cmdq); +}; + +/* + * prism2sta_inf_chinforesults + * + * Handles the receipt of a Channel Info Results info frame. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_chinforesults(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + unsigned int i, n; + + hw->channel_info.results.scanchannels = + inf->info.chinforesult.scanchannels; + + for (i = 0, n = 0; i < HFA384x_CHINFORESULT_MAX; i++) { + struct hfa384x_ch_info_result_sub *result; + struct hfa384x_ch_info_result_sub *chinforesult; + int chan; + + if (!(hw->channel_info.results.scanchannels & (1 << i))) + continue; + + result = &inf->info.chinforesult.result[n]; + chan = result->chid - 1; + + if (chan < 0 || chan >= HFA384x_CHINFORESULT_MAX) + continue; + + chinforesult = &hw->channel_info.results.result[chan]; + chinforesult->chid = chan; + chinforesult->anl = result->anl; + chinforesult->pnl = result->pnl; + chinforesult->active = result->active; + + pr_debug("chinfo: channel %d, %s level (avg/peak)=%d/%d dB, pcf %d\n", + chan + 1, + (chinforesult->active & HFA384x_CHINFORESULT_BSSACTIVE) + ? "signal" : "noise", + chinforesult->anl, chinforesult->pnl, + (chinforesult->active & HFA384x_CHINFORESULT_PCFACTIVE) + ? 1 : 0); + n++; + } + atomic_set(&hw->channel_info.done, 2); + + hw->channel_info.count = n; +} + +void prism2sta_processing_defer(struct work_struct *data) +{ + struct hfa384x *hw = container_of(data, struct hfa384x, link_bh); + struct wlandevice *wlandev = hw->wlandev; + struct hfa384x_bytestr32 ssid; + int result; + + /* First let's process the auth frames */ + { + struct sk_buff *skb; + struct hfa384x_inf_frame *inf; + + while ((skb = skb_dequeue(&hw->authq))) { + inf = (struct hfa384x_inf_frame *)skb->data; + prism2sta_inf_authreq_defer(wlandev, inf); + } + } + + /* Now let's handle the linkstatus stuff */ + if (hw->link_status == hw->link_status_new) + return; + + hw->link_status = hw->link_status_new; + + switch (hw->link_status) { + case HFA384x_LINK_NOTCONNECTED: + /* I'm currently assuming that this is the initial link + * state. It should only be possible immediately + * following an Enable command. + * Response: + * Block Transmits, Ignore receives of data frames + */ + netif_carrier_off(wlandev->netdev); + + netdev_info(wlandev->netdev, "linkstatus=NOTCONNECTED (unhandled)\n"); + break; + + case HFA384x_LINK_CONNECTED: + /* This one indicates a successful scan/join/auth/assoc. + * When we have the full MLME complement, this event will + * signify successful completion of both mlme_authenticate + * and mlme_associate. State management will get a little + * ugly here. + * Response: + * Indicate authentication and/or association + * Enable Transmits, Receives and pass up data frames + */ + + netif_carrier_on(wlandev->netdev); + + /* If we are joining a specific AP, set our + * state and reset retries + */ + if (hw->join_ap == 1) + hw->join_ap = 2; + hw->join_retries = 60; + + /* Don't call this in monitor mode */ + if (wlandev->netdev->type == ARPHRD_ETHER) { + u16 portstatus; + + netdev_info(wlandev->netdev, "linkstatus=CONNECTED\n"); + + /* For non-usb devices, we can use the sync versions */ + /* Collect the BSSID, and set state to allow tx */ + + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTBSSID, + wlandev->bssid, + WLAN_BSSID_LEN); + if (result) { + pr_debug + ("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTBSSID, result); + return; + } + + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTSSID, + &ssid, sizeof(ssid)); + if (result) { + pr_debug + ("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTSSID, result); + return; + } + prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *)&ssid, + (struct p80211pstrd *)&wlandev->ssid); + + /* Collect the port status */ + result = hfa384x_drvr_getconfig16(hw, + HFA384x_RID_PORTSTATUS, + &portstatus); + if (result) { + pr_debug + ("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_PORTSTATUS, result); + return; + } + wlandev->macmode = + (portstatus == HFA384x_PSTATUS_CONN_IBSS) ? + WLAN_MACMODE_IBSS_STA : WLAN_MACMODE_ESS_STA; + + /* signal back up to cfg80211 layer */ + prism2_connect_result(wlandev, P80211ENUM_truth_false); + + /* Get the ball rolling on the comms quality stuff */ + prism2sta_commsqual_defer(&hw->commsqual_bh); + } + break; + + case HFA384x_LINK_DISCONNECTED: + /* This one indicates that our association is gone. We've + * lost connection with the AP and/or been disassociated. + * This indicates that the MAC has completely cleared it's + * associated state. We * should send a deauth indication + * (implying disassoc) up * to the MLME. + * Response: + * Indicate Deauthentication + * Block Transmits, Ignore receives of data frames + */ + if (wlandev->netdev->type == ARPHRD_ETHER) + netdev_info(wlandev->netdev, + "linkstatus=DISCONNECTED (unhandled)\n"); + wlandev->macmode = WLAN_MACMODE_NONE; + + netif_carrier_off(wlandev->netdev); + + /* signal back up to cfg80211 layer */ + prism2_disconnected(wlandev); + + break; + + case HFA384x_LINK_AP_CHANGE: + /* This one indicates that the MAC has decided to and + * successfully completed a change to another AP. We + * should probably implement a reassociation indication + * in response to this one. I'm thinking that the + * p80211 layer needs to be notified in case of + * buffering/queueing issues. User mode also needs to be + * notified so that any BSS dependent elements can be + * updated. + * associated state. We * should send a deauth indication + * (implying disassoc) up * to the MLME. + * Response: + * Indicate Reassociation + * Enable Transmits, Receives and pass up data frames + */ + netdev_info(wlandev->netdev, "linkstatus=AP_CHANGE\n"); + + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTBSSID, + wlandev->bssid, WLAN_BSSID_LEN); + if (result) { + pr_debug("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTBSSID, result); + return; + } + + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTSSID, + &ssid, sizeof(ssid)); + if (result) { + pr_debug("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTSSID, result); + return; + } + prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *)&ssid, + (struct p80211pstrd *)&wlandev->ssid); + + hw->link_status = HFA384x_LINK_CONNECTED; + netif_carrier_on(wlandev->netdev); + + /* signal back up to cfg80211 layer */ + prism2_roamed(wlandev); + + break; + + case HFA384x_LINK_AP_OUTOFRANGE: + /* This one indicates that the MAC has decided that the + * AP is out of range, but hasn't found a better candidate + * so the MAC maintains its "associated" state in case + * we get back in range. We should block transmits and + * receives in this state. Do we need an indication here? + * Probably not since a polling user-mode element would + * get this status from p2PortStatus(FD40). What about + * p80211? + * Response: + * Block Transmits, Ignore receives of data frames + */ + netdev_info(wlandev->netdev, "linkstatus=AP_OUTOFRANGE (unhandled)\n"); + + netif_carrier_off(wlandev->netdev); + + break; + + case HFA384x_LINK_AP_INRANGE: + /* This one indicates that the MAC has decided that the + * AP is back in range. We continue working with our + * existing association. + * Response: + * Enable Transmits, Receives and pass up data frames + */ + netdev_info(wlandev->netdev, "linkstatus=AP_INRANGE\n"); + + hw->link_status = HFA384x_LINK_CONNECTED; + netif_carrier_on(wlandev->netdev); + + break; + + case HFA384x_LINK_ASSOCFAIL: + /* This one is actually a peer to CONNECTED. We've + * requested a join for a given SSID and optionally BSSID. + * We can use this one to indicate authentication and + * association failures. The trick is going to be + * 1) identifying the failure, and 2) state management. + * Response: + * Disable Transmits, Ignore receives of data frames + */ + if (hw->join_ap && --hw->join_retries > 0) { + struct hfa384x_join_request_data joinreq; + + joinreq = hw->joinreq; + /* Send the join request */ + hfa384x_drvr_setconfig(hw, + HFA384x_RID_JOINREQUEST, + &joinreq, + HFA384x_RID_JOINREQUEST_LEN); + netdev_info(wlandev->netdev, + "linkstatus=ASSOCFAIL (re-submitting join)\n"); + } else { + netdev_info(wlandev->netdev, "linkstatus=ASSOCFAIL (unhandled)\n"); + } + + netif_carrier_off(wlandev->netdev); + + /* signal back up to cfg80211 layer */ + prism2_connect_result(wlandev, P80211ENUM_truth_true); + + break; + + default: + /* This is bad, IO port problems? */ + netdev_warn(wlandev->netdev, + "unknown linkstatus=0x%02x\n", hw->link_status); + return; + } + + wlandev->linkstatus = (hw->link_status == HFA384x_LINK_CONNECTED); +} + +/* + * prism2sta_inf_linkstatus + * + * Handles the receipt of a Link Status info frame. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_linkstatus(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + + hw->link_status_new = le16_to_cpu(inf->info.linkstatus.linkstatus); + + schedule_work(&hw->link_bh); +} + +/* + * prism2sta_inf_assocstatus + * + * Handles the receipt of an Association Status info frame. Should + * be present in APs only. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_assocstatus(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + struct hfa384x_assoc_status rec; + int i; + + memcpy(&rec, &inf->info.assocstatus, sizeof(rec)); + le16_to_cpus(&rec.assocstatus); + le16_to_cpus(&rec.reason); + + /* + * Find the address in the list of authenticated stations. + * If it wasn't found, then this address has not been previously + * authenticated and something weird has happened if this is + * anything other than an "authentication failed" message. + * If the address was found, then set the "associated" flag for + * that station, based on whether the station is associating or + * losing its association. Something weird has also happened + * if we find the address in the list of authenticated stations + * but we are getting an "authentication failed" message. + */ + + for (i = 0; i < hw->authlist.cnt; i++) + if (ether_addr_equal(rec.sta_addr, hw->authlist.addr[i])) + break; + + if (i >= hw->authlist.cnt) { + if (rec.assocstatus != HFA384x_ASSOCSTATUS_AUTHFAIL) + netdev_warn(wlandev->netdev, + "assocstatus info frame received for non-authenticated station.\n"); + } else { + hw->authlist.assoc[i] = + (rec.assocstatus == HFA384x_ASSOCSTATUS_STAASSOC || + rec.assocstatus == HFA384x_ASSOCSTATUS_REASSOC); + + if (rec.assocstatus == HFA384x_ASSOCSTATUS_AUTHFAIL) + netdev_warn(wlandev->netdev, + "authfail assocstatus info frame received for authenticated station.\n"); + } +} + +/* + * prism2sta_inf_authreq + * + * Handles the receipt of an Authentication Request info frame. Should + * be present in APs only. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + * + */ +static void prism2sta_inf_authreq(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + struct sk_buff *skb; + + skb = dev_alloc_skb(sizeof(*inf)); + if (skb) { + skb_put(skb, sizeof(*inf)); + memcpy(skb->data, inf, sizeof(*inf)); + skb_queue_tail(&hw->authq, skb); + schedule_work(&hw->link_bh); + } +} + +static void prism2sta_inf_authreq_defer(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + struct hfa384x_authenticate_station_data rec; + + int i, added, result, cnt; + u8 *addr; + + /* + * Build the AuthenticateStation record. Initialize it for denying + * authentication. + */ + + ether_addr_copy(rec.address, inf->info.authreq.sta_addr); + rec.status = cpu_to_le16(P80211ENUM_status_unspec_failure); + + /* + * Authenticate based on the access mode. + */ + + switch (hw->accessmode) { + case WLAN_ACCESS_NONE: + + /* + * Deny all new authentications. However, if a station + * is ALREADY authenticated, then accept it. + */ + + for (i = 0; i < hw->authlist.cnt; i++) + if (ether_addr_equal(rec.address, + hw->authlist.addr[i])) { + rec.status = cpu_to_le16(P80211ENUM_status_successful); + break; + } + + break; + + case WLAN_ACCESS_ALL: + + /* + * Allow all authentications. + */ + + rec.status = cpu_to_le16(P80211ENUM_status_successful); + break; + + case WLAN_ACCESS_ALLOW: + + /* + * Only allow the authentication if the MAC address + * is in the list of allowed addresses. + * + * Since this is the interrupt handler, we may be here + * while the access list is in the middle of being + * updated. Choose the list which is currently okay. + * See "prism2mib_priv_accessallow()" for details. + */ + + if (hw->allow.modify == 0) { + cnt = hw->allow.cnt; + addr = hw->allow.addr[0]; + } else { + cnt = hw->allow.cnt1; + addr = hw->allow.addr1[0]; + } + + for (i = 0; i < cnt; i++, addr += ETH_ALEN) + if (ether_addr_equal(rec.address, addr)) { + rec.status = cpu_to_le16(P80211ENUM_status_successful); + break; + } + + break; + + case WLAN_ACCESS_DENY: + + /* + * Allow the authentication UNLESS the MAC address is + * in the list of denied addresses. + * + * Since this is the interrupt handler, we may be here + * while the access list is in the middle of being + * updated. Choose the list which is currently okay. + * See "prism2mib_priv_accessdeny()" for details. + */ + + if (hw->deny.modify == 0) { + cnt = hw->deny.cnt; + addr = hw->deny.addr[0]; + } else { + cnt = hw->deny.cnt1; + addr = hw->deny.addr1[0]; + } + + rec.status = cpu_to_le16(P80211ENUM_status_successful); + + for (i = 0; i < cnt; i++, addr += ETH_ALEN) + if (ether_addr_equal(rec.address, addr)) { + rec.status = cpu_to_le16(P80211ENUM_status_unspec_failure); + break; + } + + break; + } + + /* + * If the authentication is okay, then add the MAC address to the + * list of authenticated stations. Don't add the address if it + * is already in the list. (802.11b does not seem to disallow + * a station from issuing an authentication request when the + * station is already authenticated. Does this sort of thing + * ever happen? We might as well do the check just in case.) + */ + + added = 0; + + if (rec.status == cpu_to_le16(P80211ENUM_status_successful)) { + for (i = 0; i < hw->authlist.cnt; i++) + if (ether_addr_equal(rec.address, + hw->authlist.addr[i])) + break; + + if (i >= hw->authlist.cnt) { + if (hw->authlist.cnt >= WLAN_AUTH_MAX) { + rec.status = cpu_to_le16(P80211ENUM_status_ap_full); + } else { + ether_addr_copy(hw->authlist.addr[hw->authlist.cnt], + rec.address); + hw->authlist.cnt++; + added = 1; + } + } + } + + /* + * Send back the results of the authentication. If this doesn't work, + * then make sure to remove the address from the authenticated list if + * it was added. + */ + + rec.algorithm = inf->info.authreq.algorithm; + + result = hfa384x_drvr_setconfig(hw, HFA384x_RID_AUTHENTICATESTA, + &rec, sizeof(rec)); + if (result) { + if (added) + hw->authlist.cnt--; + netdev_err(wlandev->netdev, + "setconfig(authenticatestation) failed, result=%d\n", + result); + } +} + +/* + * prism2sta_inf_psusercnt + * + * Handles the receipt of a PowerSaveUserCount info frame. Should + * be present in APs only. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to info frame (contents in hfa384x order) + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +static void prism2sta_inf_psusercnt(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + struct hfa384x *hw = wlandev->priv; + + hw->psusercount = le16_to_cpu(inf->info.psusercnt.usercnt); +} + +/* + * prism2sta_ev_info + * + * Handles the Info event. + * + * Arguments: + * wlandev wlan device structure + * inf ptr to a generic info frame + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +void prism2sta_ev_info(struct wlandevice *wlandev, + struct hfa384x_inf_frame *inf) +{ + le16_to_cpus(&inf->infotype); + /* Dispatch */ + switch (inf->infotype) { + case HFA384x_IT_HANDOVERADDR: + prism2sta_inf_handover(wlandev, inf); + break; + case HFA384x_IT_COMMTALLIES: + prism2sta_inf_tallies(wlandev, inf); + break; + case HFA384x_IT_HOSTSCANRESULTS: + prism2sta_inf_hostscanresults(wlandev, inf); + break; + case HFA384x_IT_SCANRESULTS: + prism2sta_inf_scanresults(wlandev, inf); + break; + case HFA384x_IT_CHINFORESULTS: + prism2sta_inf_chinforesults(wlandev, inf); + break; + case HFA384x_IT_LINKSTATUS: + prism2sta_inf_linkstatus(wlandev, inf); + break; + case HFA384x_IT_ASSOCSTATUS: + prism2sta_inf_assocstatus(wlandev, inf); + break; + case HFA384x_IT_AUTHREQ: + prism2sta_inf_authreq(wlandev, inf); + break; + case HFA384x_IT_PSUSERCNT: + prism2sta_inf_psusercnt(wlandev, inf); + break; + case HFA384x_IT_KEYIDCHANGED: + netdev_warn(wlandev->netdev, "Unhandled IT_KEYIDCHANGED\n"); + break; + case HFA384x_IT_ASSOCREQ: + netdev_warn(wlandev->netdev, "Unhandled IT_ASSOCREQ\n"); + break; + case HFA384x_IT_MICFAILURE: + netdev_warn(wlandev->netdev, "Unhandled IT_MICFAILURE\n"); + break; + default: + netdev_warn(wlandev->netdev, + "Unknown info type=0x%02x\n", inf->infotype); + break; + } +} + +/* + * prism2sta_ev_txexc + * + * Handles the TxExc event. A Transmit Exception event indicates + * that the MAC's TX process was unsuccessful - so the packet did + * not get transmitted. + * + * Arguments: + * wlandev wlan device structure + * status tx frame status word + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +void prism2sta_ev_txexc(struct wlandevice *wlandev, u16 status) +{ + pr_debug("TxExc status=0x%x.\n", status); +} + +/* + * prism2sta_ev_tx + * + * Handles the Tx event. + * + * Arguments: + * wlandev wlan device structure + * status tx frame status word + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +void prism2sta_ev_tx(struct wlandevice *wlandev, u16 status) +{ + pr_debug("Tx Complete, status=0x%04x\n", status); + /* update linux network stats */ + wlandev->netdev->stats.tx_packets++; +} + +/* + * prism2sta_ev_alloc + * + * Handles the Alloc event. + * + * Arguments: + * wlandev wlan device structure + * + * Returns: + * nothing + * + * Side effects: + * + * Call context: + * interrupt + */ +void prism2sta_ev_alloc(struct wlandevice *wlandev) +{ + netif_wake_queue(wlandev->netdev); +} + +/* + * create_wlan + * + * Called at module init time. This creates the struct wlandevice structure + * and initializes it with relevant bits. + * + * Arguments: + * none + * + * Returns: + * the created struct wlandevice structure. + * + * Side effects: + * also allocates the priv/hw structures. + * + * Call context: + * process thread + * + */ +static struct wlandevice *create_wlan(void) +{ + struct wlandevice *wlandev = NULL; + struct hfa384x *hw = NULL; + + /* Alloc our structures */ + wlandev = kzalloc(sizeof(*wlandev), GFP_KERNEL); + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + + if (!wlandev || !hw) { + kfree(wlandev); + kfree(hw); + return NULL; + } + + /* Initialize the network device object. */ + wlandev->nsdname = dev_info; + wlandev->msdstate = WLAN_MSD_HWPRESENT_PENDING; + wlandev->priv = hw; + wlandev->open = prism2sta_open; + wlandev->close = prism2sta_close; + wlandev->reset = prism2sta_reset; + wlandev->txframe = prism2sta_txframe; + wlandev->mlmerequest = prism2sta_mlmerequest; + wlandev->set_multicast_list = prism2sta_setmulticast; + wlandev->tx_timeout = hfa384x_tx_timeout; + + wlandev->nsdcaps = P80211_NSDCAP_HWFRAGMENT | P80211_NSDCAP_AUTOJOIN; + + /* Initialize the device private data structure. */ + hw->dot11_desired_bss_type = 1; + + return wlandev; +} + +void prism2sta_commsqual_defer(struct work_struct *data) +{ + struct hfa384x *hw = container_of(data, struct hfa384x, commsqual_bh); + struct wlandevice *wlandev = hw->wlandev; + struct hfa384x_bytestr32 ssid; + struct p80211msg_dot11req_mibget msg; + struct p80211item_uint32 *mibitem = (struct p80211item_uint32 *) + &msg.mibattribute.data; + int result = 0; + + if (hw->wlandev->hwremoved) + return; + + /* we don't care if we're in AP mode */ + if ((wlandev->macmode == WLAN_MACMODE_NONE) || + (wlandev->macmode == WLAN_MACMODE_ESS_AP)) { + return; + } + + /* It only makes sense to poll these in non-IBSS */ + if (wlandev->macmode != WLAN_MACMODE_IBSS_STA) { + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_DBMCOMMSQUALITY, + &hw->qual, HFA384x_RID_DBMCOMMSQUALITY_LEN); + + if (result) { + netdev_err(wlandev->netdev, "error fetching commsqual\n"); + return; + } + + pr_debug("commsqual %d %d %d\n", + le16_to_cpu(hw->qual.cq_curr_bss), + le16_to_cpu(hw->qual.asl_curr_bss), + le16_to_cpu(hw->qual.anl_curr_fc)); + } + + /* Get the signal rate */ + msg.msgcode = DIDMSG_DOT11REQ_MIBGET; + mibitem->did = DIDMIB_P2_MAC_CURRENTTXRATE; + result = p80211req_dorequest(wlandev, (u8 *)&msg); + + if (result) { + pr_debug("get signal rate failed, result = %d\n", + result); + return; + } + + switch (mibitem->data) { + case HFA384x_RATEBIT_1: + hw->txrate = 10; + break; + case HFA384x_RATEBIT_2: + hw->txrate = 20; + break; + case HFA384x_RATEBIT_5dot5: + hw->txrate = 55; + break; + case HFA384x_RATEBIT_11: + hw->txrate = 110; + break; + default: + pr_debug("Bad ratebit (%d)\n", mibitem->data); + } + + /* Lastly, we need to make sure the BSSID didn't change on us */ + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTBSSID, + wlandev->bssid, WLAN_BSSID_LEN); + if (result) { + pr_debug("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTBSSID, result); + return; + } + + result = hfa384x_drvr_getconfig(hw, + HFA384x_RID_CURRENTSSID, + &ssid, sizeof(ssid)); + if (result) { + pr_debug("getconfig(0x%02x) failed, result = %d\n", + HFA384x_RID_CURRENTSSID, result); + return; + } + prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *)&ssid, + (struct p80211pstrd *)&wlandev->ssid); + + /* Reschedule timer */ + mod_timer(&hw->commsqual_timer, jiffies + HZ); +} + +void prism2sta_commsqual_timer(struct timer_list *t) +{ + struct hfa384x *hw = from_timer(hw, t, commsqual_timer); + + schedule_work(&hw->commsqual_bh); +} diff --git a/drivers/staging/wlan-ng/prism2usb.c b/drivers/staging/wlan-ng/prism2usb.c new file mode 100644 index 0000000000..0e0ccef487 --- /dev/null +++ b/drivers/staging/wlan-ng/prism2usb.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "hfa384x_usb.c" +#include "prism2mgmt.c" +#include "prism2mib.c" +#include "prism2sta.c" +#include "prism2fw.c" + +#define PRISM_DEV(vid, pid, name) \ + { USB_DEVICE(vid, pid), \ + .driver_info = (unsigned long)name } + +static const struct usb_device_id usb_prism_tbl[] = { + PRISM_DEV(0x04bb, 0x0922, "IOData AirPort WN-B11/USBS"), + PRISM_DEV(0x07aa, 0x0012, "Corega USB Wireless LAN Stick-11"), + PRISM_DEV(0x09aa, 0x3642, "Prism2.x 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x1668, 0x0408, "Actiontec Prism2.5 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x1668, 0x0421, "Actiontec Prism2.5 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x1915, 0x2236, "Linksys WUSB11v3.0 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x066b, 0x2212, "Linksys WUSB11v2.5 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x066b, 0x2213, "Linksys WUSB12v1.1 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x0411, 0x0016, "Melco WLI-USB-S11 11Mbps WLAN Adapter"), + PRISM_DEV(0x08de, 0x7a01, "PRISM25 USB IEEE 802.11 Mini Adapter"), + PRISM_DEV(0x8086, 0x1111, "Intel PRO/Wireless 2011B USB LAN Adapter"), + PRISM_DEV(0x0d8e, 0x7a01, "PRISM25 IEEE 802.11 Mini USB Adapter"), + PRISM_DEV(0x045e, 0x006e, "Microsoft MN510 USB Wireless Adapter"), + PRISM_DEV(0x0967, 0x0204, "Acer Warplink USB Adapter"), + PRISM_DEV(0x0cde, 0x0002, "Z-Com 725/726 Prism2.5 USB/USB Integrated"), + PRISM_DEV(0x0cde, 0x0005, "Z-Com Xl735 USB Wireless 802.11b Adapter"), + PRISM_DEV(0x413c, 0x8100, "Dell TrueMobile 1180 USB Wireless Adapter"), + PRISM_DEV(0x0b3b, 0x1601, "ALLNET 0193 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x0b3b, 0x1602, "ZyXEL ZyAIR B200 USB Wireless Adapter"), + PRISM_DEV(0x0baf, 0x00eb, "USRobotics USR1120 USB Wireless Adapter"), + PRISM_DEV(0x0411, 0x0027, "Melco WLI-USB-KS11G 11Mbps WLAN Adapter"), + PRISM_DEV(0x04f1, 0x3009, "JVC MP-XP7250 Builtin USB WLAN Adapter"), + PRISM_DEV(0x0846, 0x4110, "NetGear MA111"), + PRISM_DEV(0x03f3, 0x0020, "Adaptec AWN-8020 USB WLAN Adapter"), + PRISM_DEV(0x2821, 0x3300, "ASUS-WL140 / Hawking HighDB USB Wireless Adapter"), + PRISM_DEV(0x2001, 0x3700, "DWL-122 USB Wireless Adapter"), + PRISM_DEV(0x2001, 0x3702, "DWL-120 Rev F USB Wireless Adapter"), + PRISM_DEV(0x50c2, 0x4013, "Averatec USB WLAN Adapter"), + PRISM_DEV(0x2c02, 0x14ea, "Planex GW-US11H USB WLAN Adapter"), + PRISM_DEV(0x124a, 0x168b, "Airvast PRISM3 USB WLAN Adapter"), + PRISM_DEV(0x083a, 0x3503, "T-Sinus 111 USB WLAN Adapter"), + PRISM_DEV(0x0411, 0x0044, "Melco WLI-USB-KB11 11Mbps WLAN Adapter"), + PRISM_DEV(0x1668, 0x6106, "ROPEX FreeLan USB 802.11b Adapter"), + PRISM_DEV(0x124a, 0x4017, "Pheenet WL-503IA USB 802.11b Adapter"), + PRISM_DEV(0x0bb2, 0x0302, "Ambit Microsystems Corp."), + PRISM_DEV(0x9016, 0x182d, "Sitecom WL-022 USB 802.11b Adapter"), + PRISM_DEV(0x0543, 0x0f01, + "ViewSonic Airsync USB Adapter 11Mbps (Prism2.5)"), + PRISM_DEV(0x067c, 0x1022, + "Siemens SpeedStream 1022 11Mbps USB WLAN Adapter"), + PRISM_DEV(0x049f, 0x0033, + "Compaq/Intel W100 PRO/Wireless 11Mbps multiport WLAN Adapter"), + { } /* terminator */ +}; +MODULE_DEVICE_TABLE(usb, usb_prism_tbl); + +static int prism2sta_probe_usb(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev; + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct usb_host_interface *iface_desc = interface->cur_altsetting; + struct wlandevice *wlandev = NULL; + struct hfa384x *hw = NULL; + int result = 0; + + result = usb_find_common_endpoints(iface_desc, &bulk_in, &bulk_out, NULL, NULL); + if (result) + goto failed; + + dev = interface_to_usbdev(interface); + wlandev = create_wlan(); + if (!wlandev) { + dev_err(&interface->dev, "Memory allocation failure.\n"); + result = -EIO; + goto failed; + } + hw = wlandev->priv; + + if (wlan_setup(wlandev, &interface->dev) != 0) { + dev_err(&interface->dev, "wlan_setup() failed.\n"); + result = -EIO; + goto failed; + } + + /* Initialize the hw data */ + hw->endp_in = usb_rcvbulkpipe(dev, bulk_in->bEndpointAddress); + hw->endp_out = usb_sndbulkpipe(dev, bulk_out->bEndpointAddress); + hfa384x_create(hw, dev); + hw->wlandev = wlandev; + + /* Register the wlandev, this gets us a name and registers the + * linux netdevice. + */ + SET_NETDEV_DEV(wlandev->netdev, &interface->dev); + + /* Do a chip-level reset on the MAC */ + if (prism2_doreset) { + result = hfa384x_corereset(hw, + prism2_reset_holdtime, + prism2_reset_settletime, 0); + if (result != 0) { + result = -EIO; + dev_err(&interface->dev, + "hfa384x_corereset() failed.\n"); + goto failed_reset; + } + } + + usb_get_dev(dev); + + wlandev->msdstate = WLAN_MSD_HWPRESENT; + + /* Try and load firmware, then enable card before we register */ + prism2_fwtry(dev, wlandev); + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable); + + if (register_wlandev(wlandev) != 0) { + dev_err(&interface->dev, "register_wlandev() failed.\n"); + result = -EIO; + goto failed_register; + } + + goto done; + +failed_register: + usb_put_dev(dev); +failed_reset: + wlan_unsetup(wlandev); +failed: + kfree(wlandev); + kfree(hw); + wlandev = NULL; + +done: + usb_set_intfdata(interface, wlandev); + return result; +} + +static void prism2sta_disconnect_usb(struct usb_interface *interface) +{ + struct wlandevice *wlandev; + + wlandev = usb_get_intfdata(interface); + if (wlandev) { + LIST_HEAD(cleanlist); + struct hfa384x_usbctlx *ctlx, *temp; + unsigned long flags; + + struct hfa384x *hw = wlandev->priv; + + if (!hw) + goto exit; + + spin_lock_irqsave(&hw->ctlxq.lock, flags); + + p80211netdev_hwremoved(wlandev); + list_splice_init(&hw->ctlxq.reapable, &cleanlist); + list_splice_init(&hw->ctlxq.completing, &cleanlist); + list_splice_init(&hw->ctlxq.pending, &cleanlist); + list_splice_init(&hw->ctlxq.active, &cleanlist); + + spin_unlock_irqrestore(&hw->ctlxq.lock, flags); + + /* There's no hardware to shutdown, but the driver + * might have some tasks that must be stopped before + * we can tear everything down. + */ + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable); + + timer_shutdown_sync(&hw->throttle); + timer_shutdown_sync(&hw->reqtimer); + timer_shutdown_sync(&hw->resptimer); + + /* Unlink all the URBs. This "removes the wheels" + * from the entire CTLX handling mechanism. + */ + usb_kill_urb(&hw->rx_urb); + usb_kill_urb(&hw->tx_urb); + usb_kill_urb(&hw->ctlx_urb); + + cancel_work_sync(&hw->completion_bh); + cancel_work_sync(&hw->reaper_bh); + + cancel_work_sync(&hw->link_bh); + cancel_work_sync(&hw->commsqual_bh); + cancel_work_sync(&hw->usb_work); + + /* Now we complete any outstanding commands + * and tell everyone who is waiting for their + * responses that we have shut down. + */ + list_for_each_entry(ctlx, &cleanlist, list) + complete(&ctlx->done); + + /* Give any outstanding synchronous commands + * a chance to complete. All they need to do + * is "wake up", so that's easy. + * (I'd like a better way to do this, really.) + */ + msleep(100); + + /* Now delete the CTLXs, because no-one else can now. */ + list_for_each_entry_safe(ctlx, temp, &cleanlist, list) + kfree(ctlx); + + /* Unhook the wlandev */ + unregister_wlandev(wlandev); + wlan_unsetup(wlandev); + + usb_put_dev(hw->usb); + + hfa384x_destroy(hw); + kfree(hw); + + kfree(wlandev); + } + +exit: + usb_set_intfdata(interface, NULL); +} + +#ifdef CONFIG_PM +static int prism2sta_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct hfa384x *hw = NULL; + struct wlandevice *wlandev; + + wlandev = usb_get_intfdata(interface); + if (!wlandev) + return -ENODEV; + + hw = wlandev->priv; + if (!hw) + return -ENODEV; + + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable); + + usb_kill_urb(&hw->rx_urb); + usb_kill_urb(&hw->tx_urb); + usb_kill_urb(&hw->ctlx_urb); + + return 0; +} + +static int prism2sta_resume(struct usb_interface *interface) +{ + int result = 0; + struct hfa384x *hw = NULL; + struct wlandevice *wlandev; + + wlandev = usb_get_intfdata(interface); + if (!wlandev) + return -ENODEV; + + hw = wlandev->priv; + if (!hw) + return -ENODEV; + + /* Do a chip-level reset on the MAC */ + if (prism2_doreset) { + result = hfa384x_corereset(hw, + prism2_reset_holdtime, + prism2_reset_settletime, 0); + if (result != 0) { + unregister_wlandev(wlandev); + hfa384x_destroy(hw); + dev_err(&interface->dev, "hfa384x_corereset() failed.\n"); + kfree(wlandev); + kfree(hw); + wlandev = NULL; + return -ENODEV; + } + } + + prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable); + + return 0; +} +#else +#define prism2sta_suspend NULL +#define prism2sta_resume NULL +#endif /* CONFIG_PM */ + +static struct usb_driver prism2_usb_driver = { + .name = "prism2_usb", + .probe = prism2sta_probe_usb, + .disconnect = prism2sta_disconnect_usb, + .id_table = usb_prism_tbl, + .suspend = prism2sta_suspend, + .resume = prism2sta_resume, + .reset_resume = prism2sta_resume, + /* fops, minor? */ +}; + +module_usb_driver(prism2_usb_driver); -- cgit v1.2.3