if_ath_btcoex.c revision 300896
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: head/sys/dev/ath/if_ath_btcoex.c 300896 2016-05-28 02:14:24Z adrian $
30251487Sadrian */
31251487Sadrian#include <sys/cdefs.h>
32251487Sadrian__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_btcoex.c 300896 2016-05-28 02:14:24Z adrian $");
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
51251487Sadrian#include <machine/bus.h>
52251487Sadrian#include <machine/resource.h>
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>
74251487Sadrian
75251487Sadrian/*
76251487Sadrian * Initial AR9285 / (WB195) bluetooth coexistence settings,
77251487Sadrian * just for experimentation.
78251487Sadrian *
79251487Sadrian * Return 0 for OK; errno for error.
80251487Sadrian *
81251487Sadrian * XXX TODO: There needs to be a PCIe workaround to disable ASPM if
82251487Sadrian * bluetooth coexistence is enabled.
83251487Sadrian */
84251487Sadrianstatic int
85251487Sadrianath_btcoex_cfg_wb195(struct ath_softc *sc)
86251487Sadrian{
87251487Sadrian	HAL_BT_COEX_INFO btinfo;
88251487Sadrian	HAL_BT_COEX_CONFIG btconfig;
89251487Sadrian	struct ath_hal *ah = sc->sc_ah;
90251487Sadrian
91251487Sadrian	if (! ath_hal_btcoex_supported(ah))
92251487Sadrian		return (EINVAL);
93251487Sadrian
94251487Sadrian	bzero(&btinfo, sizeof(btinfo));
95251487Sadrian	bzero(&btconfig, sizeof(btconfig));
96251487Sadrian
97251487Sadrian	device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n");
98251487Sadrian
99251487Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;
100251487Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
101251487Sadrian	/*
102251487Sadrian	 * These are the three GPIO pins hooked up between the AR9285 and
103251487Sadrian	 * the AR3011.
104251487Sadrian	 */
105251487Sadrian	btinfo.bt_gpio_bt_active = 6;
106251487Sadrian	btinfo.bt_gpio_bt_priority = 7;
107251487Sadrian	btinfo.bt_gpio_wlan_active = 5;
108251487Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
109251487Sadrian	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
110251487Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
111251487Sadrian
112251487Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
113251487Sadrian
114251487Sadrian	btconfig.bt_time_extend = 0;
115251487Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
116251487Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
117251487Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
118251487Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
119251487Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
120251487Sadrian	btconfig.bt_priority_time = 2;
121251487Sadrian	btconfig.bt_first_slot_time = 5;
122251487Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
123251487Sadrian
124251487Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
125251487Sadrian
126251487Sadrian	/*
127251487Sadrian	 * Enable antenna diversity.
128251487Sadrian	 */
129251487Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
130251487Sadrian
131251487Sadrian	return (0);
132251487Sadrian}
133251487Sadrian
134251742Sadrian/*
135251742Sadrian * Initial AR9485 / (WB225) bluetooth coexistence settings,
136251742Sadrian * just for experimentation.
137251742Sadrian *
138251742Sadrian * Return 0 for OK; errno for error.
139251742Sadrian */
140251742Sadrianstatic int
141251742Sadrianath_btcoex_cfg_wb225(struct ath_softc *sc)
142251742Sadrian{
143251742Sadrian	HAL_BT_COEX_INFO btinfo;
144251742Sadrian	HAL_BT_COEX_CONFIG btconfig;
145251742Sadrian	struct ath_hal *ah = sc->sc_ah;
146251742Sadrian
147251742Sadrian	if (! ath_hal_btcoex_supported(ah))
148251742Sadrian		return (EINVAL);
149251742Sadrian
150251742Sadrian	bzero(&btinfo, sizeof(btinfo));
151251742Sadrian	bzero(&btconfig, sizeof(btconfig));
152251742Sadrian
153251742Sadrian	device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n");
154251742Sadrian
155251742Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
156251742Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
157251742Sadrian	/*
158251742Sadrian	 * These are the three GPIO pins hooked up between the AR9485 and
159251742Sadrian	 * the bluetooth module.
160251742Sadrian	 */
161251742Sadrian	btinfo.bt_gpio_bt_active = 4;
162251742Sadrian	btinfo.bt_gpio_bt_priority = 8;
163251742Sadrian	btinfo.bt_gpio_wlan_active = 5;
164251742Sadrian
165251742Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
166251742Sadrian	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
167251742Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
168251742Sadrian
169251742Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
170251742Sadrian
171251742Sadrian	btconfig.bt_time_extend = 0;
172251742Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
173251742Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
174251742Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
175251742Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
176251742Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
177251742Sadrian	btconfig.bt_priority_time = 2;
178251742Sadrian	btconfig.bt_first_slot_time = 5;
179251742Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
180251742Sadrian
181251742Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
182251742Sadrian
183251742Sadrian	/*
184251742Sadrian	 * Enable antenna diversity.
185251742Sadrian	 */
186251742Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
187251742Sadrian
188251742Sadrian	return (0);
189251742Sadrian}
190251742Sadrian
191277277Sadrian/*
192277277Sadrian * Initial AR9462 / (WB222) bluetooth coexistence settings,
193277277Sadrian * just for experimentation.
194277277Sadrian *
195277277Sadrian * Return 0 for OK; errno for error.
196277277Sadrian */
197277277Sadrianstatic int
198277277Sadrianath_btcoex_cfg_wb222(struct ath_softc *sc)
199277277Sadrian{
200277277Sadrian	HAL_BT_COEX_INFO btinfo;
201277277Sadrian	HAL_BT_COEX_CONFIG btconfig;
202277277Sadrian	struct ath_hal *ah = sc->sc_ah;
203251742Sadrian
204277277Sadrian	if (! ath_hal_btcoex_supported(ah))
205277277Sadrian		return (EINVAL);
206277277Sadrian
207277277Sadrian	bzero(&btinfo, sizeof(btinfo));
208277277Sadrian	bzero(&btconfig, sizeof(btconfig));
209277277Sadrian
210277277Sadrian	device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n");
211277277Sadrian
212277277Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
213277277Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;
214277277Sadrian
215277277Sadrian	/*
216277277Sadrian	 * MCI uses a completely different interface to speak
217277277Sadrian	 * to the bluetooth module - it's a command based
218277277Sadrian	 * thing over a serial line, rather than
219277277Sadrian	 * state pins to/from the bluetooth module.
220277277Sadrian	 *
221277277Sadrian	 * So, the GPIO configuration, polarity, etc
222277277Sadrian	 * doesn't matter on MCI devices; it's just
223277277Sadrian	 * completely ignored by the HAL.
224277277Sadrian	 */
225277277Sadrian	btinfo.bt_gpio_bt_active = 4;
226277277Sadrian	btinfo.bt_gpio_bt_priority = 8;
227277277Sadrian	btinfo.bt_gpio_wlan_active = 5;
228277277Sadrian
229277277Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
230277277Sadrian	btinfo.bt_single_ant = 0;	/* 2 antenna on WB222 */
231277277Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
232277277Sadrian
233277277Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
234277277Sadrian
235277277Sadrian	btconfig.bt_time_extend = 0;
236277277Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
237277277Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
238277277Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
239277277Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
240277277Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
241277277Sadrian	btconfig.bt_priority_time = 2;
242277277Sadrian	btconfig.bt_first_slot_time = 5;
243277277Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
244277277Sadrian
245277277Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
246277277Sadrian
247277277Sadrian	/*
248277277Sadrian	 * Enable antenna diversity.
249277277Sadrian	 */
250277277Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
251277277Sadrian
252277277Sadrian	return (0);
253277277Sadrian}
254277277Sadrian
255300896Sadrian/*
256300896Sadrian * Initial QCA9565 / (WB335B) bluetooth coexistence settings,
257300896Sadrian * just for experimentation.
258300896Sadrian *
259300896Sadrian * Return 0 for OK; errno for error.
260300896Sadrian */
261300896Sadrianstatic int
262300896Sadrianath_btcoex_cfg_wb335b(struct ath_softc *sc)
263300896Sadrian{
264300896Sadrian	HAL_BT_COEX_INFO btinfo;
265300896Sadrian	HAL_BT_COEX_CONFIG btconfig;
266300896Sadrian	struct ath_hal *ah = sc->sc_ah;
267277277Sadrian
268300896Sadrian	if (! ath_hal_btcoex_supported(ah))
269300896Sadrian		return (EINVAL);
270277277Sadrian
271300896Sadrian	bzero(&btinfo, sizeof(btinfo));
272300896Sadrian	bzero(&btconfig, sizeof(btconfig));
273277277Sadrian
274300896Sadrian	device_printf(sc->sc_dev, "Enabling WB335B BTCOEX\n");
275300896Sadrian
276300896Sadrian	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
277300896Sadrian	btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;
278300896Sadrian
279300896Sadrian	/*
280300896Sadrian	 * MCI uses a completely different interface to speak
281300896Sadrian	 * to the bluetooth module - it's a command based
282300896Sadrian	 * thing over a serial line, rather than
283300896Sadrian	 * state pins to/from the bluetooth module.
284300896Sadrian	 *
285300896Sadrian	 * So, the GPIO configuration, polarity, etc
286300896Sadrian	 * doesn't matter on MCI devices; it's just
287300896Sadrian	 * completely ignored by the HAL.
288300896Sadrian	 */
289300896Sadrian	btinfo.bt_gpio_bt_active = 4;
290300896Sadrian	btinfo.bt_gpio_bt_priority = 8;
291300896Sadrian	btinfo.bt_gpio_wlan_active = 5;
292300896Sadrian
293300896Sadrian	btinfo.bt_active_polarity = 1;	/* XXX not used */
294300896Sadrian	btinfo.bt_single_ant = 0;	/* 2 antenna on WB335 */
295300896Sadrian	btinfo.bt_isolation = 0;	/* in dB, not used */
296300896Sadrian
297300896Sadrian	ath_hal_btcoex_set_info(ah, &btinfo);
298300896Sadrian
299300896Sadrian	btconfig.bt_time_extend = 0;
300300896Sadrian	btconfig.bt_txstate_extend = 1;	/* true */
301300896Sadrian	btconfig.bt_txframe_extend = 1;	/* true */
302300896Sadrian	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
303300896Sadrian	btconfig.bt_quiet_collision = 1;	/* true */
304300896Sadrian	btconfig.bt_rxclear_polarity = 1;	/* true */
305300896Sadrian	btconfig.bt_priority_time = 2;
306300896Sadrian	btconfig.bt_first_slot_time = 5;
307300896Sadrian	btconfig.bt_hold_rxclear = 1;	/* true */
308300896Sadrian
309300896Sadrian	ath_hal_btcoex_set_config(ah, &btconfig);
310300896Sadrian
311300896Sadrian	/*
312300896Sadrian	 * Enable antenna diversity.
313300896Sadrian	 */
314300896Sadrian	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
315300896Sadrian
316300896Sadrian	return (0);
317300896Sadrian}
318300896Sadrian
319300896Sadrian
320251487Sadrian#if 0
321251487Sadrian/*
322251487Sadrian * When using bluetooth coexistence, ASPM needs to be disabled
323251487Sadrian * otherwise the sleeping interferes with the bluetooth (USB)
324251487Sadrian * operation and the MAC sleep/wakeup hardware.
325251606Sadrian *
326251606Sadrian * The PCIe powersave routine also needs to not be called
327251606Sadrian * by the driver during suspend/resume, else things will get
328251606Sadrian * a little odd.  Check Linux ath9k for more details.
329251487Sadrian */
330251487Sadrianstatic int
331251487Sadrianath_btcoex_aspm_wb195(struct ath_softc *sc)
332251487Sadrian{
333251487Sadrian
334251487Sadrian	/* XXX TODO: clear device ASPM L0S and L1 */
335251487Sadrian	/* XXX TODO: clear _parent_ ASPM L0S and L1 */
336251487Sadrian}
337251487Sadrian#endif
338251487Sadrian
339251487Sadrian/*
340251487Sadrian * Methods which are required
341251487Sadrian */
342251487Sadrian
343251487Sadrian/*
344251487Sadrian * Attach btcoex to the given interface
345251487Sadrian */
346251487Sadrianint
347251487Sadrianath_btcoex_attach(struct ath_softc *sc)
348251487Sadrian{
349251487Sadrian	int ret;
350251487Sadrian	struct ath_hal *ah = sc->sc_ah;
351251487Sadrian	const char *profname;
352251487Sadrian
353251487Sadrian	/*
354251487Sadrian	 * No chipset bluetooth coexistence? Then do nothing.
355251487Sadrian	 */
356251487Sadrian	if (! ath_hal_btcoex_supported(ah))
357251487Sadrian		return (0);
358251487Sadrian
359251487Sadrian	/*
360251487Sadrian	 * Look at the hints to determine which bluetooth
361251487Sadrian	 * profile to configure.
362251487Sadrian	 */
363251487Sadrian	ret = resource_string_value(device_get_name(sc->sc_dev),
364251487Sadrian	    device_get_unit(sc->sc_dev),
365251487Sadrian	    "btcoex_profile",
366251487Sadrian	    &profname);
367251487Sadrian	if (ret != 0) {
368251487Sadrian		/* nothing to do */
369251487Sadrian		return (0);
370251487Sadrian	}
371251487Sadrian
372251487Sadrian	if (strncmp(profname, "wb195", 5) == 0) {
373251487Sadrian		ret = ath_btcoex_cfg_wb195(sc);
374277277Sadrian	} else if (strncmp(profname, "wb222", 5) == 0) {
375277277Sadrian		ret = ath_btcoex_cfg_wb222(sc);
376251742Sadrian	} else if (strncmp(profname, "wb225", 5) == 0) {
377251742Sadrian		ret = ath_btcoex_cfg_wb225(sc);
378300896Sadrian	} else if (strncmp(profname, "wb335", 5) == 0) {
379300896Sadrian		ret = ath_btcoex_cfg_wb335b(sc);
380251487Sadrian	} else {
381251487Sadrian		return (0);
382251487Sadrian	}
383251487Sadrian
384251487Sadrian	/*
385251487Sadrian	 * Propagate up failure from the actual attach phase.
386251487Sadrian	 */
387251487Sadrian	if (ret != 0)
388251487Sadrian		return (ret);
389251487Sadrian
390251487Sadrian	return (0);
391251487Sadrian}
392251487Sadrian
393251487Sadrian/*
394251487Sadrian * Detach btcoex from the given interface
395251487Sadrian */
396251487Sadrianint
397251487Sadrianath_btcoex_detach(struct ath_softc *sc)
398251487Sadrian{
399251487Sadrian
400251487Sadrian	return (0);
401251487Sadrian}
402251487Sadrian
403251487Sadrian/*
404251487Sadrian * Configure or disable bluetooth coexistence on the given channel.
405251487Sadrian *
406251487Sadrian * For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just
407251487Sadrian * assume bluetooth coexistence is always on.
408251487Sadrian *
409251487Sadrian * For AR9462, we may see a 5GHz channel; bluetooth coexistence should
410251487Sadrian * not be enabled on those channels.
411251487Sadrian */
412251487Sadrianint
413251487Sadrianath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
414251487Sadrian{
415251487Sadrian
416251487Sadrian	return (0);
417251487Sadrian}
418251487Sadrian
419251487Sadrian/*
420251487Sadrian * Handle ioctl requests from the diagnostic interface.
421251487Sadrian *
422251487Sadrian * The initial part of this code resembles ath_ioctl_diag();
423251487Sadrian * it's likely a good idea to reduce duplication between
424251487Sadrian * these two routines.
425251487Sadrian */
426251487Sadrianint
427251487Sadrianath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad)
428251487Sadrian{
429251487Sadrian	unsigned int id = ad->ad_id & ATH_DIAG_ID;
430251487Sadrian	void *indata = NULL;
431251487Sadrian	void *outdata = NULL;
432251487Sadrian	u_int32_t insize = ad->ad_in_size;
433251487Sadrian	u_int32_t outsize = ad->ad_out_size;
434251487Sadrian	int error = 0;
435251487Sadrian//	int val;
436251487Sadrian
437251487Sadrian	if (ad->ad_id & ATH_DIAG_IN) {
438251487Sadrian		/*
439251487Sadrian		 * Copy in data.
440251487Sadrian		 */
441251487Sadrian		indata = malloc(insize, M_TEMP, M_NOWAIT);
442251487Sadrian		if (indata == NULL) {
443251487Sadrian			error = ENOMEM;
444251487Sadrian			goto bad;
445251487Sadrian		}
446251487Sadrian		error = copyin(ad->ad_in_data, indata, insize);
447251487Sadrian		if (error)
448251487Sadrian			goto bad;
449251487Sadrian	}
450251487Sadrian	if (ad->ad_id & ATH_DIAG_DYN) {
451251487Sadrian		/*
452251487Sadrian		 * Allocate a buffer for the results (otherwise the HAL
453251487Sadrian		 * returns a pointer to a buffer where we can read the
454251487Sadrian		 * results).  Note that we depend on the HAL leaving this
455251487Sadrian		 * pointer for us to use below in reclaiming the buffer;
456251487Sadrian		 * may want to be more defensive.
457251487Sadrian		 */
458251487Sadrian		outdata = malloc(outsize, M_TEMP, M_NOWAIT);
459251487Sadrian		if (outdata == NULL) {
460251487Sadrian			error = ENOMEM;
461251487Sadrian			goto bad;
462251487Sadrian		}
463251487Sadrian	}
464251487Sadrian	switch (id) {
465251487Sadrian		default:
466251487Sadrian			error = EINVAL;
467251487Sadrian	}
468251487Sadrian	if (outsize < ad->ad_out_size)
469251487Sadrian		ad->ad_out_size = outsize;
470251487Sadrian	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
471251487Sadrian		error = EFAULT;
472251487Sadrianbad:
473251487Sadrian	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
474251487Sadrian		free(indata, M_TEMP);
475251487Sadrian	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
476251487Sadrian		free(outdata, M_TEMP);
477251487Sadrian	return (error);
478251487Sadrian}
479251487Sadrian
480