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