1/*-
2 * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/cdefs.h>
18__FBSDID("$FreeBSD$");
19
20#include "opt_wlan.h"
21
22#include <sys/param.h>
23#include <sys/lock.h>
24#include <sys/mutex.h>
25#include <sys/mbuf.h>
26#include <sys/kernel.h>
27#include <sys/socket.h>
28#include <sys/systm.h>
29#include <sys/malloc.h>
30#include <sys/queue.h>
31#include <sys/taskqueue.h>
32#include <sys/bus.h>
33#include <sys/endian.h>
34
35#include <net/if.h>
36#include <net/ethernet.h>
37#include <net/if_media.h>
38
39#include <net80211/ieee80211_var.h>
40#include <net80211/ieee80211_radiotap.h>
41
42#include <dev/rtwn/if_rtwnvar.h>
43
44#include <dev/rtwn/if_rtwn_task.h>
45
46static void
47rtwn_cmdq_cb(void *arg, int pending)
48{
49	struct rtwn_softc *sc = arg;
50	struct rtwn_cmdq *item;
51
52	/*
53	 * Device must be powered on (via rtwn_power_on())
54	 * before any command may be sent.
55	 */
56	RTWN_LOCK(sc);
57	if (!(sc->sc_flags & RTWN_RUNNING)) {
58		RTWN_UNLOCK(sc);
59		return;
60	}
61
62	RTWN_CMDQ_LOCK(sc);
63	while (sc->cmdq[sc->cmdq_first].func != NULL) {
64		item = &sc->cmdq[sc->cmdq_first];
65		sc->cmdq_first = (sc->cmdq_first + 1) % RTWN_CMDQ_SIZE;
66		RTWN_CMDQ_UNLOCK(sc);
67
68		item->func(sc, &item->data);
69
70		RTWN_CMDQ_LOCK(sc);
71		memset(item, 0, sizeof (*item));
72	}
73	RTWN_CMDQ_UNLOCK(sc);
74	RTWN_UNLOCK(sc);
75}
76
77void
78rtwn_cmdq_init(struct rtwn_softc *sc)
79{
80	RTWN_CMDQ_LOCK_INIT(sc);
81	TASK_INIT(&sc->cmdq_task, 0, rtwn_cmdq_cb, sc);
82}
83
84void
85rtwn_cmdq_destroy(struct rtwn_softc *sc)
86{
87	if (RTWN_CMDQ_LOCK_INITIALIZED(sc))
88		RTWN_CMDQ_LOCK_DESTROY(sc);
89}
90
91int
92rtwn_cmd_sleepable(struct rtwn_softc *sc, const void *ptr, size_t len,
93    CMD_FUNC_PROTO)
94{
95	struct ieee80211com *ic = &sc->sc_ic;
96
97	KASSERT(len <= sizeof(union sec_param), ("buffer overflow"));
98
99	RTWN_CMDQ_LOCK(sc);
100	if (sc->sc_detached) {
101		RTWN_CMDQ_UNLOCK(sc);
102		return (ESHUTDOWN);
103	}
104
105	if (sc->cmdq[sc->cmdq_last].func != NULL) {
106		device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__);
107		RTWN_CMDQ_UNLOCK(sc);
108
109		return (EAGAIN);
110	}
111
112	if (ptr != NULL)
113		memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len);
114	sc->cmdq[sc->cmdq_last].func = func;
115	sc->cmdq_last = (sc->cmdq_last + 1) % RTWN_CMDQ_SIZE;
116	RTWN_CMDQ_UNLOCK(sc);
117
118	ieee80211_runtask(ic, &sc->cmdq_task);
119
120	return (0);
121}
122