1/*	$OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2014 genua mbh <info@genua.de>
5 * Copyright (c) 2014 Fixup Software Ltd.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/*-
21 * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22 * which were used as the reference documentation for this implementation.
23 *
24 * Driver version we are currently based off of is
25 * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
26 *
27 ***********************************************************************
28 *
29 * This file is provided under a dual BSD/GPLv2 license.  When using or
30 * redistributing this file, you may do so under either license.
31 *
32 * GPL LICENSE SUMMARY
33 *
34 * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
35 *
36 * This program is free software; you can redistribute it and/or modify
37 * it under the terms of version 2 of the GNU General Public License as
38 * published by the Free Software Foundation.
39 *
40 * This program is distributed in the hope that it will be useful, but
41 * WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43 * General Public License for more details.
44 *
45 * You should have received a copy of the GNU General Public License
46 * along with this program; if not, write to the Free Software
47 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
48 * USA
49 *
50 * The full GNU General Public License is included in this distribution
51 * in the file called COPYING.
52 *
53 * Contact Information:
54 *  Intel Linux Wireless <ilw@linux.intel.com>
55 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
56 *
57 *
58 * BSD LICENSE
59 *
60 * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61 * All rights reserved.
62 *
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
65 * are met:
66 *
67 *  * Redistributions of source code must retain the above copyright
68 *    notice, this list of conditions and the following disclaimer.
69 *  * Redistributions in binary form must reproduce the above copyright
70 *    notice, this list of conditions and the following disclaimer in
71 *    the documentation and/or other materials provided with the
72 *    distribution.
73 *  * Neither the name Intel Corporation nor the names of its
74 *    contributors may be used to endorse or promote products derived
75 *    from this software without specific prior written permission.
76 *
77 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
88 */
89
90/*-
91 * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
92 *
93 * Permission to use, copy, modify, and distribute this software for any
94 * purpose with or without fee is hereby granted, provided that the above
95 * copyright notice and this permission notice appear in all copies.
96 *
97 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
100 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
101 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
102 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
103 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
104 */
105#include <sys/cdefs.h>
106__FBSDID("$FreeBSD: stable/11/sys/dev/iwm/if_iwm_phy_db.c 330455 2018-03-05 08:05:30Z eadler $");
107
108#include "opt_wlan.h"
109#include "opt_iwm.h"
110
111#include <sys/param.h>
112#include <sys/bus.h>
113#include <sys/conf.h>
114#include <sys/endian.h>
115#include <sys/firmware.h>
116#include <sys/kernel.h>
117#include <sys/malloc.h>
118#include <sys/mbuf.h>
119#include <sys/mutex.h>
120#include <sys/module.h>
121#include <sys/proc.h>
122#include <sys/rman.h>
123#include <sys/socket.h>
124#include <sys/sockio.h>
125#include <sys/sysctl.h>
126#include <sys/linker.h>
127
128#include <machine/bus.h>
129#include <machine/endian.h>
130#include <machine/resource.h>
131
132#include <dev/pci/pcivar.h>
133#include <dev/pci/pcireg.h>
134
135#include <net/bpf.h>
136
137#include <net/if.h>
138#include <net/if_var.h>
139#include <net/if_arp.h>
140#include <net/if_dl.h>
141#include <net/if_media.h>
142#include <net/if_types.h>
143
144#include <netinet/in.h>
145#include <netinet/in_systm.h>
146#include <netinet/if_ether.h>
147#include <netinet/ip.h>
148
149#include <net80211/ieee80211_var.h>
150#include <net80211/ieee80211_regdomain.h>
151#include <net80211/ieee80211_ratectl.h>
152#include <net80211/ieee80211_radiotap.h>
153
154#include "if_iwmreg.h"
155#include "if_iwmvar.h"
156#include "if_iwm_debug.h"
157#include "if_iwm_util.h"
158#include "if_iwm_phy_db.h"
159
160#define CHANNEL_NUM_SIZE	4	/* num of channels in calib_ch size */
161
162struct iwm_phy_db_entry {
163	uint16_t	size;
164	uint8_t		*data;
165};
166
167/**
168 * struct iwm_phy_db - stores phy configuration and calibration data.
169 *
170 * @cfg: phy configuration.
171 * @calib_nch: non channel specific calibration data.
172 * @calib_ch: channel specific calibration data.
173 * @n_group_papd: number of entries in papd channel group.
174 * @calib_ch_group_papd: calibration data related to papd channel group.
175 * @n_group_txp: number of entries in tx power channel group.
176 * @calib_ch_group_txp: calibration data related to tx power chanel group.
177 */
178struct iwm_phy_db {
179	struct iwm_phy_db_entry cfg;
180	struct iwm_phy_db_entry calib_nch;
181	int n_group_papd;
182	struct iwm_phy_db_entry *calib_ch_group_papd;
183	int n_group_txp;
184	struct iwm_phy_db_entry *calib_ch_group_txp;
185
186	struct iwm_softc *sc;
187};
188
189enum iwm_phy_db_section_type {
190	IWM_PHY_DB_CFG = 1,
191	IWM_PHY_DB_CALIB_NCH,
192	IWM_PHY_DB_UNUSED,
193	IWM_PHY_DB_CALIB_CHG_PAPD,
194	IWM_PHY_DB_CALIB_CHG_TXP,
195	IWM_PHY_DB_MAX
196};
197
198#define PHY_DB_CMD 0x6c
199
200/*
201 * phy db - configure operational ucode
202 */
203struct iwm_phy_db_cmd {
204	uint16_t type;
205	uint16_t length;
206	uint8_t data[];
207} __packed;
208
209/* for parsing of tx power channel group data that comes from the firmware*/
210struct iwm_phy_db_chg_txp {
211	uint32_t space;
212	uint16_t max_channel_idx;
213} __packed;
214
215/*
216 * phy db - Receive phy db chunk after calibrations
217 */
218struct iwm_calib_res_notif_phy_db {
219	uint16_t type;
220	uint16_t length;
221	uint8_t data[];
222} __packed;
223
224struct iwm_phy_db *
225iwm_phy_db_init(struct iwm_softc *sc)
226{
227	struct iwm_phy_db *phy_db = malloc(sizeof(struct iwm_phy_db),
228	    M_DEVBUF, M_NOWAIT | M_ZERO);
229
230	if (!phy_db)
231		return phy_db;
232
233	phy_db->sc = sc;
234
235	phy_db->n_group_txp = -1;
236	phy_db->n_group_papd = -1;
237
238	/* TODO: add default values of the phy db. */
239	return phy_db;
240}
241
242/*
243 * get phy db section: returns a pointer to a phy db section specified by
244 * type and channel group id.
245 */
246static struct iwm_phy_db_entry *
247iwm_phy_db_get_section(struct iwm_phy_db *phy_db,
248		       enum iwm_phy_db_section_type type,
249		       uint16_t chg_id)
250{
251	if (!phy_db || type >= IWM_PHY_DB_MAX)
252		return NULL;
253
254	switch (type) {
255	case IWM_PHY_DB_CFG:
256		return &phy_db->cfg;
257	case IWM_PHY_DB_CALIB_NCH:
258		return &phy_db->calib_nch;
259	case IWM_PHY_DB_CALIB_CHG_PAPD:
260		if (chg_id >= phy_db->n_group_papd)
261			return NULL;
262		return &phy_db->calib_ch_group_papd[chg_id];
263	case IWM_PHY_DB_CALIB_CHG_TXP:
264		if (chg_id >= phy_db->n_group_txp)
265			return NULL;
266		return &phy_db->calib_ch_group_txp[chg_id];
267	default:
268		return NULL;
269	}
270	return NULL;
271}
272
273static void
274iwm_phy_db_free_section(struct iwm_phy_db *phy_db,
275			enum iwm_phy_db_section_type type, uint16_t chg_id)
276{
277	struct iwm_phy_db_entry *entry =
278				iwm_phy_db_get_section(phy_db, type, chg_id);
279	if (!entry)
280		return;
281
282	if (entry->data != NULL)
283		free(entry->data, M_DEVBUF);
284	entry->data = NULL;
285	entry->size = 0;
286}
287
288void
289iwm_phy_db_free(struct iwm_phy_db *phy_db)
290{
291	int i;
292
293	if (!phy_db)
294		return;
295
296	iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CFG, 0);
297	iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_NCH, 0);
298
299	for (i = 0; i < phy_db->n_group_papd; i++)
300		iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_PAPD, i);
301	if (phy_db->calib_ch_group_papd != NULL)
302		free(phy_db->calib_ch_group_papd, M_DEVBUF);
303
304	for (i = 0; i < phy_db->n_group_txp; i++)
305		iwm_phy_db_free_section(phy_db, IWM_PHY_DB_CALIB_CHG_TXP, i);
306	if (phy_db->calib_ch_group_txp != NULL)
307		free(phy_db->calib_ch_group_txp, M_DEVBUF);
308
309	free(phy_db, M_DEVBUF);
310}
311
312int
313iwm_phy_db_set_section(struct iwm_phy_db *phy_db,
314		       struct iwm_rx_packet *pkt)
315{
316	struct iwm_calib_res_notif_phy_db *phy_db_notif =
317			(struct iwm_calib_res_notif_phy_db *)pkt->data;
318	enum iwm_phy_db_section_type type = le16toh(phy_db_notif->type);
319        uint16_t size  = le16toh(phy_db_notif->length);
320        struct iwm_phy_db_entry *entry;
321        uint16_t chg_id = 0;
322
323	if (!phy_db)
324		return EINVAL;
325
326	if (type == IWM_PHY_DB_CALIB_CHG_PAPD) {
327		chg_id = le16toh(*(uint16_t *)phy_db_notif->data);
328		if (phy_db && !phy_db->calib_ch_group_papd) {
329			/*
330			 * Firmware sends the largest index first, so we can use
331			 * it to know how much we should allocate.
332			 */
333			phy_db->calib_ch_group_papd = malloc(
334			    (chg_id + 1) * sizeof(struct iwm_phy_db_entry),
335			    M_DEVBUF, M_NOWAIT | M_ZERO);
336			if (!phy_db->calib_ch_group_papd)
337				return ENOMEM;
338			phy_db->n_group_papd = chg_id + 1;
339		}
340	} else if (type == IWM_PHY_DB_CALIB_CHG_TXP) {
341		chg_id = le16toh(*(uint16_t *)phy_db_notif->data);
342		if (phy_db && !phy_db->calib_ch_group_txp) {
343			/*
344			 * Firmware sends the largest index first, so we can use
345			 * it to know how much we should allocate.
346			 */
347			phy_db->calib_ch_group_txp = malloc(
348			    (chg_id + 1) * sizeof(struct iwm_phy_db_entry),
349			    M_DEVBUF, M_NOWAIT | M_ZERO);
350			if (!phy_db->calib_ch_group_txp)
351				return ENOMEM;
352			phy_db->n_group_txp = chg_id + 1;
353		}
354	}
355
356	entry = iwm_phy_db_get_section(phy_db, type, chg_id);
357	if (!entry)
358		return EINVAL;
359
360	if (entry->data != NULL)
361		free(entry->data, M_DEVBUF);
362	entry->data = malloc(size, M_DEVBUF, M_NOWAIT);
363	if (!entry->data) {
364		entry->size = 0;
365		return ENOMEM;
366	}
367	memcpy(entry->data, phy_db_notif->data, size);
368
369	entry->size = size;
370
371	IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
372		    "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
373		    __func__, __LINE__, type, size);
374
375	return 0;
376}
377
378static int
379is_valid_channel(uint16_t ch_id)
380{
381	if (ch_id <= 14 ||
382	    (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
383	    (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
384	    (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
385		return 1;
386	return 0;
387}
388
389static uint8_t
390ch_id_to_ch_index(uint16_t ch_id)
391{
392	if (!is_valid_channel(ch_id))
393                return 0xff;
394
395	if (ch_id <= 14)
396		return ch_id - 1;
397	if (ch_id <= 64)
398		return (ch_id + 20) / 4;
399	if (ch_id <= 140)
400		return (ch_id - 12) / 4;
401	return (ch_id - 13) / 4;
402}
403
404
405static uint16_t
406channel_id_to_papd(uint16_t ch_id)
407{
408	if (!is_valid_channel(ch_id))
409		return 0xff;
410
411	if (1 <= ch_id && ch_id <= 14)
412		return 0;
413	if (36 <= ch_id && ch_id <= 64)
414		return 1;
415	if (100 <= ch_id && ch_id <= 140)
416		return 2;
417	return 3;
418}
419
420static uint16_t
421channel_id_to_txp(struct iwm_phy_db *phy_db, uint16_t ch_id)
422{
423	struct iwm_phy_db_chg_txp *txp_chg;
424	int i;
425	uint8_t ch_index = ch_id_to_ch_index(ch_id);
426	if (ch_index == 0xff)
427		return 0xff;
428
429	for (i = 0; i < phy_db->n_group_txp; i++) {
430		txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
431		if (!txp_chg)
432			return 0xff;
433		/*
434		 * Looking for the first channel group that its max channel is
435		 * higher then wanted channel.
436		 */
437		if (le16toh(txp_chg->max_channel_idx) >= ch_index)
438			return i;
439	}
440	return 0xff;
441}
442
443static int
444iwm_phy_db_get_section_data(struct iwm_phy_db *phy_db,
445			   uint32_t type, uint8_t **data, uint16_t *size,
446			   uint16_t ch_id)
447{
448	struct iwm_phy_db_entry *entry;
449	uint16_t ch_group_id = 0;
450
451	if (!phy_db)
452		return EINVAL;
453
454	/* find wanted channel group */
455	if (type == IWM_PHY_DB_CALIB_CHG_PAPD)
456		ch_group_id = channel_id_to_papd(ch_id);
457	else if (type == IWM_PHY_DB_CALIB_CHG_TXP)
458		ch_group_id = channel_id_to_txp(phy_db, ch_id);
459
460	entry = iwm_phy_db_get_section(phy_db, type, ch_group_id);
461	if (!entry)
462		return EINVAL;
463
464	*data = entry->data;
465	*size = entry->size;
466
467	IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
468		   "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
469		   __func__, __LINE__, type, *size);
470
471	return 0;
472}
473
474static int
475iwm_send_phy_db_cmd(struct iwm_phy_db *phy_db, uint16_t type,
476		    uint16_t length, void *data)
477{
478	struct iwm_phy_db_cmd phy_db_cmd;
479	struct iwm_host_cmd cmd = {
480		.id = PHY_DB_CMD,
481	};
482
483	IWM_DPRINTF(phy_db->sc, IWM_DEBUG_RESET,
484		   "Sending PHY-DB hcmd of type %d, of length %d\n",
485		   type, length);
486
487	/* Set phy db cmd variables */
488	phy_db_cmd.type = htole16(type);
489	phy_db_cmd.length = htole16(length);
490
491	/* Set hcmd variables */
492	cmd.data[0] = &phy_db_cmd;
493	cmd.len[0] = sizeof(struct iwm_phy_db_cmd);
494	cmd.data[1] = data;
495	cmd.len[1] = length;
496#ifdef notyet
497	cmd.dataflags[1] = IWM_HCMD_DFL_NOCOPY;
498#endif
499
500	return iwm_send_cmd(phy_db->sc, &cmd);
501}
502
503static int
504iwm_phy_db_send_all_channel_groups(struct iwm_phy_db *phy_db,
505				   enum iwm_phy_db_section_type type,
506				   uint8_t max_ch_groups)
507{
508	uint16_t i;
509	int err;
510	struct iwm_phy_db_entry *entry;
511
512	/* Send all the  channel specific groups to operational fw */
513	for (i = 0; i < max_ch_groups; i++) {
514		entry = iwm_phy_db_get_section(phy_db,
515                                               type,
516                                               i);
517		if (!entry)
518			return EINVAL;
519
520		if (!entry->size)
521			continue;
522
523		/* Send the requested PHY DB section */
524		err = iwm_send_phy_db_cmd(phy_db,
525					  type,
526					  entry->size,
527					  entry->data);
528		if (err) {
529			device_printf(phy_db->sc->sc_dev,
530				"Can't SEND phy_db section %d (%d), err %d\n",
531				type, i, err);
532			return err;
533		}
534
535		IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD,
536		    "Sent PHY_DB HCMD, type = %d num = %d\n", type, i);
537	}
538
539	return 0;
540}
541
542int
543iwm_send_phy_db_data(struct iwm_phy_db *phy_db)
544{
545	uint8_t *data = NULL;
546	uint16_t size = 0;
547	int err;
548
549	IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
550	    "%s: Sending phy db data and configuration to runtime image\n",
551	    __func__);
552
553	/* Send PHY DB CFG section */
554	err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CFG,
555					  &data, &size, 0);
556	if (err) {
557		device_printf(phy_db->sc->sc_dev,
558		    "%s: Cannot get Phy DB cfg section, %d\n",
559		    __func__, err);
560		return err;
561	}
562
563	err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CFG, size, data);
564	if (err) {
565		device_printf(phy_db->sc->sc_dev,
566		    "%s: Cannot send HCMD of Phy DB cfg section, %d\n",
567		    __func__, err);
568		return err;
569	}
570
571	err = iwm_phy_db_get_section_data(phy_db, IWM_PHY_DB_CALIB_NCH,
572	    &data, &size, 0);
573	if (err) {
574		device_printf(phy_db->sc->sc_dev,
575		    "%s: Cannot get Phy DB non specific channel section, "
576		    "%d\n", __func__, err);
577		return err;
578	}
579
580	err = iwm_send_phy_db_cmd(phy_db, IWM_PHY_DB_CALIB_NCH, size, data);
581	if (err) {
582		device_printf(phy_db->sc->sc_dev,
583		    "%s: Cannot send HCMD of Phy DB non specific channel "
584		    "sect, %d\n", __func__, err);
585		return err;
586	}
587
588	/* Send all the TXP channel specific data */
589	err = iwm_phy_db_send_all_channel_groups(phy_db,
590	    IWM_PHY_DB_CALIB_CHG_PAPD, phy_db->n_group_papd);
591	if (err) {
592		device_printf(phy_db->sc->sc_dev,
593		    "%s: Cannot send channel specific PAPD groups, %d\n",
594		    __func__, err);
595		return err;
596	}
597
598	/* Send all the TXP channel specific data */
599	err = iwm_phy_db_send_all_channel_groups(phy_db,
600	    IWM_PHY_DB_CALIB_CHG_TXP, phy_db->n_group_txp);
601	if (err) {
602		device_printf(phy_db->sc->sc_dev,
603		    "%s: Cannot send channel specific TX power groups, "
604		    "%d\n", __func__, err);
605		return err;
606	}
607
608	IWM_DPRINTF(phy_db->sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET,
609	    "%s: Finished sending phy db non channel data\n",
610	    __func__);
611	return 0;
612}
613