123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- // 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_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)
- {
- 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_dot11PrivacyTable_dot11WEPDefaultKeyID,
- key_index))
- return -EFAULT;
- /* send key to driver */
- did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_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,
- 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,
- 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_dot11WEPDefaultKeysTable_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,
- u8 key_index, bool unicast, bool multicast)
- {
- struct wlandevice *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)
- {
- 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;
- }
- 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;
- 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,
- 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;
- 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_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)
- {
- 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_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) {
- if (sme->key_idx >= NUM_WEPKEYS)
- return -EINVAL;
- result = prism2_domibset_uint32(wlandev,
- DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
- sme->key_idx);
- if (result)
- goto exit;
- /* send key to driver */
- did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_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_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)
- {
- 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_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);
- 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_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(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 = {
- .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);
- }
|