acx100.c revision 1.28
1/*	$OpenBSD: acx100.c,v 1.28 2022/01/09 05:42:38 jsg Exp $ */
2
3/*
4 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
21 *
22 * This code is derived from software contributed to The DragonFly Project
23 * by Sepherosa Ziehau <sepherosa@gmail.com>
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 *
29 * 1. Redistributions of source code must retain the above copyright
30 *    notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 *    notice, this list of conditions and the following disclaimer in
33 *    the documentation and/or other materials provided with the
34 *    distribution.
35 * 3. Neither the name of The DragonFly Project nor the names of its
36 *    contributors may be used to endorse or promote products derived
37 *    from this software without specific, prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
40 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
41 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
42 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
43 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
44 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
45 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
47 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 */
52
53#include "bpfilter.h"
54
55#include <sys/param.h>
56#include <sys/systm.h>
57#include <sys/mbuf.h>
58#include <sys/endian.h>
59#include <sys/socket.h>
60#include <sys/device.h>
61
62#include <machine/bus.h>
63
64#include <net/if.h>
65#include <net/if_media.h>
66
67#include <netinet/in.h>
68#include <netinet/if_ether.h>
69
70#include <net80211/ieee80211_var.h>
71#include <net80211/ieee80211_amrr.h>
72#include <net80211/ieee80211_radiotap.h>
73
74#include <dev/pci/pcireg.h>
75
76#include <dev/ic/acxvar.h>
77#include <dev/ic/acxreg.h>
78
79#define ACX100_CONF_FW_RING	0x0003
80#define ACX100_CONF_MEMOPT	0x0005
81
82#define ACX100_INTR_ENABLE	(ACXRV_INTR_TX_FINI | ACXRV_INTR_RX_FINI)
83/*
84 * XXX do we really care about following interrupts?
85 *
86 * ACXRV_INTR_INFO | ACXRV_INTR_SCAN_FINI
87 */
88
89#define ACX100_INTR_DISABLE	(uint16_t)~(ACXRV_INTR_UNKN)
90
91#define ACX100_RATE(rate)	((rate) * 5)
92
93#define ACX100_TXPOWER		18
94#define ACX100_GPIO_POWER_LED	0x0800
95#define ACX100_EE_EADDR_OFS	0x1a
96
97#define ACX100_FW_TXRING_SIZE	(ACX_TX_DESC_CNT * sizeof(struct acx_fw_txdesc))
98#define ACX100_FW_RXRING_SIZE	(ACX_RX_DESC_CNT * sizeof(struct acx_fw_rxdesc))
99
100int	acx100_init(struct acx_softc *);
101int	acx100_init_wep(struct acx_softc *);
102int	acx100_init_tmplt(struct acx_softc *);
103int	acx100_init_fw_ring(struct acx_softc *);
104int	acx100_init_memory(struct acx_softc *);
105void	acx100_init_fw_txring(struct acx_softc *, uint32_t);
106void	acx100_init_fw_rxring(struct acx_softc *, uint32_t);
107int	acx100_read_config(struct acx_softc *, struct acx_config *);
108int	acx100_write_config(struct acx_softc *, struct acx_config *);
109int	acx100_set_txpower(struct acx_softc *);
110void	acx100_set_fw_txdesc_rate(struct acx_softc *,
111	    struct acx_txbuf *, int);
112void	acx100_set_bss_join_param(struct acx_softc *, void *, int);
113int	acx100_set_wepkey(struct acx_softc *, struct ieee80211_key *, int);
114void	acx100_proc_wep_rxbuf(struct acx_softc *, struct mbuf *, int *);
115
116/*
117 * NOTE:
118 * Following structs' fields are little endian
119 */
120struct acx100_bss_join {
121	uint8_t	dtim_intvl;
122	uint8_t	basic_rates;
123	uint8_t	all_rates;
124} __packed;
125
126struct acx100_conf_fw_ring {
127	struct acx_conf	confcom;
128	uint32_t	fw_ring_size;	/* total size of fw (tx + rx) ring */
129	uint32_t	fw_rxring_addr;	/* start phyaddr of fw rx desc */
130	uint8_t		opt;		/* see ACX100_RINGOPT_ */
131	uint8_t		fw_txring_num;	/* num of TX ring */
132	uint8_t		fw_rxdesc_num;	/* num of fw rx desc */
133	uint8_t		reserved0;
134	uint32_t	fw_ring_end[2];	/* see ACX100_SET_RING_END() */
135	uint32_t	fw_txring_addr;	/* start phyaddr of fw tx desc */
136	uint8_t		fw_txring_prio;	/* see ACX100_TXRING_PRIO_ */
137	uint8_t		fw_txdesc_num;	/* num of fw tx desc */
138	uint16_t	reserved1;
139} __packed;
140
141#define ACX100_RINGOPT_AUTO_RESET	0x1
142#define ACX100_TXRING_PRIO_DEFAULT	0
143#define ACX100_SET_RING_END(conf, end)			\
144do {							\
145	(conf)->fw_ring_end[0] = htole32(end);		\
146	(conf)->fw_ring_end[1] = htole32(end + 8);	\
147} while (0)
148
149struct acx100_conf_memblk_size {
150	struct acx_conf	confcom;
151	uint16_t	memblk_size;	/* size of each mem block */
152} __packed;
153
154struct acx100_conf_mem {
155	struct acx_conf	confcom;
156	uint32_t	opt;		/* see ACX100_MEMOPT_ */
157	uint32_t	h_rxring_paddr;	/* host rx desc start phyaddr */
158
159	/*
160	 * Memory blocks are controlled by hardware
161	 * once after they are initialized
162	 */
163	uint32_t	rx_memblk_addr;	/* start addr of rx mem blocks */
164	uint32_t	tx_memblk_addr;	/* start addr of tx mem blocks */
165	uint16_t	rx_memblk_num;	/* num of RX mem block */
166	uint16_t	tx_memblk_num;	/* num of TX mem block */
167} __packed;
168
169#define ACX100_MEMOPT_MEM_INSTR		0x00000000 /* memory access instruct */
170#define ACX100_MEMOPT_HOSTDESC		0x00010000 /* host indirect desc */
171#define ACX100_MEMOPT_MEMBLOCK		0x00020000 /* local mem block list */
172#define ACX100_MEMOPT_IO_INSTR		0x00040000 /* IO instruct */
173#define ACX100_MEMOPT_PCICONF		0x00080000 /* PCI conf space */
174
175#define ACX100_MEMBLK_ALIGN		0x20
176
177struct acx100_conf_cca_mode {
178	struct acx_conf	confcom;
179	uint8_t		cca_mode;
180	uint8_t		unknown;
181} __packed;
182
183struct acx100_conf_ed_thresh {
184	struct acx_conf	confcom;
185	uint8_t		ed_thresh;
186	uint8_t		unknown[3];
187} __packed;
188
189struct acx100_conf_wepkey {
190	struct acx_conf	confcom;
191	uint8_t		action;	/* see ACX100_WEPKEY_ACT_ */
192	uint8_t		key_len;
193	uint8_t		key_idx;
194#define ACX100_WEPKEY_LEN	29
195	uint8_t		key[ACX100_WEPKEY_LEN];
196} __packed;
197
198#define ACX100_WEPKEY_ACT_ADD	1
199
200static const uint16_t	acx100_reg[ACXREG_MAX] = {
201	ACXREG(SOFT_RESET,		0x0000),
202
203	ACXREG(FWMEM_ADDR,		0x0014),
204	ACXREG(FWMEM_DATA,		0x0018),
205	ACXREG(FWMEM_CTRL,		0x001c),
206	ACXREG(FWMEM_START,		0x0020),
207
208	ACXREG(EVENT_MASK,		0x0034),
209
210	ACXREG(INTR_TRIG,		0x007c),
211	ACXREG(INTR_MASK,		0x0098),
212	ACXREG(INTR_STATUS,		0x00a4),
213	ACXREG(INTR_STATUS_CLR,		0x00a8),
214	ACXREG(INTR_ACK,		0x00ac),
215
216	ACXREG(HINTR_TRIG,		0x00b0),
217	ACXREG(RADIO_ENABLE,		0x0104),
218
219	ACXREG(EEPROM_INIT,		0x02d0),
220	ACXREG(EEPROM_CTRL,		0x0250),
221	ACXREG(EEPROM_ADDR,		0x0254),
222	ACXREG(EEPROM_DATA,		0x0258),
223	ACXREG(EEPROM_CONF,		0x025c),
224	ACXREG(EEPROM_INFO,		0x02ac),
225
226	ACXREG(PHY_ADDR,		0x0268),
227	ACXREG(PHY_DATA,		0x026c),
228	ACXREG(PHY_CTRL,		0x0270),
229
230	ACXREG(GPIO_OUT_ENABLE,		0x0290),
231	ACXREG(GPIO_OUT,		0x0298),
232
233	ACXREG(CMD_REG_OFFSET,		0x02a4),
234	ACXREG(INFO_REG_OFFSET,		0x02a8),
235
236	ACXREG(RESET_SENSE,		0x02d4),
237	ACXREG(ECPU_CTRL,		0x02d8)
238};
239
240static const uint8_t	acx100_txpower_maxim[21] = {
241	63, 63, 63, 62,
242	61, 61, 60, 60,
243	59, 58, 57, 55,
244	53, 50, 47, 43,
245	38, 31, 23, 13,
246	0
247};
248
249static const uint8_t	acx100_txpower_rfmd[21] = {
250	 0,  0,  0,  1,
251	 2,  2,  3,  3,
252	 4,  5,  6,  8,
253	10, 13, 16, 20,
254	25, 32, 41, 50,
255	63
256};
257
258void
259acx100_set_param(struct acx_softc *sc)
260{
261	sc->chip_mem1_rid = PCIR_BAR(1);
262	sc->chip_mem2_rid = PCIR_BAR(2);
263	sc->chip_ioreg = acx100_reg;
264	sc->chip_hw_crypt = 1;
265	sc->chip_intr_enable = ACX100_INTR_ENABLE;
266#ifndef IEEE80211_STA_ONLY
267	sc->chip_intr_enable |= ACXRV_INTR_DTIM;
268#endif
269	sc->chip_intr_disable = ACX100_INTR_DISABLE;
270	sc->chip_gpio_pled = ACX100_GPIO_POWER_LED;
271	sc->chip_ee_eaddr_ofs = ACX100_EE_EADDR_OFS;
272	sc->chip_txdesc1_len = ACX_FRAME_HDRLEN;
273	sc->chip_fw_txdesc_ctrl = DESC_CTRL_AUTODMA |
274	    DESC_CTRL_RECLAIM | DESC_CTRL_FIRST_FRAG;
275
276	sc->chip_phymode = IEEE80211_MODE_11B;
277	sc->chip_chan_flags = IEEE80211_CHAN_B;
278	sc->sc_ic.ic_phytype = IEEE80211_T_DS;
279	sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
280
281	sc->chip_init = acx100_init;
282	sc->chip_set_wepkey = acx100_set_wepkey;
283	sc->chip_read_config = acx100_read_config;
284	sc->chip_write_config = acx100_write_config;
285	sc->chip_set_fw_txdesc_rate = acx100_set_fw_txdesc_rate;
286	sc->chip_set_bss_join_param = acx100_set_bss_join_param;
287	sc->chip_proc_wep_rxbuf = acx100_proc_wep_rxbuf;
288}
289
290int
291acx100_init(struct acx_softc *sc)
292{
293	struct ifnet *ifp = &sc->sc_ic.ic_if;
294
295	/*
296	 * NOTE:
297	 * Order of initialization:
298	 * 1) WEP
299	 * 2) Templates
300	 * 3) Firmware TX/RX ring
301	 * 4) Hardware memory
302	 * Above order is critical to get a correct memory map
303	 */
304	if (acx100_init_wep(sc) != 0) {
305		printf("%s: %s can't initialize wep\n",
306		    ifp->if_xname, __func__);
307		return (ENXIO);
308	}
309
310	if (acx100_init_tmplt(sc) != 0) {
311		printf("%s: %s can't initialize templates\n",
312		    ifp->if_xname, __func__);
313		return (ENXIO);
314	}
315
316	if (acx100_init_fw_ring(sc) != 0) {
317		printf("%s: %s can't initialize fw ring\n",
318		    ifp->if_xname, __func__);
319		return (ENXIO);
320	}
321
322	if (acx100_init_memory(sc) != 0) {
323		printf("%s: %s can't initialize hw memory\n",
324		    ifp->if_xname, __func__);
325		return (ENXIO);
326	}
327
328	return (0);
329}
330
331int
332acx100_init_wep(struct acx_softc *sc)
333{
334	struct acx_conf_wepopt wep_opt;
335	struct acx_conf_mmap mem_map;
336	struct ifnet *ifp = &sc->sc_ic.ic_if;
337
338	/* Set WEP cache start/end address */
339	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
340		printf("%s: can't get mmap\n", ifp->if_xname);
341		return (1);
342	}
343
344	mem_map.wep_cache_start = htole32(letoh32(mem_map.code_end) + 4);
345	mem_map.wep_cache_end = htole32(letoh32(mem_map.code_end) + 4);
346	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
347		printf("%s: can't set mmap\n", ifp->if_xname);
348		return (1);
349	}
350
351	/* Set WEP options */
352	wep_opt.nkey = htole16(IEEE80211_WEP_NKID + 10);
353	wep_opt.opt = WEPOPT_HDWEP;
354	if (acx_set_conf(sc, ACX_CONF_WEPOPT, &wep_opt, sizeof(wep_opt)) != 0) {
355		printf("%s: can't set wep opt\n", ifp->if_xname);
356		return (1);
357	}
358
359	return (0);
360}
361
362int
363acx100_init_tmplt(struct acx_softc *sc)
364{
365	struct acx_conf_mmap mem_map;
366	struct ifnet *ifp = &sc->sc_ic.ic_if;
367
368	/* Set templates start address */
369	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
370		printf("%s: can't get mmap\n", ifp->if_xname);
371		return (1);
372	}
373
374	mem_map.pkt_tmplt_start = mem_map.wep_cache_end;
375	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
376		printf("%s: can't set mmap\n", ifp->if_xname);
377		return (1);
378	}
379
380	/* Initialize various packet templates */
381	if (acx_init_tmplt_ordered(sc) != 0) {
382		printf("%s: can't init tmplt\n", ifp->if_xname);
383		return (1);
384	}
385
386	return (0);
387}
388
389int
390acx100_init_fw_ring(struct acx_softc *sc)
391{
392	struct acx100_conf_fw_ring ring;
393	struct acx_conf_mmap mem_map;
394	struct ifnet *ifp = &sc->sc_ic.ic_if;
395	uint32_t txring_start, rxring_start, ring_end;
396
397	/* Set firmware descriptor ring start address */
398	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
399		printf("%s: can't get mmap\n", ifp->if_xname);
400		return (1);
401	}
402
403	txring_start = letoh32(mem_map.pkt_tmplt_end) + 4;
404	rxring_start = txring_start + ACX100_FW_TXRING_SIZE;
405	ring_end = rxring_start + ACX100_FW_RXRING_SIZE;
406
407	mem_map.fw_desc_start = htole32(txring_start);
408	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
409		printf("%s: can't set mmap\n", ifp->if_xname);
410		return (1);
411	}
412
413	/* Set firmware descriptor ring configure */
414	bzero(&ring, sizeof(ring));
415	ring.fw_ring_size = htole32(ACX100_FW_TXRING_SIZE +
416	    ACX100_FW_RXRING_SIZE + 8);
417
418	ring.fw_txring_num = 1;
419	ring.fw_txring_addr = htole32(txring_start);
420	ring.fw_txring_prio = ACX100_TXRING_PRIO_DEFAULT;
421	ring.fw_txdesc_num = 0; /* XXX ignored?? */
422
423	ring.fw_rxring_addr = htole32(rxring_start);
424	ring.fw_rxdesc_num = 0; /* XXX ignored?? */
425
426	ring.opt = ACX100_RINGOPT_AUTO_RESET;
427	ACX100_SET_RING_END(&ring, ring_end);
428	if (acx_set_conf(sc, ACX100_CONF_FW_RING, &ring, sizeof(ring)) != 0) {
429		printf("%s: can't set fw ring configure\n", ifp->if_xname);
430		return (1);
431	}
432
433	/* Setup firmware TX/RX descriptor ring */
434	acx100_init_fw_txring(sc, txring_start);
435	acx100_init_fw_rxring(sc, rxring_start);
436
437	return (0);
438}
439
440#define MEMBLK_ALIGN(addr)	\
441    (((addr) + (ACX100_MEMBLK_ALIGN - 1)) & ~(ACX100_MEMBLK_ALIGN - 1))
442
443int
444acx100_init_memory(struct acx_softc *sc)
445{
446	struct acx100_conf_memblk_size memblk_sz;
447	struct acx100_conf_mem mem;
448	struct acx_conf_mmap mem_map;
449	struct ifnet *ifp = &sc->sc_ic.ic_if;
450	uint32_t memblk_start, memblk_end;
451	int total_memblk, txblk_num, rxblk_num;
452
453	/* Set memory block start address */
454	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
455		printf("%s: can't get mmap\n", ifp->if_xname);
456		return (1);
457	}
458
459	mem_map.memblk_start =
460	    htole32(MEMBLK_ALIGN(letoh32(mem_map.fw_desc_end) + 4));
461
462	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
463		printf("%s: can't set mmap\n", ifp->if_xname);
464		return (1);
465	}
466
467	/* Set memory block size */
468	memblk_sz.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
469	if (acx_set_conf(sc, ACX_CONF_MEMBLK_SIZE, &memblk_sz,
470	    sizeof(memblk_sz)) != 0) {
471		printf("%s: can't set mem block size\n", ifp->if_xname);
472		return (1);
473	}
474
475	/* Get memory map after setting it */
476	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
477		printf("%s: can't get mmap again\n", ifp->if_xname);
478		return (1);
479	}
480	memblk_start = letoh32(mem_map.memblk_start);
481	memblk_end = letoh32(mem_map.memblk_end);
482
483	/* Set memory options */
484	mem.opt = htole32(ACX100_MEMOPT_MEMBLOCK | ACX100_MEMOPT_HOSTDESC);
485	mem.h_rxring_paddr = htole32(sc->sc_ring_data.rx_ring_paddr);
486
487	total_memblk = (memblk_end - memblk_start) / ACX_MEMBLOCK_SIZE;
488
489	rxblk_num = total_memblk / 2;		/* 50% */
490	txblk_num = total_memblk - rxblk_num;	/* 50% */
491
492	DPRINTF(("%s: \ttotal memory blocks\t%d\n"
493	    "\trx memory blocks\t%d\n"
494	    "\ttx memory blocks\t%d\n",
495	    ifp->if_xname, total_memblk, rxblk_num, txblk_num));
496
497	mem.rx_memblk_num = htole16(rxblk_num);
498	mem.tx_memblk_num = htole16(txblk_num);
499
500	mem.rx_memblk_addr = htole32(MEMBLK_ALIGN(memblk_start));
501	mem.tx_memblk_addr = htole32(MEMBLK_ALIGN(memblk_start +
502	    (ACX_MEMBLOCK_SIZE * rxblk_num)));
503
504	if (acx_set_conf(sc, ACX100_CONF_MEMOPT, &mem, sizeof(mem)) != 0) {
505		printf("%s: can't set mem options\n", ifp->if_xname);
506		return (1);
507	}
508
509	/* Initialize memory */
510	if (acx_exec_command(sc, ACXCMD_INIT_MEM, NULL, 0, NULL, 0) != 0) {
511		printf("%s: can't init mem\n", ifp->if_xname);
512		return (1);
513	}
514
515	return (0);
516}
517
518#undef MEMBLK_ALIGN
519
520void
521acx100_init_fw_txring(struct acx_softc *sc, uint32_t fw_txdesc_start)
522{
523	struct acx_fw_txdesc fw_desc;
524	struct acx_txbuf *tx_buf;
525	uint32_t desc_paddr, fw_desc_offset;
526	int i;
527
528	bzero(&fw_desc, sizeof(fw_desc));
529	fw_desc.f_tx_ctrl = DESC_CTRL_HOSTOWN | DESC_CTRL_RECLAIM |
530	    DESC_CTRL_AUTODMA | DESC_CTRL_FIRST_FRAG;
531
532	tx_buf = sc->sc_buf_data.tx_buf;
533	fw_desc_offset = fw_txdesc_start;
534	desc_paddr = sc->sc_ring_data.tx_ring_paddr;
535
536	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
537		fw_desc.f_tx_host_desc = htole32(desc_paddr);
538
539		if (i == ACX_TX_DESC_CNT - 1) {
540			fw_desc.f_tx_next_desc = htole32(fw_txdesc_start);
541		} else {
542			fw_desc.f_tx_next_desc = htole32(fw_desc_offset +
543			    sizeof(struct acx_fw_txdesc));
544		}
545
546		tx_buf[i].tb_fwdesc_ofs = fw_desc_offset;
547		DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
548		    sizeof(fw_desc));
549
550		desc_paddr += (2 * sizeof(struct acx_host_desc));
551		fw_desc_offset += sizeof(fw_desc);
552	}
553}
554
555void
556acx100_init_fw_rxring(struct acx_softc *sc, uint32_t fw_rxdesc_start)
557{
558	struct acx_fw_rxdesc fw_desc;
559	uint32_t fw_desc_offset;
560	int i;
561
562	bzero(&fw_desc, sizeof(fw_desc));
563	fw_desc.f_rx_ctrl = DESC_CTRL_RECLAIM | DESC_CTRL_AUTODMA;
564
565	fw_desc_offset = fw_rxdesc_start;
566
567	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
568		if (i == ACX_RX_DESC_CNT - 1) {
569			fw_desc.f_rx_next_desc = htole32(fw_rxdesc_start);
570		} else {
571			fw_desc.f_rx_next_desc =
572			    htole32(fw_desc_offset +
573			    sizeof(struct acx_fw_rxdesc));
574		}
575
576		DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
577		    sizeof(fw_desc));
578
579		fw_desc_offset += sizeof(fw_desc);
580	}
581}
582
583int
584acx100_read_config(struct acx_softc *sc, struct acx_config *conf)
585{
586	struct acx100_conf_cca_mode cca;
587	struct acx100_conf_ed_thresh ed;
588	struct ifnet *ifp = &sc->sc_ic.ic_if;
589
590	/*
591	 * NOTE:
592	 * CCA mode and ED threshold MUST be read during initialization
593	 * or the acx100 card won't work as expected
594	 */
595
596	/* Get CCA mode */
597	if (acx_get_conf(sc, ACX_CONF_CCA_MODE, &cca, sizeof(cca)) != 0) {
598		printf("%s: %s can't get cca mode\n",
599		    ifp->if_xname, __func__);
600		return (ENXIO);
601	}
602	conf->cca_mode = cca.cca_mode;
603	DPRINTF(("%s: cca mode %02x\n", ifp->if_xname, cca.cca_mode));
604
605	/* Get ED threshold */
606	if (acx_get_conf(sc, ACX_CONF_ED_THRESH, &ed, sizeof(ed)) != 0) {
607		printf("%s: %s can't get ed threshold\n",
608		    ifp->if_xname, __func__);
609		return (ENXIO);
610	}
611	conf->ed_thresh = ed.ed_thresh;
612	DPRINTF(("%s: ed threshold %02x\n", ifp->if_xname, ed.ed_thresh));
613
614	return (0);
615}
616
617int
618acx100_write_config(struct acx_softc *sc, struct acx_config *conf)
619{
620	struct acx100_conf_cca_mode cca;
621	struct acx100_conf_ed_thresh ed;
622	struct ifnet *ifp = &sc->sc_ic.ic_if;
623
624	/* Set CCA mode */
625	cca.cca_mode = conf->cca_mode;
626	if (acx_set_conf(sc, ACX_CONF_CCA_MODE, &cca, sizeof(cca)) != 0) {
627		printf("%s: %s can't set cca mode\n",
628		    ifp->if_xname, __func__);
629		return (ENXIO);
630	}
631
632	/* Set ED threshold */
633	ed.ed_thresh = conf->ed_thresh;
634	if (acx_set_conf(sc, ACX_CONF_ED_THRESH, &ed, sizeof(ed)) != 0) {
635		printf("%s: %s can't set ed threshold\n",
636		    ifp->if_xname, __func__);
637		return (ENXIO);
638	}
639
640	/* Set TX power */
641	acx100_set_txpower(sc);	/* ignore return value */
642
643	return (0);
644}
645
646int
647acx100_set_txpower(struct acx_softc *sc)
648{
649	struct ifnet *ifp = &sc->sc_ic.ic_if;
650	const uint8_t *map;
651
652	switch (sc->sc_radio_type) {
653	case ACX_RADIO_TYPE_MAXIM:
654		map = acx100_txpower_maxim;
655		break;
656	case ACX_RADIO_TYPE_RFMD:
657	case ACX_RADIO_TYPE_RALINK:
658		map = acx100_txpower_rfmd;
659		break;
660	default:
661		printf("%s: TX power for radio type 0x%02x can't be set yet\n",
662		    ifp->if_xname, sc->sc_radio_type);
663		return (1);
664	}
665
666	acx_write_phyreg(sc, ACXRV_PHYREG_TXPOWER, map[ACX100_TXPOWER]);
667
668	return (0);
669}
670
671void
672acx100_set_fw_txdesc_rate(struct acx_softc *sc, struct acx_txbuf *tx_buf,
673    int rate)
674{
675	FW_TXDESC_SETFIELD_1(sc, tx_buf, f_tx_rate100, ACX100_RATE(rate));
676}
677
678void
679acx100_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl)
680{
681	struct acx100_bss_join *bj = param;
682
683	bj->dtim_intvl = dtim_intvl;
684	bj->basic_rates = 15;	/* XXX */
685	bj->all_rates = 31;	/* XXX */
686}
687
688int
689acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_key *k, int k_idx)
690{
691	struct acx100_conf_wepkey conf_wk;
692	struct ifnet *ifp = &sc->sc_ic.ic_if;
693
694	if (k->k_len > ACX100_WEPKEY_LEN) {
695		printf("%s: %dth WEP key size beyond %d\n",
696		    ifp->if_xname, k_idx, ACX100_WEPKEY_LEN);
697		return EINVAL;
698	}
699
700	conf_wk.action = ACX100_WEPKEY_ACT_ADD;
701	conf_wk.key_len = k->k_len;
702	conf_wk.key_idx = k_idx;
703	bcopy(k->k_key, conf_wk.key, k->k_len);
704	if (acx_set_conf(sc, ACX_CONF_WEPKEY, &conf_wk, sizeof(conf_wk)) != 0) {
705		printf("%s: %s set %dth WEP key failed\n",
706		    ifp->if_xname, __func__, k_idx);
707		return ENXIO;
708	}
709	return 0;
710}
711
712void
713acx100_proc_wep_rxbuf(struct acx_softc *sc, struct mbuf *m, int *len)
714{
715	int mac_hdrlen;
716	struct ieee80211_frame *f;
717
718	/*
719	 * Strip leading IV and KID, and trailing CRC
720	 */
721	f = mtod(m, struct ieee80211_frame *);
722
723	if (ieee80211_has_addr4(f))
724		mac_hdrlen = sizeof(struct ieee80211_frame_addr4);
725	else
726		mac_hdrlen = sizeof(struct ieee80211_frame);
727
728#define IEEEWEP_IVLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
729#define IEEEWEP_EXLEN	(IEEEWEP_IVLEN + IEEE80211_WEP_CRCLEN)
730
731	*len = *len - IEEEWEP_EXLEN;
732
733	/* Move MAC header toward frame body */
734	memmove((uint8_t *)f + IEEEWEP_IVLEN, f, mac_hdrlen);
735	m_adj(m, IEEEWEP_IVLEN);
736
737#undef IEEEWEP_EXLEN
738#undef IEEEWEP_IVLEN
739}
740