xref: /openbsd/sys/dev/ic/acx100.c (revision 17df1aa7)
1 /*	$OpenBSD: acx100.c,v 1.21 2010/04/20 22:05:41 tedu 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 
101 int	acx100_init(struct acx_softc *);
102 int	acx100_init_wep(struct acx_softc *);
103 int	acx100_init_tmplt(struct acx_softc *);
104 int	acx100_init_fw_ring(struct acx_softc *);
105 int	acx100_init_memory(struct acx_softc *);
106 void	acx100_init_fw_txring(struct acx_softc *, uint32_t);
107 void	acx100_init_fw_rxring(struct acx_softc *, uint32_t);
108 int	acx100_read_config(struct acx_softc *, struct acx_config *);
109 int	acx100_write_config(struct acx_softc *, struct acx_config *);
110 int	acx100_set_txpower(struct acx_softc *);
111 void	acx100_set_fw_txdesc_rate(struct acx_softc *,
112 	    struct acx_txbuf *, int);
113 void	acx100_set_bss_join_param(struct acx_softc *, void *, int);
114 int	acx100_set_wepkey(struct acx_softc *, struct ieee80211_key *, int);
115 void	acx100_proc_wep_rxbuf(struct acx_softc *, struct mbuf *, int *);
116 
117 /*
118  * NOTE:
119  * Following structs' fields are little endian
120  */
121 struct acx100_bss_join {
122 	uint8_t	dtim_intvl;
123 	uint8_t	basic_rates;
124 	uint8_t	all_rates;
125 } __packed;
126 
127 struct 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)			\
145 do {							\
146 	(conf)->fw_ring_end[0] = htole32(end);		\
147 	(conf)->fw_ring_end[1] = htole32(end + 8);	\
148 } while (0)
149 
150 struct acx100_conf_memblk_size {
151 	struct acx_conf	confcom;
152 	uint16_t	memblk_size;	/* size of each mem block */
153 } __packed;
154 
155 struct 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 
178 struct acx100_conf_cca_mode {
179 	struct acx_conf	confcom;
180 	uint8_t		cca_mode;
181 	uint8_t		unknown;
182 } __packed;
183 
184 struct acx100_conf_ed_thresh {
185 	struct acx_conf	confcom;
186 	uint8_t		ed_thresh;
187 	uint8_t		unknown[3];
188 } __packed;
189 
190 struct 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 
201 static 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 
241 static 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 
250 static 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 
259 void
260 acx100_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 	sc->chip_intr_disable = ACX100_INTR_DISABLE;
268 	sc->chip_gpio_pled = ACX100_GPIO_POWER_LED;
269 	sc->chip_ee_eaddr_ofs = ACX100_EE_EADDR_OFS;
270 	sc->chip_txdesc1_len = ACX_FRAME_HDRLEN;
271 	sc->chip_fw_txdesc_ctrl = DESC_CTRL_AUTODMA |
272 	    DESC_CTRL_RECLAIM | DESC_CTRL_FIRST_FRAG;
273 
274 	sc->chip_phymode = IEEE80211_MODE_11B;
275 	sc->chip_chan_flags = IEEE80211_CHAN_B;
276 	sc->sc_ic.ic_phytype = IEEE80211_T_DS;
277 	sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
278 
279 	sc->chip_init = acx100_init;
280 	sc->chip_set_wepkey = acx100_set_wepkey;
281 	sc->chip_read_config = acx100_read_config;
282 	sc->chip_write_config = acx100_write_config;
283 	sc->chip_set_fw_txdesc_rate = acx100_set_fw_txdesc_rate;
284 	sc->chip_set_bss_join_param = acx100_set_bss_join_param;
285 	sc->chip_proc_wep_rxbuf = acx100_proc_wep_rxbuf;
286 }
287 
288 int
289 acx100_init(struct acx_softc *sc)
290 {
291 	struct ifnet *ifp = &sc->sc_ic.ic_if;
292 
293 	/*
294 	 * NOTE:
295 	 * Order of initialization:
296 	 * 1) WEP
297 	 * 2) Templates
298 	 * 3) Firmware TX/RX ring
299 	 * 4) Hardware memory
300 	 * Above order is critical to get a correct memory map
301 	 */
302 	if (acx100_init_wep(sc) != 0) {
303 		printf("%s: %s can't initialize wep\n",
304 		    ifp->if_xname, __func__);
305 		return (ENXIO);
306 	}
307 
308 	if (acx100_init_tmplt(sc) != 0) {
309 		printf("%s: %s can't initialize templates\n",
310 		    ifp->if_xname, __func__);
311 		return (ENXIO);
312 	}
313 
314 	if (acx100_init_fw_ring(sc) != 0) {
315 		printf("%s: %s can't initialize fw ring\n",
316 		    ifp->if_xname, __func__);
317 		return (ENXIO);
318 	}
319 
320 	if (acx100_init_memory(sc) != 0) {
321 		printf("%s: %s can't initialize hw memory\n",
322 		    ifp->if_xname, __func__);
323 		return (ENXIO);
324 	}
325 
326 	return (0);
327 }
328 
329 int
330 acx100_init_wep(struct acx_softc *sc)
331 {
332 	struct acx_conf_wepopt wep_opt;
333 	struct acx_conf_mmap mem_map;
334 	struct ifnet *ifp = &sc->sc_ic.ic_if;
335 
336 	/* Set WEP cache start/end address */
337 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
338 		printf("%s: can't get mmap\n", ifp->if_xname);
339 		return (1);
340 	}
341 
342 	mem_map.wep_cache_start = htole32(letoh32(mem_map.code_end) + 4);
343 	mem_map.wep_cache_end = htole32(letoh32(mem_map.code_end) + 4);
344 	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
345 		printf("%s: can't set mmap\n", ifp->if_xname);
346 		return (1);
347 	}
348 
349 	/* Set WEP options */
350 	wep_opt.nkey = htole16(IEEE80211_WEP_NKID + 10);
351 	wep_opt.opt = WEPOPT_HDWEP;
352 	if (acx_set_conf(sc, ACX_CONF_WEPOPT, &wep_opt, sizeof(wep_opt)) != 0) {
353 		printf("%s: can't set wep opt\n", ifp->if_xname);
354 		return (1);
355 	}
356 
357 	return (0);
358 }
359 
360 int
361 acx100_init_tmplt(struct acx_softc *sc)
362 {
363 	struct acx_conf_mmap mem_map;
364 	struct ifnet *ifp = &sc->sc_ic.ic_if;
365 
366 	/* Set templates start address */
367 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
368 		printf("%s: can't get mmap\n", ifp->if_xname);
369 		return (1);
370 	}
371 
372 	mem_map.pkt_tmplt_start = mem_map.wep_cache_end;
373 	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
374 		printf("%s: can't set mmap\n", ifp->if_xname);
375 		return (1);
376 	}
377 
378 	/* Initialize various packet templates */
379 	if (acx_init_tmplt_ordered(sc) != 0) {
380 		printf("%s: can't init tmplt\n", ifp->if_xname);
381 		return (1);
382 	}
383 
384 	return (0);
385 }
386 
387 int
388 acx100_init_fw_ring(struct acx_softc *sc)
389 {
390 	struct acx100_conf_fw_ring ring;
391 	struct acx_conf_mmap mem_map;
392 	struct ifnet *ifp = &sc->sc_ic.ic_if;
393 	uint32_t txring_start, rxring_start, ring_end;
394 
395 	/* Set firmware descriptor ring start address */
396 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
397 		printf("%s: can't get mmap\n", ifp->if_xname);
398 		return (1);
399 	}
400 
401 	txring_start = letoh32(mem_map.pkt_tmplt_end) + 4;
402 	rxring_start = txring_start + ACX100_FW_TXRING_SIZE;
403 	ring_end = rxring_start + ACX100_FW_RXRING_SIZE;
404 
405 	mem_map.fw_desc_start = htole32(txring_start);
406 	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
407 		printf("%s: can't set mmap\n", ifp->if_xname);
408 		return (1);
409 	}
410 
411 	/* Set firmware descriptor ring configure */
412 	bzero(&ring, sizeof(ring));
413 	ring.fw_ring_size = htole32(ACX100_FW_TXRING_SIZE +
414 	    ACX100_FW_RXRING_SIZE + 8);
415 
416 	ring.fw_txring_num = 1;
417 	ring.fw_txring_addr = htole32(txring_start);
418 	ring.fw_txring_prio = ACX100_TXRING_PRIO_DEFAULT;
419 	ring.fw_txdesc_num = 0; /* XXX ignored?? */
420 
421 	ring.fw_rxring_addr = htole32(rxring_start);
422 	ring.fw_rxdesc_num = 0; /* XXX ignored?? */
423 
424 	ring.opt = ACX100_RINGOPT_AUTO_RESET;
425 	ACX100_SET_RING_END(&ring, ring_end);
426 	if (acx_set_conf(sc, ACX100_CONF_FW_RING, &ring, sizeof(ring)) != 0) {
427 		printf("%s: can't set fw ring configure\n", ifp->if_xname);
428 		return (1);
429 	}
430 
431 	/* Setup firmware TX/RX descriptor ring */
432 	acx100_init_fw_txring(sc, txring_start);
433 	acx100_init_fw_rxring(sc, rxring_start);
434 
435 	return (0);
436 }
437 
438 #define MEMBLK_ALIGN(addr)	\
439     (((addr) + (ACX100_MEMBLK_ALIGN - 1)) & ~(ACX100_MEMBLK_ALIGN - 1))
440 
441 int
442 acx100_init_memory(struct acx_softc *sc)
443 {
444 	struct acx100_conf_memblk_size memblk_sz;
445 	struct acx100_conf_mem mem;
446 	struct acx_conf_mmap mem_map;
447 	struct ifnet *ifp = &sc->sc_ic.ic_if;
448 	uint32_t memblk_start, memblk_end;
449 	int total_memblk, txblk_num, rxblk_num;
450 
451 	/* Set memory block start address */
452 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
453 		printf("%s: can't get mmap\n", ifp->if_xname);
454 		return (1);
455 	}
456 
457 	mem_map.memblk_start =
458 	    htole32(MEMBLK_ALIGN(letoh32(mem_map.fw_desc_end) + 4));
459 
460 	if (acx_set_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
461 		printf("%s: can't set mmap\n", ifp->if_xname);
462 		return (1);
463 	}
464 
465 	/* Set memory block size */
466 	memblk_sz.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
467 	if (acx_set_conf(sc, ACX_CONF_MEMBLK_SIZE, &memblk_sz,
468 	    sizeof(memblk_sz)) != 0) {
469 		printf("%s: can't set mem block size\n", ifp->if_xname);
470 		return (1);
471 	}
472 
473 	/* Get memory map after setting it */
474 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
475 		printf("%s: can't get mmap again\n", ifp->if_xname);
476 		return (1);
477 	}
478 	memblk_start = letoh32(mem_map.memblk_start);
479 	memblk_end = letoh32(mem_map.memblk_end);
480 
481 	/* Set memory options */
482 	mem.opt = htole32(ACX100_MEMOPT_MEMBLOCK | ACX100_MEMOPT_HOSTDESC);
483 	mem.h_rxring_paddr = htole32(sc->sc_ring_data.rx_ring_paddr);
484 
485 	total_memblk = (memblk_end - memblk_start) / ACX_MEMBLOCK_SIZE;
486 
487 	rxblk_num = total_memblk / 2;		/* 50% */
488 	txblk_num = total_memblk - rxblk_num;	/* 50% */
489 
490 	DPRINTF(("%s: \ttotal memory blocks\t%d\n"
491 	    "\trx memory blocks\t%d\n"
492 	    "\ttx memory blocks\t%d\n",
493 	    ifp->if_xname, total_memblk, rxblk_num, txblk_num));
494 
495 	mem.rx_memblk_num = htole16(rxblk_num);
496 	mem.tx_memblk_num = htole16(txblk_num);
497 
498 	mem.rx_memblk_addr = htole32(MEMBLK_ALIGN(memblk_start));
499 	mem.tx_memblk_addr = htole32(MEMBLK_ALIGN(memblk_start +
500 	    (ACX_MEMBLOCK_SIZE * rxblk_num)));
501 
502 	if (acx_set_conf(sc, ACX100_CONF_MEMOPT, &mem, sizeof(mem)) != 0) {
503 		printf("%s: can't set mem options\n", ifp->if_xname);
504 		return (1);
505 	}
506 
507 	/* Initialize memory */
508 	if (acx_exec_command(sc, ACXCMD_INIT_MEM, NULL, 0, NULL, 0) != 0) {
509 		printf("%s: can't init mem\n", ifp->if_xname);
510 		return (1);
511 	}
512 
513 	return (0);
514 }
515 
516 #undef MEMBLK_ALIGN
517 
518 void
519 acx100_init_fw_txring(struct acx_softc *sc, uint32_t fw_txdesc_start)
520 {
521 	struct acx_fw_txdesc fw_desc;
522 	struct acx_txbuf *tx_buf;
523 	uint32_t desc_paddr, fw_desc_offset;
524 	int i;
525 
526 	bzero(&fw_desc, sizeof(fw_desc));
527 	fw_desc.f_tx_ctrl = DESC_CTRL_HOSTOWN | DESC_CTRL_RECLAIM |
528 	    DESC_CTRL_AUTODMA | DESC_CTRL_FIRST_FRAG;
529 
530 	tx_buf = sc->sc_buf_data.tx_buf;
531 	fw_desc_offset = fw_txdesc_start;
532 	desc_paddr = sc->sc_ring_data.tx_ring_paddr;
533 
534 	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
535 		fw_desc.f_tx_host_desc = htole32(desc_paddr);
536 
537 		if (i == ACX_TX_DESC_CNT - 1) {
538 			fw_desc.f_tx_next_desc = htole32(fw_txdesc_start);
539 		} else {
540 			fw_desc.f_tx_next_desc = htole32(fw_desc_offset +
541 			    sizeof(struct acx_fw_txdesc));
542 		}
543 
544 		tx_buf[i].tb_fwdesc_ofs = fw_desc_offset;
545 		DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
546 		    sizeof(fw_desc));
547 
548 		desc_paddr += (2 * sizeof(struct acx_host_desc));
549 		fw_desc_offset += sizeof(fw_desc);
550 	}
551 }
552 
553 void
554 acx100_init_fw_rxring(struct acx_softc *sc, uint32_t fw_rxdesc_start)
555 {
556 	struct acx_fw_rxdesc fw_desc;
557 	uint32_t fw_desc_offset;
558 	int i;
559 
560 	bzero(&fw_desc, sizeof(fw_desc));
561 	fw_desc.f_rx_ctrl = DESC_CTRL_RECLAIM | DESC_CTRL_AUTODMA;
562 
563 	fw_desc_offset = fw_rxdesc_start;
564 
565 	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
566 		if (i == ACX_RX_DESC_CNT - 1) {
567 			fw_desc.f_rx_next_desc = htole32(fw_rxdesc_start);
568 		} else {
569 			fw_desc.f_rx_next_desc =
570 			    htole32(fw_desc_offset +
571 			    sizeof(struct acx_fw_rxdesc));
572 		}
573 
574 		DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
575 		    sizeof(fw_desc));
576 
577 		fw_desc_offset += sizeof(fw_desc);
578 	}
579 }
580 
581 int
582 acx100_read_config(struct acx_softc *sc, struct acx_config *conf)
583 {
584 	struct acx100_conf_cca_mode cca;
585 	struct acx100_conf_ed_thresh ed;
586 	struct ifnet *ifp = &sc->sc_ic.ic_if;
587 
588 	/*
589 	 * NOTE:
590 	 * CCA mode and ED threshold MUST be read during initialization
591 	 * or the acx100 card won't work as expected
592 	 */
593 
594 	/* Get CCA mode */
595 	if (acx_get_conf(sc, ACX_CONF_CCA_MODE, &cca, sizeof(cca)) != 0) {
596 		printf("%s: %s can't get cca mode\n",
597 		    ifp->if_xname, __func__);
598 		return (ENXIO);
599 	}
600 	conf->cca_mode = cca.cca_mode;
601 	DPRINTF(("%s: cca mode %02x\n", ifp->if_xname, cca.cca_mode));
602 
603 	/* Get ED threshold */
604 	if (acx_get_conf(sc, ACX_CONF_ED_THRESH, &ed, sizeof(ed)) != 0) {
605 		printf("%s: %s can't get ed threshold\n",
606 		    ifp->if_xname, __func__);
607 		return (ENXIO);
608 	}
609 	conf->ed_thresh = ed.ed_thresh;
610 	DPRINTF(("%s: ed threshold %02x\n", ifp->if_xname, ed.ed_thresh));
611 
612 	return (0);
613 }
614 
615 int
616 acx100_write_config(struct acx_softc *sc, struct acx_config *conf)
617 {
618 	struct acx100_conf_cca_mode cca;
619 	struct acx100_conf_ed_thresh ed;
620 	struct ifnet *ifp = &sc->sc_ic.ic_if;
621 
622 	/* Set CCA mode */
623 	cca.cca_mode = conf->cca_mode;
624 	if (acx_set_conf(sc, ACX_CONF_CCA_MODE, &cca, sizeof(cca)) != 0) {
625 		printf("%s: %s can't set cca mode\n",
626 		    ifp->if_xname, __func__);
627 		return (ENXIO);
628 	}
629 
630 	/* Set ED threshold */
631 	ed.ed_thresh = conf->ed_thresh;
632 	if (acx_set_conf(sc, ACX_CONF_ED_THRESH, &ed, sizeof(ed)) != 0) {
633 		printf("%s: %s can't set ed threshold\n",
634 		    ifp->if_xname, __func__);
635 		return (ENXIO);
636 	}
637 
638 	/* Set TX power */
639 	acx100_set_txpower(sc);	/* ignore return value */
640 
641 	return (0);
642 }
643 
644 int
645 acx100_set_txpower(struct acx_softc *sc)
646 {
647 	struct ifnet *ifp = &sc->sc_ic.ic_if;
648 	const uint8_t *map;
649 
650 	switch (sc->sc_radio_type) {
651 	case ACX_RADIO_TYPE_MAXIM:
652 		map = acx100_txpower_maxim;
653 		break;
654 	case ACX_RADIO_TYPE_RFMD:
655 	case ACX_RADIO_TYPE_RALINK:
656 		map = acx100_txpower_rfmd;
657 		break;
658 	default:
659 		printf("%s: TX power for radio type 0x%02x can't be set yet\n",
660 		    ifp->if_xname, sc->sc_radio_type);
661 		return (1);
662 	}
663 
664 	acx_write_phyreg(sc, ACXRV_PHYREG_TXPOWER, map[ACX100_TXPOWER]);
665 
666 	return (0);
667 }
668 
669 void
670 acx100_set_fw_txdesc_rate(struct acx_softc *sc, struct acx_txbuf *tx_buf,
671     int rate)
672 {
673 	FW_TXDESC_SETFIELD_1(sc, tx_buf, f_tx_rate100, ACX100_RATE(rate));
674 }
675 
676 void
677 acx100_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl)
678 {
679 	struct acx100_bss_join *bj = param;
680 
681 	bj->dtim_intvl = dtim_intvl;
682 	bj->basic_rates = 15;	/* XXX */
683 	bj->all_rates = 31;	/* XXX */
684 }
685 
686 int
687 acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_key *k, int k_idx)
688 {
689 	struct acx100_conf_wepkey conf_wk;
690 	struct ifnet *ifp = &sc->sc_ic.ic_if;
691 
692 	if (k->k_len > ACX100_WEPKEY_LEN) {
693 		printf("%s: %dth WEP key size beyond %d\n",
694 		    ifp->if_xname, k_idx, ACX100_WEPKEY_LEN);
695 		return EINVAL;
696 	}
697 
698 	conf_wk.action = ACX100_WEPKEY_ACT_ADD;
699 	conf_wk.key_len = k->k_len;
700 	conf_wk.key_idx = k_idx;
701 	bcopy(k->k_key, conf_wk.key, k->k_len);
702 	if (acx_set_conf(sc, ACX_CONF_WEPKEY, &conf_wk, sizeof(conf_wk)) != 0) {
703 		printf("%s: %s set %dth WEP key failed\n",
704 		    ifp->if_xname, __func__, k_idx);
705 		return ENXIO;
706 	}
707 	return 0;
708 }
709 
710 void
711 acx100_proc_wep_rxbuf(struct acx_softc *sc, struct mbuf *m, int *len)
712 {
713 	int mac_hdrlen;
714 	struct ieee80211_frame *f;
715 
716 	/*
717 	 * Strip leading IV and KID, and trailing CRC
718 	 */
719 	f = mtod(m, struct ieee80211_frame *);
720 
721 	if ((f->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
722 		mac_hdrlen = sizeof(struct ieee80211_frame_addr4);
723 	else
724 		mac_hdrlen = sizeof(struct ieee80211_frame);
725 
726 #define IEEEWEP_IVLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
727 #define IEEEWEP_EXLEN	(IEEEWEP_IVLEN + IEEE80211_WEP_CRCLEN)
728 
729 	*len = *len - IEEEWEP_EXLEN;
730 
731 	/* Move MAC header toward frame body */
732 	ovbcopy(f, (uint8_t *)f + IEEEWEP_IVLEN, mac_hdrlen);
733 	m_adj(m, IEEEWEP_IVLEN);
734 
735 #undef IEEEWEP_EXLEN
736 #undef IEEEWEP_IVLEN
737 }
738