1// SPDX-License-Identifier: GPL-2.0
2/* cfg80211 Interface for prism2_usb module */
3#include "hfa384x.h"
4#include "prism2mgmt.h"
5
6/* Prism2 channel/frequency/bitrate declarations */
7static const struct ieee80211_channel prism2_channels[] = {
8	{ .center_freq = 2412 },
9	{ .center_freq = 2417 },
10	{ .center_freq = 2422 },
11	{ .center_freq = 2427 },
12	{ .center_freq = 2432 },
13	{ .center_freq = 2437 },
14	{ .center_freq = 2442 },
15	{ .center_freq = 2447 },
16	{ .center_freq = 2452 },
17	{ .center_freq = 2457 },
18	{ .center_freq = 2462 },
19	{ .center_freq = 2467 },
20	{ .center_freq = 2472 },
21	{ .center_freq = 2484 },
22};
23
24static const struct ieee80211_rate prism2_rates[] = {
25	{ .bitrate = 10 },
26	{ .bitrate = 20 },
27	{ .bitrate = 55 },
28	{ .bitrate = 110 }
29};
30
31#define PRISM2_NUM_CIPHER_SUITES 2
32static const u32 prism2_cipher_suites[PRISM2_NUM_CIPHER_SUITES] = {
33	WLAN_CIPHER_SUITE_WEP40,
34	WLAN_CIPHER_SUITE_WEP104
35};
36
37/* prism2 device private data */
38struct prism2_wiphy_private {
39	struct wlandevice *wlandev;
40
41	struct ieee80211_supported_band band;
42	struct ieee80211_channel channels[ARRAY_SIZE(prism2_channels)];
43	struct ieee80211_rate rates[ARRAY_SIZE(prism2_rates)];
44
45	struct cfg80211_scan_request *scan_request;
46};
47
48static const void * const prism2_wiphy_privid = &prism2_wiphy_privid;
49
50/* Helper Functions */
51static int prism2_result2err(int prism2_result)
52{
53	int err = 0;
54
55	switch (prism2_result) {
56	case P80211ENUM_resultcode_invalid_parameters:
57		err = -EINVAL;
58		break;
59	case P80211ENUM_resultcode_implementation_failure:
60		err = -EIO;
61		break;
62	case P80211ENUM_resultcode_not_supported:
63		err = -EOPNOTSUPP;
64		break;
65	default:
66		err = 0;
67		break;
68	}
69
70	return err;
71}
72
73static int prism2_domibset_uint32(struct wlandevice *wlandev,
74				  u32 did, u32 data)
75{
76	struct p80211msg_dot11req_mibset msg;
77	struct p80211item_uint32 *mibitem =
78			(struct p80211item_uint32 *)&msg.mibattribute.data;
79
80	msg.msgcode = DIDMSG_DOT11REQ_MIBSET;
81	mibitem->did = did;
82	mibitem->data = data;
83
84	return p80211req_dorequest(wlandev, (u8 *)&msg);
85}
86
87static int prism2_domibset_pstr32(struct wlandevice *wlandev,
88				  u32 did, u8 len, const u8 *data)
89{
90	struct p80211msg_dot11req_mibset msg;
91	struct p80211item_pstr32 *mibitem =
92			(struct p80211item_pstr32 *)&msg.mibattribute.data;
93
94	msg.msgcode = DIDMSG_DOT11REQ_MIBSET;
95	mibitem->did = did;
96	mibitem->data.len = len;
97	memcpy(mibitem->data.data, data, len);
98
99	return p80211req_dorequest(wlandev, (u8 *)&msg);
100}
101
102/* The interface functions, called by the cfg80211 layer */
103static int prism2_change_virtual_intf(struct wiphy *wiphy,
104				      struct net_device *dev,
105				      enum nl80211_iftype type,
106				      struct vif_params *params)
107{
108	struct wlandevice *wlandev = dev->ml_priv;
109	u32 data;
110	int result;
111	int err = 0;
112
113	switch (type) {
114	case NL80211_IFTYPE_ADHOC:
115		if (wlandev->macmode == WLAN_MACMODE_IBSS_STA)
116			goto exit;
117		wlandev->macmode = WLAN_MACMODE_IBSS_STA;
118		data = 0;
119		break;
120	case NL80211_IFTYPE_STATION:
121		if (wlandev->macmode == WLAN_MACMODE_ESS_STA)
122			goto exit;
123		wlandev->macmode = WLAN_MACMODE_ESS_STA;
124		data = 1;
125		break;
126	default:
127		netdev_warn(dev, "Operation mode: %d not support\n", type);
128		return -EOPNOTSUPP;
129	}
130
131	/* Set Operation mode to the PORT TYPE RID */
132	result = prism2_domibset_uint32(wlandev,
133					DIDMIB_P2_STATIC_CNFPORTTYPE,
134					data);
135
136	if (result)
137		err = -EFAULT;
138
139	dev->ieee80211_ptr->iftype = type;
140
141exit:
142	return err;
143}
144
145static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev,
146			  int link_id, u8 key_index, bool pairwise,
147			  const u8 *mac_addr, struct key_params *params)
148{
149	struct wlandevice *wlandev = dev->ml_priv;
150	u32 did;
151
152	if (key_index >= NUM_WEPKEYS)
153		return -EINVAL;
154
155	if (params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
156	    params->cipher != WLAN_CIPHER_SUITE_WEP104) {
157		pr_debug("Unsupported cipher suite\n");
158		return -EFAULT;
159	}
160
161	if (prism2_domibset_uint32(wlandev,
162				   DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID,
163				   key_index))
164		return -EFAULT;
165
166	/* send key to driver */
167	did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1);
168
169	if (prism2_domibset_pstr32(wlandev, did, params->key_len, params->key))
170		return -EFAULT;
171	return 0;
172}
173
174static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev,
175			  int link_id, u8 key_index, bool pairwise,
176			  const u8 *mac_addr, void *cookie,
177			  void (*callback)(void *cookie, struct key_params*))
178{
179	struct wlandevice *wlandev = dev->ml_priv;
180	struct key_params params;
181	int len;
182
183	if (key_index >= NUM_WEPKEYS)
184		return -EINVAL;
185
186	len = wlandev->wep_keylens[key_index];
187	memset(&params, 0, sizeof(params));
188
189	if (len == 13)
190		params.cipher = WLAN_CIPHER_SUITE_WEP104;
191	else if (len == 5)
192		params.cipher = WLAN_CIPHER_SUITE_WEP104;
193	else
194		return -ENOENT;
195	params.key_len = len;
196	params.key = wlandev->wep_keys[key_index];
197	params.seq_len = 0;
198
199	callback(cookie, &params);
200
201	return 0;
202}
203
204static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev,
205			  int link_id, u8 key_index, bool pairwise,
206			  const u8 *mac_addr)
207{
208	struct wlandevice *wlandev = dev->ml_priv;
209	u32 did;
210	int err = 0;
211	int result = 0;
212
213	/* There is no direct way in the hardware (AFAIK) of removing
214	 * a key, so we will cheat by setting the key to a bogus value
215	 */
216
217	if (key_index >= NUM_WEPKEYS)
218		return -EINVAL;
219
220	/* send key to driver */
221	did = didmib_dot11smt_wepdefaultkeystable_key(key_index + 1);
222	result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000");
223
224	if (result)
225		err = -EFAULT;
226
227	return err;
228}
229
230static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
231				  int link_id, u8 key_index, bool unicast,
232				  bool multicast)
233{
234	struct wlandevice *wlandev = dev->ml_priv;
235
236	return  prism2_domibset_uint32(wlandev,
237				       DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID,
238				       key_index);
239}
240
241static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
242			      const u8 *mac, struct station_info *sinfo)
243{
244	struct wlandevice *wlandev = dev->ml_priv;
245	struct p80211msg_lnxreq_commsquality quality;
246	int result;
247
248	memset(sinfo, 0, sizeof(*sinfo));
249
250	if (!wlandev || (wlandev->msdstate != WLAN_MSD_RUNNING))
251		return -EOPNOTSUPP;
252
253	/* build request message */
254	quality.msgcode = DIDMSG_LNXREQ_COMMSQUALITY;
255	quality.dbm.data = P80211ENUM_truth_true;
256	quality.dbm.status = P80211ENUM_msgitem_status_data_ok;
257
258	/* send message to nsd */
259	if (!wlandev->mlmerequest)
260		return -EOPNOTSUPP;
261
262	result = wlandev->mlmerequest(wlandev, (struct p80211msg *)&quality);
263
264	if (result == 0) {
265		sinfo->txrate.legacy = quality.txrate.data;
266		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
267		sinfo->signal = quality.level.data;
268		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
269	}
270
271	return result;
272}
273
274static int prism2_scan(struct wiphy *wiphy,
275		       struct cfg80211_scan_request *request)
276{
277	struct net_device *dev;
278	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
279	struct wlandevice *wlandev;
280	struct p80211msg_dot11req_scan msg1;
281	struct p80211msg_dot11req_scan_results *msg2;
282	struct cfg80211_bss *bss;
283	struct cfg80211_scan_info info = {};
284
285	int result;
286	int err = 0;
287	int numbss = 0;
288	int i = 0;
289	u8 ie_buf[46];
290	int ie_len;
291
292	if (!request)
293		return -EINVAL;
294
295	dev = request->wdev->netdev;
296	wlandev = dev->ml_priv;
297
298	if (priv->scan_request && priv->scan_request != request)
299		return -EBUSY;
300
301	if (wlandev->macmode == WLAN_MACMODE_ESS_AP) {
302		netdev_err(dev, "Can't scan in AP mode\n");
303		return -EOPNOTSUPP;
304	}
305
306	msg2 = kzalloc(sizeof(*msg2), GFP_KERNEL);
307	if (!msg2)
308		return -ENOMEM;
309
310	priv->scan_request = request;
311
312	memset(&msg1, 0x00, sizeof(msg1));
313	msg1.msgcode = DIDMSG_DOT11REQ_SCAN;
314	msg1.bsstype.data = P80211ENUM_bsstype_any;
315
316	memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data));
317	msg1.bssid.data.len = 6;
318
319	if (request->n_ssids > 0) {
320		msg1.scantype.data = P80211ENUM_scantype_active;
321		msg1.ssid.data.len = request->ssids->ssid_len;
322		memcpy(msg1.ssid.data.data,
323		       request->ssids->ssid, request->ssids->ssid_len);
324	} else {
325		msg1.scantype.data = 0;
326	}
327	msg1.probedelay.data = 0;
328
329	for (i = 0;
330		(i < request->n_channels) && i < ARRAY_SIZE(prism2_channels);
331		i++)
332		msg1.channellist.data.data[i] =
333			ieee80211_frequency_to_channel(request->channels[i]->center_freq);
334	msg1.channellist.data.len = request->n_channels;
335
336	msg1.maxchanneltime.data = 250;
337	msg1.minchanneltime.data = 200;
338
339	result = p80211req_dorequest(wlandev, (u8 *)&msg1);
340	if (result) {
341		err = prism2_result2err(msg1.resultcode.data);
342		goto exit;
343	}
344	/* Now retrieve scan results */
345	numbss = msg1.numbss.data;
346
347	for (i = 0; i < numbss; i++) {
348		int freq;
349
350		msg2->msgcode = DIDMSG_DOT11REQ_SCAN_RESULTS;
351		msg2->bssindex.data = i;
352
353		result = p80211req_dorequest(wlandev, (u8 *)&msg2);
354		if ((result != 0) ||
355		    (msg2->resultcode.data != P80211ENUM_resultcode_success)) {
356			break;
357		}
358
359		ie_buf[0] = WLAN_EID_SSID;
360		ie_buf[1] = msg2->ssid.data.len;
361		ie_len = ie_buf[1] + 2;
362		memcpy(&ie_buf[2], &msg2->ssid.data.data, msg2->ssid.data.len);
363		freq = ieee80211_channel_to_frequency(msg2->dschannel.data,
364						      NL80211_BAND_2GHZ);
365		bss = cfg80211_inform_bss(wiphy,
366					  ieee80211_get_channel(wiphy, freq),
367					  CFG80211_BSS_FTYPE_UNKNOWN,
368					  (const u8 *)&msg2->bssid.data.data,
369					  msg2->timestamp.data, msg2->capinfo.data,
370					  msg2->beaconperiod.data,
371					  ie_buf,
372					  ie_len,
373					  (msg2->signal.data - 65536) * 100, /* Conversion to signed type */
374					  GFP_KERNEL);
375
376		if (!bss) {
377			err = -ENOMEM;
378			goto exit;
379		}
380
381		cfg80211_put_bss(wiphy, bss);
382	}
383
384	if (result)
385		err = prism2_result2err(msg2->resultcode.data);
386
387exit:
388	info.aborted = !!(err);
389	cfg80211_scan_done(request, &info);
390	priv->scan_request = NULL;
391	kfree(msg2);
392	return err;
393}
394
395static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed)
396{
397	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
398	struct wlandevice *wlandev = priv->wlandev;
399	u32 data;
400	int result;
401	int err = 0;
402
403	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
404		if (wiphy->rts_threshold == -1)
405			data = 2347;
406		else
407			data = wiphy->rts_threshold;
408
409		result = prism2_domibset_uint32(wlandev,
410						DIDMIB_DOT11MAC_OPERATIONTABLE_RTSTHRESHOLD,
411						data);
412		if (result) {
413			err = -EFAULT;
414			goto exit;
415		}
416	}
417
418	if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
419		if (wiphy->frag_threshold == -1)
420			data = 2346;
421		else
422			data = wiphy->frag_threshold;
423
424		result = prism2_domibset_uint32(wlandev,
425						DIDMIB_DOT11MAC_OPERATIONTABLE_FRAGMENTATIONTHRESHOLD,
426						data);
427		if (result) {
428			err = -EFAULT;
429			goto exit;
430		}
431	}
432
433exit:
434	return err;
435}
436
437static int prism2_connect(struct wiphy *wiphy, struct net_device *dev,
438			  struct cfg80211_connect_params *sme)
439{
440	struct wlandevice *wlandev = dev->ml_priv;
441	struct ieee80211_channel *channel = sme->channel;
442	struct p80211msg_lnxreq_autojoin msg_join;
443	u32 did;
444	int length = sme->ssid_len;
445	int chan = -1;
446	int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
447	    (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104);
448	int result;
449	int err = 0;
450
451	/* Set the channel */
452	if (channel) {
453		chan = ieee80211_frequency_to_channel(channel->center_freq);
454		result = prism2_domibset_uint32(wlandev,
455						DIDMIB_DOT11PHY_DSSSTABLE_CURRENTCHANNEL,
456						chan);
457		if (result)
458			goto exit;
459	}
460
461	/* Set the authorization */
462	if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) ||
463	    ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep))
464		msg_join.authtype.data = P80211ENUM_authalg_opensystem;
465	else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) ||
466		 ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep))
467		msg_join.authtype.data = P80211ENUM_authalg_sharedkey;
468	else
469		netdev_warn(dev,
470			    "Unhandled authorisation type for connect (%d)\n",
471			    sme->auth_type);
472
473	/* Set the encryption - we only support wep */
474	if (is_wep) {
475		if (sme->key) {
476			if (sme->key_idx >= NUM_WEPKEYS)
477				return -EINVAL;
478
479			result = prism2_domibset_uint32(wlandev,
480							DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID,
481				sme->key_idx);
482			if (result)
483				goto exit;
484
485			/* send key to driver */
486			did = didmib_dot11smt_wepdefaultkeystable_key(sme->key_idx + 1);
487			result = prism2_domibset_pstr32(wlandev,
488							did, sme->key_len,
489							(u8 *)sme->key);
490			if (result)
491				goto exit;
492		}
493
494		/* Assume we should set privacy invoked and exclude unencrypted
495		 * We could possible use sme->privacy here, but the assumption
496		 * seems reasonable anyways
497		 */
498		result = prism2_domibset_uint32(wlandev,
499						DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED,
500						P80211ENUM_truth_true);
501		if (result)
502			goto exit;
503
504		result = prism2_domibset_uint32(wlandev,
505						DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED,
506						P80211ENUM_truth_true);
507		if (result)
508			goto exit;
509
510	} else {
511		/* Assume we should unset privacy invoked
512		 * and exclude unencrypted
513		 */
514		result = prism2_domibset_uint32(wlandev,
515						DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED,
516						P80211ENUM_truth_false);
517		if (result)
518			goto exit;
519
520		result = prism2_domibset_uint32(wlandev,
521						DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED,
522						P80211ENUM_truth_false);
523		if (result)
524			goto exit;
525	}
526
527	/* Now do the actual join. Note there is no way that I can
528	 * see to request a specific bssid
529	 */
530	msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN;
531
532	memcpy(msg_join.ssid.data.data, sme->ssid, length);
533	msg_join.ssid.data.len = length;
534
535	result = p80211req_dorequest(wlandev, (u8 *)&msg_join);
536
537exit:
538	if (result)
539		err = -EFAULT;
540
541	return err;
542}
543
544static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev,
545			     u16 reason_code)
546{
547	struct wlandevice *wlandev = dev->ml_priv;
548	struct p80211msg_lnxreq_autojoin msg_join;
549	int result;
550	int err = 0;
551
552	/* Do a join, with a bogus ssid. Thats the only way I can think of */
553	msg_join.msgcode = DIDMSG_LNXREQ_AUTOJOIN;
554
555	memcpy(msg_join.ssid.data.data, "---", 3);
556	msg_join.ssid.data.len = 3;
557
558	result = p80211req_dorequest(wlandev, (u8 *)&msg_join);
559
560	if (result)
561		err = -EFAULT;
562
563	return err;
564}
565
566static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev,
567			    struct cfg80211_ibss_params *params)
568{
569	return -EOPNOTSUPP;
570}
571
572static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
573{
574	return -EOPNOTSUPP;
575}
576
577static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
578			       enum nl80211_tx_power_setting type, int mbm)
579{
580	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
581	struct wlandevice *wlandev = priv->wlandev;
582	u32 data;
583	int result;
584	int err = 0;
585
586	if (type == NL80211_TX_POWER_AUTOMATIC)
587		data = 30;
588	else
589		data = MBM_TO_DBM(mbm);
590
591	result = prism2_domibset_uint32(wlandev,
592					DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL,
593		data);
594
595	if (result) {
596		err = -EFAULT;
597		goto exit;
598	}
599
600exit:
601	return err;
602}
603
604static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
605			       int *dbm)
606{
607	struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
608	struct wlandevice *wlandev = priv->wlandev;
609	struct p80211msg_dot11req_mibget msg;
610	struct p80211item_uint32 *mibitem;
611	int result;
612	int err = 0;
613
614	mibitem = (struct p80211item_uint32 *)&msg.mibattribute.data;
615	msg.msgcode = DIDMSG_DOT11REQ_MIBGET;
616	mibitem->did = DIDMIB_DOT11PHY_TXPOWERTABLE_CURRENTTXPOWERLEVEL;
617
618	result = p80211req_dorequest(wlandev, (u8 *)&msg);
619
620	if (result) {
621		err = -EFAULT;
622		goto exit;
623	}
624
625	*dbm = mibitem->data;
626
627exit:
628	return err;
629}
630
631/* Interface callback functions, passing data back up to the cfg80211 layer */
632void prism2_connect_result(struct wlandevice *wlandev, u8 failed)
633{
634	u16 status = failed ?
635		     WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS;
636
637	cfg80211_connect_result(wlandev->netdev, wlandev->bssid,
638				NULL, 0, NULL, 0, status, GFP_KERNEL);
639}
640
641void prism2_disconnected(struct wlandevice *wlandev)
642{
643	cfg80211_disconnected(wlandev->netdev, 0, NULL,
644			      0, false, GFP_KERNEL);
645}
646
647void prism2_roamed(struct wlandevice *wlandev)
648{
649	struct cfg80211_roam_info roam_info = {
650		.links[0].bssid = wlandev->bssid,
651	};
652
653	cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL);
654}
655
656/* Structures for declaring wiphy interface */
657static const struct cfg80211_ops prism2_usb_cfg_ops = {
658	.change_virtual_intf = prism2_change_virtual_intf,
659	.add_key = prism2_add_key,
660	.get_key = prism2_get_key,
661	.del_key = prism2_del_key,
662	.set_default_key = prism2_set_default_key,
663	.get_station = prism2_get_station,
664	.scan = prism2_scan,
665	.set_wiphy_params = prism2_set_wiphy_params,
666	.connect = prism2_connect,
667	.disconnect = prism2_disconnect,
668	.join_ibss = prism2_join_ibss,
669	.leave_ibss = prism2_leave_ibss,
670	.set_tx_power = prism2_set_tx_power,
671	.get_tx_power = prism2_get_tx_power,
672};
673
674/* Functions to create/free wiphy interface */
675static struct wiphy *wlan_create_wiphy(struct device *dev,
676				       struct wlandevice *wlandev)
677{
678	struct wiphy *wiphy;
679	struct prism2_wiphy_private *priv;
680
681	wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv));
682	if (!wiphy)
683		return NULL;
684
685	priv = wiphy_priv(wiphy);
686	priv->wlandev = wlandev;
687	memcpy(priv->channels, prism2_channels, sizeof(prism2_channels));
688	memcpy(priv->rates, prism2_rates, sizeof(prism2_rates));
689	priv->band.channels = priv->channels;
690	priv->band.n_channels = ARRAY_SIZE(prism2_channels);
691	priv->band.bitrates = priv->rates;
692	priv->band.n_bitrates = ARRAY_SIZE(prism2_rates);
693	priv->band.band = NL80211_BAND_2GHZ;
694	priv->band.ht_cap.ht_supported = false;
695	wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
696
697	set_wiphy_dev(wiphy, dev);
698	wiphy->privid = prism2_wiphy_privid;
699	wiphy->max_scan_ssids = 1;
700	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
701				 | BIT(NL80211_IFTYPE_ADHOC);
702	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
703	wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES;
704	wiphy->cipher_suites = prism2_cipher_suites;
705
706	if (wiphy_register(wiphy) < 0) {
707		wiphy_free(wiphy);
708		return NULL;
709	}
710
711	return wiphy;
712}
713
714static void wlan_free_wiphy(struct wiphy *wiphy)
715{
716	wiphy_unregister(wiphy);
717	wiphy_free(wiphy);
718}
719