1251487Sadrian/*-
2251487Sadrian * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3251487Sadrian * All rights reserved.
4251487Sadrian *
5251487Sadrian * Redistribution and use in source and binary forms, with or without
6251487Sadrian * modification, are permitted provided that the following conditions
7251487Sadrian * are met:
8251487Sadrian * 1. Redistributions of source code must retain the above copyright
9251487Sadrian *    notice, this list of conditions and the following disclaimer,
10251487Sadrian *    without modification.
11251487Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12251487Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13251487Sadrian *    redistribution must be conditioned upon including a substantially
14251487Sadrian *    similar Disclaimer requirement for further binary redistribution.
15251487Sadrian *
16251487Sadrian * NO WARRANTY
17251487Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18251487Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19251487Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20251487Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21251487Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22251487Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23251487Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24251487Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25251487Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26251487Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27251487Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28251487Sadrian *
29251487Sadrian * $FreeBSD: stable/11/sys/dev/ath/if_ath_btcoex.c 332303 2018-04-08 20:50:16Z emaste $
30251487Sadrian */
31251487Sadrian#include <sys/cdefs.h>
32251487Sadrian__FBSDID("$FreeBSD: stable/11/sys/dev/ath/if_ath_btcoex.c 332303 2018-04-08 20:50:16Z emaste $");
33251487Sadrian
34251487Sadrian/*
35251487Sadrian * This implements some very basic bluetooth coexistence methods for
36251487Sadrian * the ath(4) hardware.
37251487Sadrian */
38251487Sadrian#include "opt_ath.h"
39251487Sadrian#include "opt_inet.h"
40251487Sadrian#include "opt_wlan.h"
41251487Sadrian
42251487Sadrian#include <sys/param.h>
43251487Sadrian#include <sys/systm.h>
44251487Sadrian#include <sys/sysctl.h>
45251487Sadrian#include <sys/kernel.h>
46251487Sadrian#include <sys/lock.h>
47257176Sglebius#include <sys/malloc.h>
48251487Sadrian#include <sys/mutex.h>
49251487Sadrian#include <sys/errno.h>
50251487Sadrian#include <machine/bus.h>
51251487Sadrian#include <machine/resource.h>
52301181Sadrian
53251487Sadrian#include <sys/bus.h>
54251487Sadrian
55251487Sadrian#include <sys/socket.h>
56251487Sadrian
57251487Sadrian#include <net/if.h>
58257176Sglebius#include <net/if_var.h>
59251487Sadrian#include <net/if_media.h>
60251487Sadrian#include <net/if_arp.h>
61251487Sadrian#include <net/ethernet.h>		/* XXX for ether_sprintf */
62251487Sadrian
63251487Sadrian#include <net80211/ieee80211_var.h>
64251487Sadrian
65251487Sadrian#include <net/bpf.h>
66251487Sadrian
67251487Sadrian#ifdef INET
68251487Sadrian#include <netinet/in.h>
69251487Sadrian#include <netinet/if_ether.h>
70251487Sadrian#endif
71251487Sadrian
72251487Sadrian#include <dev/ath/if_athvar.h>
73251487Sadrian#include <dev/ath/if_ath_btcoex.h>
74301181Sadrian#include <dev/ath/if_ath_btcoex_mci.h>
75251487Sadrian
76301181SadrianMALLOC_DECLARE(M_ATHDEV);
77301181Sadrian
78251487Sadrian/*
79251487Sadrian * Initial AR9285 / (WB195) bluetooth coexistence settings,
80251487Sadrian * just for experimentation.
81251487Sadrian *
82251487Sadrian * Return 0 for OK; errno for error.
83251487Sadrian *
84251487Sadrian * XXX TODO: There needs to be a PCIe workaround to disable ASPM if
85251487Sadrian * bluetooth coexistence is enabled.
86251487Sadrian */
87251487Sadrianstatic int
88251487Sadrianath_btcoex_cfg_wb195(struct ath_softc *sc)
89251487Sadrian{
90251487Sadrian	HAL_BT_COEX_INFO btinfo;
91251487Sadrian	HAL_BT_COEX_CONFIG btconfig;
92251487Sadrian	struct ath_hal *ah = sc->sc_ah;
93251487Sadrian
94251487Sadrian	if (! ath_hal_btcoex_supported(ah))
95251487Sadrian		return (EINVAL);
96251487Sadrian
97251487Sadrian	bzero(&btinfo, sizeof(btinfo));
98251487Sadrian	bzero(&btconfig, sizeof(btconfig));
99251487Sadrian
100251487Sadrian	device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n");
101251487Sadrian
102251487Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;
103251487Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
104251487Sadrian	/*
105251487Sadrian	 * These are the three GPIO pins hooked up between the AR9285 and
106251487Sadrian	 * the AR3011.
107251487Sadrian	 */
108251487Sadrian	btinfo.bt_gpio_bt_active = 6;
109251487Sadrian	btinfo.bt_gpio_bt_priority = 7;
110251487Sadrian	btinfo.bt_gpio_wlan_active = 5;
111251487Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
112251487Sadrian	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
113251487Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
114251487Sadrian
115251487Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
116251487Sadrian
117251487Sadrian	btconfig.bt_time_extend = 0;
118251487Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
119251487Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
120251487Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
121251487Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
122251487Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
123251487Sadrian	btconfig.bt_priority_time = 2;
124251487Sadrian	btconfig.bt_first_slot_time = 5;
125251487Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
126251487Sadrian
127251487Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
128251487Sadrian
129251487Sadrian	/*
130251487Sadrian	 * Enable antenna diversity.
131251487Sadrian	 */
132251487Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
133251487Sadrian
134251487Sadrian	return (0);
135251487Sadrian}
136251487Sadrian
137251742Sadrian/*
138251742Sadrian * Initial AR9485 / (WB225) bluetooth coexistence settings,
139251742Sadrian * just for experimentation.
140251742Sadrian *
141251742Sadrian * Return 0 for OK; errno for error.
142251742Sadrian */
143251742Sadrianstatic int
144251742Sadrianath_btcoex_cfg_wb225(struct ath_softc *sc)
145251742Sadrian{
146251742Sadrian	HAL_BT_COEX_INFO btinfo;
147251742Sadrian	HAL_BT_COEX_CONFIG btconfig;
148251742Sadrian	struct ath_hal *ah = sc->sc_ah;
149251742Sadrian
150251742Sadrian	if (! ath_hal_btcoex_supported(ah))
151251742Sadrian		return (EINVAL);
152251742Sadrian
153251742Sadrian	bzero(&btinfo, sizeof(btinfo));
154251742Sadrian	bzero(&btconfig, sizeof(btconfig));
155251742Sadrian
156251742Sadrian	device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n");
157251742Sadrian
158251742Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
159251742Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
160251742Sadrian	/*
161251742Sadrian	 * These are the three GPIO pins hooked up between the AR9485 and
162251742Sadrian	 * the bluetooth module.
163251742Sadrian	 */
164251742Sadrian	btinfo.bt_gpio_bt_active = 4;
165251742Sadrian	btinfo.bt_gpio_bt_priority = 8;
166251742Sadrian	btinfo.bt_gpio_wlan_active = 5;
167251742Sadrian
168251742Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
169251742Sadrian	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
170251742Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
171251742Sadrian
172251742Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
173251742Sadrian
174251742Sadrian	btconfig.bt_time_extend = 0;
175251742Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
176251742Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
177251742Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
178251742Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
179251742Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
180251742Sadrian	btconfig.bt_priority_time = 2;
181251742Sadrian	btconfig.bt_first_slot_time = 5;
182251742Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
183251742Sadrian
184251742Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
185251742Sadrian
186251742Sadrian	/*
187251742Sadrian	 * Enable antenna diversity.
188251742Sadrian	 */
189251742Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
190251742Sadrian
191251742Sadrian	return (0);
192251742Sadrian}
193251742Sadrian
194277277Sadrianstatic int
195301181Sadrianath_btcoex_cfg_mci(struct ath_softc *sc, uint32_t mci_cfg, int do_btdiv)
196277277Sadrian{
197277277Sadrian	HAL_BT_COEX_INFO btinfo;
198277277Sadrian	HAL_BT_COEX_CONFIG btconfig;
199277277Sadrian	struct ath_hal *ah = sc->sc_ah;
200251742Sadrian
201277277Sadrian	if (! ath_hal_btcoex_supported(ah))
202277277Sadrian		return (EINVAL);
203277277Sadrian
204277277Sadrian	bzero(&btinfo, sizeof(btinfo));
205277277Sadrian	bzero(&btconfig, sizeof(btconfig));
206277277Sadrian
207301181Sadrian	sc->sc_ah->ah_config.ath_hal_mci_config = mci_cfg;
208277277Sadrian
209301181Sadrian	if (ath_btcoex_mci_attach(sc) != 0) {
210301181Sadrian		device_printf(sc->sc_dev, "Failed to setup btcoex\n");
211301181Sadrian		return (EINVAL);
212301181Sadrian	}
213301181Sadrian
214277277Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
215277277Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;
216277277Sadrian
217277277Sadrian	/*
218277277Sadrian	 * MCI uses a completely different interface to speak
219277277Sadrian	 * to the bluetooth module - it's a command based
220277277Sadrian	 * thing over a serial line, rather than
221277277Sadrian	 * state pins to/from the bluetooth module.
222277277Sadrian	 *
223277277Sadrian	 * So, the GPIO configuration, polarity, etc
224277277Sadrian	 * doesn't matter on MCI devices; it's just
225277277Sadrian	 * completely ignored by the HAL.
226277277Sadrian	 */
227277277Sadrian	btinfo.bt_gpio_bt_active = 4;
228277277Sadrian	btinfo.bt_gpio_bt_priority = 8;
229277277Sadrian	btinfo.bt_gpio_wlan_active = 5;
230277277Sadrian
231277277Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
232301181Sadrian	btinfo.bt_single_ant = 0;	/* 2 antenna on WB335 */
233277277Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
234277277Sadrian
235277277Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
236277277Sadrian
237277277Sadrian	btconfig.bt_time_extend = 0;
238277277Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
239277277Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
240277277Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
241277277Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
242277277Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
243277277Sadrian	btconfig.bt_priority_time = 2;
244277277Sadrian	btconfig.bt_first_slot_time = 5;
245277277Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
246277277Sadrian
247277277Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
248277277Sadrian
249301181Sadrian	/* Enable */
250301181Sadrian	ath_hal_btcoex_enable(sc->sc_ah);
251301181Sadrian
252301181Sadrian	/* Stomp */
253301181Sadrian	ath_hal_btcoex_set_weights(ah, HAL_BT_COEX_STOMP_NONE);
254301181Sadrian
255277277Sadrian	/*
256277277Sadrian	 * Enable antenna diversity.
257277277Sadrian	 */
258301181Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY,
259301181Sadrian	    do_btdiv);
260277277Sadrian
261277277Sadrian	return (0);
262277277Sadrian}
263277277Sadrian
264300896Sadrian/*
265301181Sadrian * Initial AR9462 / (WB222) bluetooth coexistence settings.
266300896Sadrian *
267300896Sadrian * Return 0 for OK; errno for error.
268300896Sadrian */
269300896Sadrianstatic int
270301181Sadrianath_btcoex_cfg_wb222(struct ath_softc *sc)
271300896Sadrian{
272277277Sadrian
273301181Sadrian	device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n");
274301181Sadrian	/* XXX from ath9k */
275301181Sadrian	return (ath_btcoex_cfg_mci(sc, 0x2201, 1));
276301181Sadrian}
277277277Sadrian
278301181Sadrian/*
279301181Sadrian * Initial QCA9565 / (WB335B) bluetooth coexistence settings.
280301181Sadrian *
281301181Sadrian * Return 0 for OK; errno for error.
282301181Sadrian */
283301181Sadrianstatic int
284301181Sadrianath_btcoex_cfg_wb335b(struct ath_softc *sc)
285301181Sadrian{
286301181Sadrian	uint32_t flags;
287301181Sadrian	int do_btdiv = 0;
288277277Sadrian
289301181Sadrian	/* ath9k default */
290301181Sadrian	flags = 0xa4c1;
291300896Sadrian
292301181Sadrian	/* 1-ant and 2-ant AR9565 */
293300896Sadrian	/*
294301181Sadrian	 * XXX TODO: ensure these actually make it down to the
295301181Sadrian	 * HAL correctly!
296300896Sadrian	 */
297301181Sadrian	if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) {
298301186Sadrian		flags &= ~ATH_MCI_CONFIG_ANT_ARCH;
299301186Sadrian		flags |= ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED <<
300301186Sadrian		    ATH_MCI_CONFIG_ANT_ARCH_S;
301301181Sadrian	} else if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) {
302301186Sadrian		flags &= ~ATH_MCI_CONFIG_ANT_ARCH;
303301186Sadrian		flags |= ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED <<
304301186Sadrian		    ATH_MCI_CONFIG_ANT_ARCH_S;
305301181Sadrian	}
306300896Sadrian
307301181Sadrian	if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) {
308301181Sadrian		do_btdiv = 1;
309301181Sadrian	}
310300896Sadrian
311301181Sadrian	device_printf(sc->sc_dev, "Enabling WB335 BTCOEX\n");
312301181Sadrian	/* XXX from ath9k */
313301181Sadrian	return (ath_btcoex_cfg_mci(sc, flags, do_btdiv));
314300896Sadrian}
315300896Sadrian
316251487Sadrian#if 0
317251487Sadrian/*
318251487Sadrian * When using bluetooth coexistence, ASPM needs to be disabled
319251487Sadrian * otherwise the sleeping interferes with the bluetooth (USB)
320251487Sadrian * operation and the MAC sleep/wakeup hardware.
321251606Sadrian *
322251606Sadrian * The PCIe powersave routine also needs to not be called
323251606Sadrian * by the driver during suspend/resume, else things will get
324251606Sadrian * a little odd.  Check Linux ath9k for more details.
325251487Sadrian */
326251487Sadrianstatic int
327251487Sadrianath_btcoex_aspm_wb195(struct ath_softc *sc)
328251487Sadrian{
329251487Sadrian
330251487Sadrian	/* XXX TODO: clear device ASPM L0S and L1 */
331251487Sadrian	/* XXX TODO: clear _parent_ ASPM L0S and L1 */
332251487Sadrian}
333251487Sadrian#endif
334251487Sadrian
335251487Sadrian/*
336251487Sadrian * Methods which are required
337251487Sadrian */
338251487Sadrian
339251487Sadrian/*
340251487Sadrian * Attach btcoex to the given interface
341251487Sadrian */
342251487Sadrianint
343251487Sadrianath_btcoex_attach(struct ath_softc *sc)
344251487Sadrian{
345251487Sadrian	int ret;
346251487Sadrian	struct ath_hal *ah = sc->sc_ah;
347251487Sadrian	const char *profname;
348251487Sadrian
349251487Sadrian	/*
350251487Sadrian	 * No chipset bluetooth coexistence? Then do nothing.
351251487Sadrian	 */
352251487Sadrian	if (! ath_hal_btcoex_supported(ah))
353251487Sadrian		return (0);
354251487Sadrian
355251487Sadrian	/*
356251487Sadrian	 * Look at the hints to determine which bluetooth
357251487Sadrian	 * profile to configure.
358251487Sadrian	 */
359251487Sadrian	ret = resource_string_value(device_get_name(sc->sc_dev),
360251487Sadrian	    device_get_unit(sc->sc_dev),
361251487Sadrian	    "btcoex_profile",
362251487Sadrian	    &profname);
363251487Sadrian	if (ret != 0) {
364251487Sadrian		/* nothing to do */
365251487Sadrian		return (0);
366251487Sadrian	}
367251487Sadrian
368251487Sadrian	if (strncmp(profname, "wb195", 5) == 0) {
369251487Sadrian		ret = ath_btcoex_cfg_wb195(sc);
370277277Sadrian	} else if (strncmp(profname, "wb222", 5) == 0) {
371277277Sadrian		ret = ath_btcoex_cfg_wb222(sc);
372251742Sadrian	} else if (strncmp(profname, "wb225", 5) == 0) {
373251742Sadrian		ret = ath_btcoex_cfg_wb225(sc);
374300896Sadrian	} else if (strncmp(profname, "wb335", 5) == 0) {
375300896Sadrian		ret = ath_btcoex_cfg_wb335b(sc);
376251487Sadrian	} else {
377251487Sadrian		return (0);
378251487Sadrian	}
379251487Sadrian
380251487Sadrian	/*
381251487Sadrian	 * Propagate up failure from the actual attach phase.
382251487Sadrian	 */
383251487Sadrian	if (ret != 0)
384251487Sadrian		return (ret);
385251487Sadrian
386251487Sadrian	return (0);
387251487Sadrian}
388251487Sadrian
389251487Sadrian/*
390251487Sadrian * Detach btcoex from the given interface
391251487Sadrian */
392251487Sadrianint
393251487Sadrianath_btcoex_detach(struct ath_softc *sc)
394251487Sadrian{
395301181Sadrian	if (sc->sc_btcoex_mci) {
396301181Sadrian		ath_btcoex_mci_detach(sc);
397301181Sadrian	}
398251487Sadrian
399251487Sadrian	return (0);
400251487Sadrian}
401251487Sadrian
402251487Sadrian/*
403251487Sadrian * Configure or disable bluetooth coexistence on the given channel.
404251487Sadrian *
405251487Sadrian * For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just
406251487Sadrian * assume bluetooth coexistence is always on.
407251487Sadrian *
408251487Sadrian * For AR9462, we may see a 5GHz channel; bluetooth coexistence should
409251487Sadrian * not be enabled on those channels.
410251487Sadrian */
411251487Sadrianint
412251487Sadrianath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
413251487Sadrian{
414301181Sadrian	if (sc->sc_btcoex_mci) {
415301181Sadrian		ath_btcoex_mci_enable(sc, chan);
416301181Sadrian	}
417251487Sadrian
418251487Sadrian	return (0);
419251487Sadrian}
420251487Sadrian
421251487Sadrian/*
422251487Sadrian * Handle ioctl requests from the diagnostic interface.
423251487Sadrian *
424251487Sadrian * The initial part of this code resembles ath_ioctl_diag();
425251487Sadrian * it's likely a good idea to reduce duplication between
426251487Sadrian * these two routines.
427251487Sadrian */
428251487Sadrianint
429251487Sadrianath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad)
430251487Sadrian{
431251487Sadrian	unsigned int id = ad->ad_id & ATH_DIAG_ID;
432251487Sadrian	void *indata = NULL;
433251487Sadrian	void *outdata = NULL;
434251487Sadrian	u_int32_t insize = ad->ad_in_size;
435251487Sadrian	u_int32_t outsize = ad->ad_out_size;
436251487Sadrian	int error = 0;
437251487Sadrian//	int val;
438251487Sadrian
439251487Sadrian	if (ad->ad_id & ATH_DIAG_IN) {
440251487Sadrian		/*
441251487Sadrian		 * Copy in data.
442251487Sadrian		 */
443251487Sadrian		indata = malloc(insize, M_TEMP, M_NOWAIT);
444251487Sadrian		if (indata == NULL) {
445251487Sadrian			error = ENOMEM;
446251487Sadrian			goto bad;
447251487Sadrian		}
448251487Sadrian		error = copyin(ad->ad_in_data, indata, insize);
449251487Sadrian		if (error)
450251487Sadrian			goto bad;
451251487Sadrian	}
452251487Sadrian	if (ad->ad_id & ATH_DIAG_DYN) {
453251487Sadrian		/*
454251487Sadrian		 * Allocate a buffer for the results (otherwise the HAL
455251487Sadrian		 * returns a pointer to a buffer where we can read the
456251487Sadrian		 * results).  Note that we depend on the HAL leaving this
457251487Sadrian		 * pointer for us to use below in reclaiming the buffer;
458251487Sadrian		 * may want to be more defensive.
459251487Sadrian		 */
460332303Semaste		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
461251487Sadrian		if (outdata == NULL) {
462251487Sadrian			error = ENOMEM;
463251487Sadrian			goto bad;
464251487Sadrian		}
465251487Sadrian	}
466251487Sadrian	switch (id) {
467251487Sadrian		default:
468251487Sadrian			error = EINVAL;
469332303Semaste			goto bad;
470251487Sadrian	}
471251487Sadrian	if (outsize < ad->ad_out_size)
472251487Sadrian		ad->ad_out_size = outsize;
473251487Sadrian	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
474251487Sadrian		error = EFAULT;
475251487Sadrianbad:
476251487Sadrian	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
477251487Sadrian		free(indata, M_TEMP);
478251487Sadrian	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
479251487Sadrian		free(outdata, M_TEMP);
480251487Sadrian	return (error);
481251487Sadrian}
482251487Sadrian
483