/* 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 |= STATION_INFO_TX_BITRATE; sinfo->signal = quality.level.data; sinfo->filled |= STATION_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), (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); }