diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/staging/wlan-ng/cfg80211.c |
Initial import
Diffstat (limited to 'drivers/staging/wlan-ng/cfg80211.c')
-rw-r--r-- | drivers/staging/wlan-ng/cfg80211.c | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c new file mode 100644 index 000000000..7c87aecf4 --- /dev/null +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -0,0 +1,796 @@ +/* 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 { + wlandevice_t *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(wlandevice_t *wlandev, u32 did, u32 data) +{ + struct p80211msg_dot11req_mibset msg; + p80211item_uint32_t *mibitem = + (p80211item_uint32_t *) &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(wlandevice_t *wlandev, + u32 did, u8 len, const u8 *data) +{ + struct p80211msg_dot11req_mibset msg; + p80211item_pstr32_t *mibitem = + (p80211item_pstr32_t *) &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, u32 *flags, + struct vif_params *params) +{ + wlandevice_t *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_p2Static_p2CnfPortType, + 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, + u8 key_index, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + wlandevice_t *wlandev = dev->ml_priv; + u32 did; + + int err = 0; + int result = 0; + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, + key_index); + if (result) + goto exit; + + /* send key to driver */ + switch (key_index) { + case 0: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; + break; + + case 1: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; + break; + + case 2: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; + break; + + case 3: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; + break; + + default: + err = -EINVAL; + goto exit; + } + + result = prism2_domibset_pstr32(wlandev, did, + params->key_len, params->key); + if (result) + goto exit; + break; + + default: + pr_debug("Unsupported cipher suite\n"); + result = 1; + } + +exit: + if (result) + err = -EFAULT; + + return err; +} + +static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_index, bool pairwise, + const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, struct key_params*)) +{ + wlandevice_t *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, + u8 key_index, bool pairwise, const u8 *mac_addr) +{ + wlandevice_t *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 */ + /* send key to driver */ + switch (key_index) { + case 0: + did = + DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; + break; + + case 1: + did = + DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; + break; + + case 2: + did = + DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; + break; + + case 3: + did = + DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; + break; + + default: + err = -EINVAL; + goto exit; + } + + result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000"); + +exit: + if (result) + err = -EFAULT; + + return err; +} + +static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_index, bool unicast, bool multicast) +{ + wlandevice_t *wlandev = dev->ml_priv; + + int err = 0; + int result = 0; + + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, + key_index); + + if (result) + err = -EFAULT; + + return err; +} + + +static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + wlandevice_t *wlandev = dev->ml_priv; + struct p80211msg_lnxreq_commsquality quality; + int result; + + memset(sinfo, 0, sizeof(*sinfo)); + + if ((wlandev == NULL) || (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 == NULL) + return -EOPNOTSUPP; + + result = wlandev->mlmerequest(wlandev, (struct p80211msg *) &quality); + + + if (result == 0) { + sinfo->txrate.legacy = quality.txrate.data; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + sinfo->signal = quality.level.data; + sinfo->filled |= BIT(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); + wlandevice_t *wlandev; + struct p80211msg_dot11req_scan msg1; + struct p80211msg_dot11req_scan_results msg2; + struct cfg80211_bss *bss; + 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; + } + + priv->scan_request = request; + + memset(&msg1, 0x00, sizeof(struct p80211msg_dot11req_scan)); + 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; + + memset(&msg2, 0, sizeof(msg2)); + 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, + IEEE80211_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: + cfg80211_scan_done(request, err ? 1 : 0); + priv->scan_request = NULL; + return err; +} + +static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct prism2_wiphy_private *priv = wiphy_priv(wiphy); + wlandevice_t *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_dot11OperationTable_dot11RTSThreshold, + 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_dot11OperationTable_dot11FragmentationThreshold, + 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) +{ + wlandevice_t *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_dot11PhyDSSSTable_dot11CurrentChannel, + 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) { + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, + sme->key_idx); + if (result) + goto exit; + + /* send key to driver */ + switch (sme->key_idx) { + case 0: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; + break; + + case 1: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; + break; + + case 2: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; + break; + + case 3: + did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; + break; + + default: + err = -EINVAL; + goto exit; + } + + 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 possibly use sme->privacy here, but the assumption + seems reasonable anyway */ + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked, + P80211ENUM_truth_true); + if (result) + goto exit; + + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted, + P80211ENUM_truth_true); + if (result) + goto exit; + + } else { + /* Assume we should unset privacy invoked + and exclude unencrypted */ + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked, + P80211ENUM_truth_false); + if (result) + goto exit; + + result = prism2_domibset_uint32(wlandev, + DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted, + 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) +{ + wlandevice_t *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); + wlandevice_t *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_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel, + 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); + wlandevice_t *wlandev = priv->wlandev; + struct p80211msg_dot11req_mibget msg; + p80211item_uint32_t *mibitem; + int result; + int err = 0; + + mibitem = (p80211item_uint32_t *) &msg.mibattribute.data; + msg.msgcode = DIDmsg_dot11req_mibget; + mibitem->did = + DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel; + + 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(wlandevice_t *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(wlandevice_t *wlandev) +{ + cfg80211_disconnected(wlandev->netdev, 0, NULL, + 0, GFP_KERNEL); +} + +void prism2_roamed(wlandevice_t *wlandev) +{ + cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid, + NULL, 0, NULL, 0, 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, wlandevice_t *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 = IEEE80211_BAND_2GHZ; + priv->band.ht_cap.ht_supported = false; + wiphy->bands[IEEE80211_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) + return NULL; + + return wiphy; +} + + +static void wlan_free_wiphy(struct wiphy *wiphy) +{ + wiphy_unregister(wiphy); + wiphy_free(wiphy); +} |