aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_bta.c')
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_bta.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.c b/drivers/net/wireless/bcmdhd/dhd_bta.c
new file mode 100644
index 00000000000..6b782ea4a4d
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/dhd_bta.c
@@ -0,0 +1,335 @@
1/*
2 * BT-AMP support routines
3 *
4 * Copyright (C) 1999-2011, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhd_bta.c,v 1.10.4.2 2010-12-22 23:47:23 Exp $
25 */
26
27#include <typedefs.h>
28#include <osl.h>
29#include <bcmcdc.h>
30#include <bcmutils.h>
31#include <bcmendian.h>
32#include <proto/802.11.h>
33#include <proto/802.11_bta.h>
34#include <proto/bt_amp_hci.h>
35#include <dngl_stats.h>
36#include <dhd.h>
37#include <dhd_bus.h>
38#include <dhd_proto.h>
39#include <dhdioctl.h>
40#include <dhd_dbg.h>
41
42#include <dhd_bta.h>
43
44
45#ifdef SEND_HCI_CMD_VIA_IOCTL
46#define BTA_HCI_CMD_MAX_LEN HCI_CMD_PREAMBLE_SIZE + HCI_CMD_DATA_SIZE
47
48/* Send HCI cmd via wl iovar HCI_cmd to the dongle. */
49int
50dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len)
51{
52 amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf;
53 uint8 buf[BTA_HCI_CMD_MAX_LEN + 16];
54 uint len = sizeof(buf);
55 wl_ioctl_t ioc;
56
57 if (cmd_len < HCI_CMD_PREAMBLE_SIZE)
58 return BCME_BADLEN;
59
60 if ((uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE > cmd_len)
61 return BCME_BADLEN;
62
63 len = bcm_mkiovar("HCI_cmd",
64 (char *)cmd, (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE, (char *)buf, len);
65
66
67 memset(&ioc, 0, sizeof(ioc));
68
69 ioc.cmd = WLC_SET_VAR;
70 ioc.buf = buf;
71 ioc.len = len;
72 ioc.set = TRUE;
73
74 return dhd_wl_ioctl(pub, &ioc, ioc.buf, ioc.len);
75}
76#else /* !SEND_HCI_CMD_VIA_IOCTL */
77
78static void
79dhd_bta_flush_hcidata(dhd_pub_t *pub, uint16 llh)
80{
81 int prec;
82 struct pktq *q;
83 uint count = 0;
84
85 q = dhd_bus_txq(pub->bus);
86 if (q == NULL)
87 return;
88
89 DHD_BTA(("dhd: flushing HCI ACL data for logical link %u...\n", llh));
90
91 dhd_os_sdlock_txq(pub);
92
93 /* Walk through the txq and toss all HCI ACL data packets */
94 PKTQ_PREC_ITER(q, prec) {
95 void *head_pkt = NULL;
96
97 while (pktq_ppeek(q, prec) != head_pkt) {
98 void *pkt = pktq_pdeq(q, prec);
99 int ifidx;
100
101 PKTPULL(pub->osh, pkt, dhd_bus_hdrlen(pub->bus));
102 dhd_prot_hdrpull(pub, &ifidx, pkt);
103
104 if (PKTLEN(pub->osh, pkt) >= RFC1042_HDR_LEN) {
105 struct ether_header *eh =
106 (struct ether_header *)PKTDATA(pub->osh, pkt);
107
108 if (ntoh16(eh->ether_type) < ETHER_TYPE_MIN) {
109 struct dot11_llc_snap_header *lsh =
110 (struct dot11_llc_snap_header *)&eh[1];
111
112 if (bcmp(lsh, BT_SIG_SNAP_MPROT,
113 DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
114 ntoh16(lsh->type) == BTA_PROT_L2CAP) {
115 amp_hci_ACL_data_t *ACL_data =
116 (amp_hci_ACL_data_t *)&lsh[1];
117 uint16 handle = ltoh16(ACL_data->handle);
118
119 if (HCI_ACL_DATA_HANDLE(handle) == llh) {
120 PKTFREE(pub->osh, pkt, TRUE);
121 count ++;
122 continue;
123 }
124 }
125 }
126 }
127
128 dhd_prot_hdrpush(pub, ifidx, pkt);
129 PKTPUSH(pub->osh, pkt, dhd_bus_hdrlen(pub->bus));
130
131 if (head_pkt == NULL)
132 head_pkt = pkt;
133 pktq_penq(q, prec, pkt);
134 }
135 }
136
137 dhd_os_sdunlock_txq(pub);
138
139 DHD_BTA(("dhd: flushed %u packet(s) for logical link %u...\n", count, llh));
140}
141
142/* Handle HCI cmd locally.
143 * Return 0: continue to send the cmd across SDIO
144 * < 0: stop, fail
145 * > 0: stop, succuess
146 */
147static int
148_dhd_bta_docmd(dhd_pub_t *pub, amp_hci_cmd_t *cmd)
149{
150 int status = 0;
151
152 switch (ltoh16_ua((uint8 *)&cmd->opcode)) {
153 case HCI_Enhanced_Flush: {
154 eflush_cmd_parms_t *cmdparms = (eflush_cmd_parms_t *)cmd->parms;
155 dhd_bta_flush_hcidata(pub, ltoh16_ua(cmdparms->llh));
156 break;
157 }
158 default:
159 break;
160 }
161
162 return status;
163}
164
165/* Send HCI cmd encapsulated in BT-SIG frame via data channel to the dongle. */
166int
167dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len)
168{
169 amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf;
170 struct ether_header *eh;
171 struct dot11_llc_snap_header *lsh;
172 osl_t *osh = pub->osh;
173 uint len;
174 void *p;
175 int status;
176
177 if (cmd_len < HCI_CMD_PREAMBLE_SIZE) {
178 DHD_ERROR(("dhd_bta_docmd: short command, cmd_len %u\n", cmd_len));
179 return BCME_BADLEN;
180 }
181
182 if ((len = (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE) > cmd_len) {
183 DHD_ERROR(("dhd_bta_docmd: malformed command, len %u cmd_len %u\n",
184 len, cmd_len));
185 /* return BCME_BADLEN; */
186 }
187
188 p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE);
189 if (p == NULL) {
190 DHD_ERROR(("dhd_bta_docmd: out of memory\n"));
191 return BCME_NOMEM;
192 }
193
194
195 /* intercept and handle the HCI cmd locally */
196 if ((status = _dhd_bta_docmd(pub, cmd)) > 0)
197 return 0;
198 else if (status < 0)
199 return status;
200
201 /* copy in HCI cmd */
202 PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN);
203 bcopy(cmd, PKTDATA(osh, p), len);
204
205 /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */
206 PKTPUSH(osh, p, RFC1042_HDR_LEN);
207 eh = (struct ether_header *)PKTDATA(osh, p);
208 bzero(eh->ether_dhost, ETHER_ADDR_LEN);
209 ETHER_SET_LOCALADDR(eh->ether_dhost);
210 bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN);
211 eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN);
212 lsh = (struct dot11_llc_snap_header *)&eh[1];
213 bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2);
214 lsh->type = 0;
215
216 return dhd_sendpkt(pub, 0, p);
217}
218#endif /* !SEND_HCI_CMD_VIA_IOCTL */
219
220/* Send HCI ACL data to dongle via data channel */
221int
222dhd_bta_tx_hcidata(dhd_pub_t *pub, void *data_buf, uint data_len)
223{
224 amp_hci_ACL_data_t *data = (amp_hci_ACL_data_t *)data_buf;
225 struct ether_header *eh;
226 struct dot11_llc_snap_header *lsh;
227 osl_t *osh = pub->osh;
228 uint len;
229 void *p;
230
231 if (data_len < HCI_ACL_DATA_PREAMBLE_SIZE) {
232 DHD_ERROR(("dhd_bta_tx_hcidata: short data_buf, data_len %u\n", data_len));
233 return BCME_BADLEN;
234 }
235
236 if ((len = (uint)ltoh16(data->dlen) + HCI_ACL_DATA_PREAMBLE_SIZE) > data_len) {
237 DHD_ERROR(("dhd_bta_tx_hcidata: malformed hci data, len %u data_len %u\n",
238 len, data_len));
239 /* return BCME_BADLEN; */
240 }
241
242 p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE);
243 if (p == NULL) {
244 DHD_ERROR(("dhd_bta_tx_hcidata: out of memory\n"));
245 return BCME_NOMEM;
246 }
247
248
249 /* copy in HCI ACL data header and HCI ACL data */
250 PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN);
251 bcopy(data, PKTDATA(osh, p), len);
252
253 /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */
254 PKTPUSH(osh, p, RFC1042_HDR_LEN);
255 eh = (struct ether_header *)PKTDATA(osh, p);
256 bzero(eh->ether_dhost, ETHER_ADDR_LEN);
257 bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN);
258 eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN);
259 lsh = (struct dot11_llc_snap_header *)&eh[1];
260 bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2);
261 lsh->type = HTON16(BTA_PROT_L2CAP);
262
263 return dhd_sendpkt(pub, 0, p);
264}
265
266/* txcomplete callback */
267void
268dhd_bta_tx_hcidata_complete(dhd_pub_t *dhdp, void *txp, bool success)
269{
270 uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, txp);
271 amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)(pktdata + RFC1042_HDR_LEN);
272 uint16 handle = ltoh16(ACL_data->handle);
273 uint16 llh = HCI_ACL_DATA_HANDLE(handle);
274
275 wl_event_msg_t event;
276 uint8 data[HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t)];
277 amp_hci_event_t *evt;
278 num_completed_data_blocks_evt_parms_t *parms;
279
280 uint16 len = HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t);
281
282 /* update the event struct */
283 memset(&event, 0, sizeof(event));
284 event.version = hton16(BCM_EVENT_MSG_VERSION);
285 event.event_type = hton32(WLC_E_BTA_HCI_EVENT);
286 event.status = 0;
287 event.reason = 0;
288 event.auth_type = 0;
289 event.datalen = hton32(len);
290 event.flags = 0;
291
292 /* generate Number of Completed Blocks event */
293 evt = (amp_hci_event_t *)data;
294 evt->ecode = HCI_Number_of_Completed_Data_Blocks;
295 evt->plen = sizeof(num_completed_data_blocks_evt_parms_t);
296
297 parms = (num_completed_data_blocks_evt_parms_t *)evt->parms;
298 htol16_ua_store(dhdp->maxdatablks, (uint8 *)&parms->num_blocks);
299 parms->num_handles = 1;
300 htol16_ua_store(llh, (uint8 *)&parms->completed[0].handle);
301 parms->completed[0].pkts = 1;
302 parms->completed[0].blocks = 1;
303
304 dhd_sendup_event_common(dhdp, &event, data);
305}
306
307/* event callback */
308void
309dhd_bta_doevt(dhd_pub_t *dhdp, void *data_buf, uint data_len)
310{
311 amp_hci_event_t *evt = (amp_hci_event_t *)data_buf;
312
313 switch (evt->ecode) {
314 case HCI_Command_Complete: {
315 cmd_complete_parms_t *parms = (cmd_complete_parms_t *)evt->parms;
316 switch (ltoh16_ua((uint8 *)&parms->opcode)) {
317 case HCI_Read_Data_Block_Size: {
318 read_data_block_size_evt_parms_t *parms2 =
319 (read_data_block_size_evt_parms_t *)parms->parms;
320 dhdp->maxdatablks = ltoh16_ua((uint8 *)&parms2->data_block_num);
321 break;
322 }
323 }
324 break;
325 }
326
327 case HCI_Flush_Occurred: {
328 flush_occurred_evt_parms_t *evt_parms = (flush_occurred_evt_parms_t *)evt->parms;
329 dhd_bta_flush_hcidata(dhdp, ltoh16_ua((uint8 *)&evt_parms->handle));
330 break;
331 }
332 default:
333 break;
334 }
335}