code:
/*	$OpenBSD: amireg.h,v 1.30 2008/10/22 18:42:29 marco Exp $	*/

/*
 * Copyright (c) 2000 Michael Shalayeff
 * Copyright (c) 2005 Marco Peereboom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#define	AMI_MAX_PDRIVES		(75)
#define	AMI_MAX_LDRIVES		8
#define	AMI_MAX_SPANDEPTH	4
#define	AMI_MAX_DEVDEPTH	8
#define	AMI_MAX_TARGET		16

#define	AMI_BIG_MAX_PDRIVES	(256)
#define	AMI_BIG_MAX_LDRIVES	40
#define	AMI_BIG_MAX_SPANDEPTH	8
#define	AMI_BIG_MAX_DEVDEPTH	32

#define	AMI_MAXCMDS	126		/* theoretical limit is 250 */
#define	AMI_SECTOR_SIZE	512
#define	AMI_MAXOFFSETS	26
#define	AMI_SGEPERCMD	32		/* to prevent page boundary crossing */
#define AMI_MAX_BUSYWAIT 10		/* wait up to 10 usecs */
#define AMI_MAX_POLLWAIT 1000000	/* wait up to 1000 000 usecs */
#define AMI_MAXIOCTLCMDS 1		/* number of parallel ioctl calls */
#define AMI_MAXPROCS	 2		/* number of processors on a channel */
#define AMI_MAXRAWCMDS	 2		/* number of parallel processor cmds */

#define	AMI_MAXFER	(AMI_MAXOFFSETS * PAGE_SIZE)

#define	AMI_QIDB	0x20
#define		AMI_QIDB_EXEC	0x01
#define		AMI_QIDB_ACK	0x02
#define	AMI_QODB	0x2c
#define		AMI_QODB_READY	0x10001234

#define	AMI_SCMD	0x10
#define		AMI_SCMD_EXEC	0x10
#define		AMI_SCMD_ACK	0x08
#define	AMI_SMBSTAT	0x10
#define		AMI_SMBST_BUSY	0x10
#define	AMI_SIEM	0x11
#define		AMI_SEIM_ENA	0xc0
#define	AMI_SMBADDR	0x14
#define	AMI_SMBENA	0x18
#define	AMI_ISTAT	0x1a
#define		AMI_ISTAT_PEND	0x40

/* commands */
#define	AMI_READ	0x01
#define	AMI_WRITE	0x02
#define	AMI_PASSTHRU	0x03	/* pass scsi cdb to the device */
#define	AMI_EINQUIRY	0x04	/* extended inquiry */
#define	AMI_INQUIRY	0x05	/* inquiry */
#define	AMI_CHSTATE	0x06	/* pad[0] -- state */
#define		AMI_STATE_ON	3
#define		AMI_STATE_FAIL	4
#define		AMI_STATE_SPARE	6
#define	AMI_RCONFIG	0x07	/* read configuration up to 4 spans */
#define	AMI_REBUILDPD	0x08	/* rebuild physical drive */
#define	AMI_CHECK	0x09	/* check consistency */
#define	AMI_FLUSH	0x0a
#define	AMI_ILDRIVE	0x0b	/* init logical drive */
#define	AMI_EINQUIRY3	0x0c
#define	AMI_DCHDR	0x14	/* get/set dedicated channel/drives */
#define	AMI_GRBLDPROGR	0x18	/* get rebuild progress */
#define	AMI_GCHECKPROGR	0x19	/* get check consistency progress */
#define	AMI_GILDRPROGR	0x1b	/* get init logical drive progress */
#define	AMI_WRCONFIG	0x20	/* write configuration up to 4 spans */
#define	AMI_RWRCONFIG	0x21	/* raid write config */
#define	AMI_RRDCONFIG	0x22	/* raid read config */
#define	AMI_GRBLDRATE	0x23	/* get rebuild rate */
#define	AMI_SRBLDRATE	0x24	/* set rebuild rate */
#define	AMI_UPLDCFGUT	0x25	/* upload config utility */
#define	AMI_UPLDRVPROP	0x26	/* update logical drive property */
#define	AMI_ABRTREBLD	0x28	/* abort rebuild */
#define	AMI_ABRTCHECK	0x29	/* abort check consistency */
#define	AMI_ABRTILDRV	0x2b	/* abort init logical drive */
#define	AMI_WRBLOCK	0x2c	/* flash write block */
#define	AMI_PRGFLASH	0x2d	/* flash program */
#define	AMI_SFLUSHINTV	0x2e	/* channel == cache flush interval */
#define	AMI_PCHIPSETVAL	0x2f	/* program chipset values */
#define		AMI_CS_NEPTUNE	0x61
#define		AMI_CS_OTHER	0xe1
#define		AMI_CS_TRITON	0xe2
#define	AMI_SNEG	0x30	/* scsi sync negotiation get/ena/dis */
#define		AMI_SNEG_GET	1
#define		AMI_SNEG_SET	2
#define	AMI_QTAG	0x31	/* scsi queue tag get/set */
#define		AMI_QTAG_GET	1
#define		AMI_QTAG_SET	2
#define	AMI_GSUPARAM	0x32	/* get spinup parameters */
#define	AMI_SSUPARAM	0x33	/* set spinup parameters */
#define	AMI_GDROAMINFO	0x34
#define	AMI_GMACHID	0x36	/* get machine id */
#define	AMI_BIOSPDATA	0x40	/* get bios private data */
#define	AMI_I2OCFGDLG	0x41	/* I2O config dialog */
#define	AMI_GCACHESTAT	0x50	/* get cache statistics */
#define	AMI_SPEAKER	0x51	/* speaker control */
#define		AMI_SPKR_OFF	0
#define		AMI_SPKR_ON	1
#define		AMI_SPKR_SHUT	2
#define		AMI_SPKR_GVAL	3
#define		AMI_SPKR_TEST	4
#define	AMI_GDUMP	0x52	/* get error condition in text */
#define	AMI_SENSEDUMPA	0x53	/* get SCSI sense dump area */
#define	AMI_STDIAG	0x54	/* start diagnostics -- 2.1 */
#define	AMI_FRAID_PF	0x55	/* get/set flexraid power fail */
#define		AMI_GFRAIDPF	1
#define		AMI_SFRAIDPF	2
#define	AMI_FRAIDVS	0x56	/* get/set flexraid virtual sizing */
#define		AMI_GFRAIDVS	1
#define		AMI_SFRAIDVS	2
#define	AMI_BBMANAGE	0x57	/* bad block manage */
#define	AMI_RECONSTRUCT	0x60	/* begin reconstruction */
#define	AMI_GRECONSTRUCT 0x61	/* get reconstruction progress */
#define	AMI_BIOSSTAT	0x62	/* enable/disable bios */
#define	AMI_RDCFGDSK	0x63	/* read configuration from disk */
#define	AMI_AREBUILD	0x64	/* get/set autorebuild/battery charge */
#define		AMI_GUCAP	1	/* get ultra capabilities */
#define		AMI_SUCAP	2	/* set ultra capability */
#define		AMI_GARBLD	3
#define		AMI_SARBLD	4
#define		AMI_GFCC	5	/* get fast charge counter */
#define		AMI_SFCC	6	/* set fast charge counter */
#define		AMI_GCUCAP	7	/* get channel ultra capabilities */
#define		AMI_SCUCAP	8	/* set channel ultra capabilities */
#define	AMI_SFD		0x66	/* set factory defaults */
#define	AMI_RDCONFIG8	0x67	/* read configuration up to 8 spans */
#define	AMI_WRCONFIG8	0x68	/* write config up to 8 spans */
#define	AMI_ESENSEDUMPA	0x69	/* extended scsi dump area */
#define	AMI_RERRC	0x6a	/* reset error counter */
#define	AMI_BOOTUP	0x6b	/* ena/dis physical drive boot up */
#define	AMI_ENCLOSURE	0x6c	/* get/set enclosure type */
#define	AMI_WRCFGD	0x6c	/* write config disk -- 2.1 */
#define	AMI_HAPIRRLD	0x6e
#define	AMI_LDRVRIGHTS	0x6f
#define	AMI_CLUSTERING	0x70
#define	AMI_GCHPROP	0x71	/* get channel properties */
#define	AMI_SCHTERM	0x72	/* set channel termination */
#define		AMI_TERM_DISABLE 0
#define		AMI_TERM_ENABLE	1
#define		AMI_TERM_HIGH	2
#define		AMI_TERM_WIDE	3
#define		AMI_TERM_DFLT	16
#define	AMI_QUIETCH	0x73	/* quiet channel */
#define	AMI_ACTIVATECH	0x74	/* activate channel */
#define	AMI_STARTU	0x75	/* start unit, pad[0] -- sync/async */
#define		AMI_STARTU_SYNC	1
#define		AMI_STARTU_ASYN	2
#define	AMI_STOPU	0x76	/* stop unit */
#define	AMI_GERRC	0x77	/* get error counter */
#define	AMI_GBTDS	0x78	/* get boot time drive status */
#define	AMI_FMTPROG	0x79
#define	AMI_RCAPCMD	0x7a	/* read capacity */
#define	AMI_WRCRX	0x7b
#define	AMI_RDCRX	0x7c
#define	AMI_GINID	0x7d	/* get initiator id */
#define	AMI_HAPICMD	0x7e
#define	AMI_SINID	0x7f	/* set initiator id */
#define	AMI_SMARTMSEL	0x80
#define	AMI_SPSTARTU	0x85	/* special start unit command */
#define	AMI_NVFAILHIST	0x90
#define	AMI_DCMDABRT	0x91
#define	AMI_GDRIVEHIST	0x92	/* get drive history */
#define	AMI_GESENSE	0x93	/* get extended sense data dump */
#define	AMI_ADAPTER	0x95	/* save/restore adapter params */
#define		AMI_ADP_SAVE	0
#define		AMI_ADP_LOAD	1
#define	AMI_RESET	0x96	/* adapter reset */
#define	AMI_PRGCLASS	0x97	/* program class code */
#define	AMI_UPHTML	0x98	/* upload html utility */
#define	AMI_NEWCFG	0x99
#define	AMI_NEWOP	0xa0
#define	AMI_FCOP	0xa1
#define		AMI_FC_PROCEED	0x02
#define		AMI_FC_DELLDRV	0x03
#define		AMI_FC_RDCONF	0x04
#define		AMI_FC_RDFCONF	0x05
#define		AMI_FC_GCONFDSK	0x06
#define		AMI_FC_CHLDNO	0x07
#define		AMI_FC_CMPCTCFG	0x08
#define		AMI_FC_DRVGRP	0x09
#define		AMI_FC_GLOOPINF	0x0a
#define		AMI_FC_CHLOOPID	0x0b
#define		AMI_FC_GNSCH	0x0c
#define		AMI_FC_WRCONF	0x0d
#define		AMI_FC_PRODINF	0x0e
#define		AMI_FC_EINQ3	0x0f
#define		AMI_FC_EINQ4	0x1f
#define			AMI_FC_EINQ3_SOLICITED_NOTIFY	0x01
#define			AMI_FC_EINQ3_SOLICITED_FULL	0x02
#define			AMI_FC_EINQ3_UNSOLICITED	0x03
#define	AMI_MISC	0xa4
#define		AMI_GET_BGI	0x13
#define		AMI_GET_IO_CMPL	0x5b
#define		AMI_SET_IO_CMPL	0x5c
#define	AMI_CHFUNC	0xa9
#define	AMI_MANAGE	0xb0	/* manage functions */
#define		AMI_MGR_LUN	0x00
#define		AMI_MGR_THERM	0x01
#define		AMI_MGR_EEPROM	0x02
#define		AMI_MGR_LDNAMES	0x03
#define		AMI_MGR_FCWWN	0x04
#define		AMI_MGR_CFGACC	0x05
#define	AMI_HSPDIAG	0xb1
#define	AMI_GESENSEINFO	0xb2	/* get extended sense info */
#define	AMI_SYSFLUSH	0xfe	/* flush system */

/* command structures */
struct ami_iocmd {
	u_int8_t	acc_cmd;
	u_int8_t	acc_id;
	union {
#define	acc_mbox	_._ami_mbox
		struct {
			u_int16_t	amb_nsect;
			u_int32_t	amb_lba;
			u_int32_t	amb_data;
			u_int8_t	amb_ldn;	/* logical drive no */
			u_int8_t	amb_nsge;
			u_int8_t	amb_reserved;
		} __packed _ami_mbox;

#define	acc_io		_._ami_io
		struct {
			u_int8_t	aio_channel;
			u_int8_t	aio_param;
			u_int8_t	aio_pad[4];
			u_int32_t	aio_data;
			u_int8_t	aio_pad1[3];
		} __packed _ami_io;

#define	acc_passthru	_._ami_passthru
		struct {
			u_int16_t	apt_dummy0;
			u_int32_t	apt_dummy1;
			u_int32_t	apt_data;
			u_int8_t	apt_dummy2;
			u_int8_t	apt_dummy3;
			u_int8_t	apt_reserved;
		} __packed _ami_passthru;

#define	acc_ldrv	_._ami_ldrv
		struct {
			u_int16_t	ald_dummy0;
			u_int32_t	ald_dummy1;
			u_int32_t	ald_data;
			u_int8_t	ald_ldrv;
			u_int8_t	ald_dummy2;
			u_int8_t	ald_reserved;
		} __packed _ami_ldrv;
	} __packed _;
	u_int8_t	acc_busy;
	u_int8_t	acc_nstat;
	u_int8_t	acc_status;
#define	AMI_MAXSTATACK	0x2e
	u_int8_t	acc_cmplidl[AMI_MAXSTATACK];
	u_int8_t	acc_poll;
	u_int8_t	acc_ack;
	u_int8_t	acc_pad[0x3e];	/* pad to 128 bytes */
} __packed;

struct ami_sgent {
	u_int32_t	asg_addr;
	u_int32_t	asg_len;
} __packed;

struct ami_iocmd64 {
	u_int8_t	acc_cmd;
	u_int8_t	acc_id;
	union {
		struct {
			u_int16_t	amb_nsect;
			u_int32_t	amb_lba;
			u_int32_t	amb_reserved1;
			u_int8_t	amb_ldn;	/* logical drive no */
			u_int8_t	amb_nsge;	/* high bit == 1 */
			u_int8_t	amb_reserved;
		} __packed _ami_mbox;

		struct {
			u_int8_t	aio_channel;
			u_int8_t	aio_param;
			u_int8_t	aio_pad[4];
			u_int32_t	aio_data;
			u_int8_t	aio_pad1[3];
		} __packed _ami_io;

		struct {
			u_int16_t	apt_dummy0;
			u_int32_t	apt_dummy1;
			u_int32_t	apt_data;
			u_int8_t	apt_dummy2;
			u_int8_t	apt_dummy3;
			u_int8_t	apt_reserved;
		} __packed _ami_passthru;

		struct {
			u_int16_t	ald_dummy0;
			u_int32_t	ald_dummy1;
			u_int32_t	ald_data;
			u_int8_t	ald_ldrv;
			u_int8_t	ald_dummy2;
			u_int8_t	ald_reserved;
		} __packed _ami_ldrv;
	} __packed _;
	u_int8_t	acc_busy;
	u_int32_t	acc_data_l;
	u_int32_t	acc_data_h;
	u_int32_t	acc_reserved;
	u_int8_t	acc_nstat;
	u_int8_t	acc_status;
	u_int8_t	acc_cmplidl[AMI_MAXSTATACK];
	u_int8_t	acc_poll;
	u_int8_t	acc_ack;
	u_int8_t	acc_pad[0x32];	/* pad to 128 bytes */
} __packed;

struct ami_sgent64 {
	u_int32_t	asg_addr_l;
	u_int32_t	asg_addr_h;
	u_int32_t	asg_len;
} __packed;

struct ami_passthrough {
	u_int8_t	apt_param;
#define	AMI_PTPARAM(t,a,l)	(((l) << 7) | (((a) & 1) << 3) | ((t) & 3))
#define	AMI_TIMEOUT_6	0
#define	AMI_TIMEOUT_60	1
#define	AMI_TIMEOUT_10m	2
#define	AMI_TIMEOUT_3h	3
	u_int8_t	apt_ldn;
	u_int8_t	apt_channel;
	u_int8_t	apt_target;
	u_int8_t	apt_qtag;
	u_int8_t	apt_qact;
#define	AMI_MAX_CDB	10
	u_int8_t	apt_cdb[AMI_MAX_CDB];
	u_int8_t	apt_ncdb;
	u_int8_t	apt_nsense;
#define	AMI_MAX_SENSE	32
	u_int8_t	apt_sense[AMI_MAX_SENSE];
	u_int8_t	apt_nsge;
	u_int8_t	apt_scsistat;
	u_int32_t	apt_data;
	u_int32_t	apt_datalen;
} __packed;

struct ami_inquiry {
	u_int8_t	ain_maxcmd;
	u_int8_t	ain_rbldrate;	/* rebuild rate %% */
	u_int8_t	ain_targets;	/* max targets per channel */
	u_int8_t	ain_channels;
	u_int8_t	ain_fwver[4];
	u_int16_t	ain_flashage;
	u_int8_t	ain_chipset;	/* parity generation policy */
	u_int8_t	ain_ramsize;
	u_int8_t	ain_flushintv;
	u_int8_t	ain_biosver[4];
	u_int8_t	ain_brdtype;
	u_int8_t	ain_scsisensealert;
	u_int8_t	ain_wrcfgcnt;	/* write config count */
	u_int8_t	ain_drvinscnt;	/* drive insertion count */
	u_int8_t	ain_insdrv;	/* inserted drive */
	u_int8_t	ain_battery;	/* battery status */
	u_int8_t	ain_reserved;

	u_int8_t	ain_nlogdrv;
	u_int8_t	ain_reserved1[3];
	u_int32_t	ain_ldsize[AMI_MAX_LDRIVES];
	u_int8_t	ain_ldprop[AMI_MAX_LDRIVES];
	u_int8_t	ain_ldstat[AMI_MAX_LDRIVES];

	u_int8_t	ain_pdstat[AMI_MAX_PDRIVES];
	u_int8_t	ain_predictivefailure;

	u_int8_t	ain_pdfmtinp[AMI_MAX_PDRIVES];
	u_int8_t	ain_reserved2[AMI_MAX_PDRIVES];

	u_int32_t	ain_esize;	/* extended data size */
	u_int16_t	ain_ssid;	/* subsystem id */
	u_int16_t	ain_ssvid;	/* subsystem vendor id */
	u_int32_t	ain_signature;
#define	AMI_SIGN431	0xfffe0001
#define	AMI_SIGN438	0xfffd0002
#define	AMI_SIGN762	0xfffc0003
#define	AMI_SIGNT5	0xfffb0004
#define	AMI_SIGN466	0xfffa0005
} __packed;

#define MAX_NOTIFY_SIZE	0x80
#define CUR_NOTIFY_SIZE (sizeof(struct ami_notify))
struct ami_notify {
	u_int32_t	ano_eventcounter;	/* incremented for changes */

	u_int8_t	ano_paramcounter;	/* param change */
	u_int8_t	ano_paramid;		/* param modified */
#define AMI_PARAM_RBLD_RATE		0x01 /* new rebuild rate */
#define AMI_PARAM_CACHE_FLUSH_INTERVAL	0x02 /* new cache flush interval */
#define AMI_PARAM_SENSE_ALERT		0x03 /* pd caused check condition */
#define AMI_PARAM_DRIVE_INSERTED	0x04 /* pd inserted */
#define AMI_PARAM_BATTERY_STATUS	0x05 /* battery status */
#define AMI_PARAM_NVRAM_EVENT_ALERT	0x06 /* NVRAM # of entries */
#define AMI_PARAM_PATROL_READ_UPDATE	0x07 /* # pd done with patrol read */
#define AMI_PARAM_PATROL_READ_STATUS	0x08 /* 0 stopped
					      * 2 aborted
					      * 4 started */

	u_int16_t	ano_paramval;		/* new val modified param */

	u_int8_t	ano_writeconfcounter;	/* write config */
	u_int8_t	ano_writeconfrsvd[3];

	u_int8_t	ano_ldopcounter;	/* ld op started/completed */
	u_int8_t	ano_ldopid;		/* ld modified */
	u_int8_t	ano_ldopcmd;		/* ld operation */
#define AMI_LDCMD_CHKCONSISTANCY	0x01
#define AMI_LDCMD_INITIALIZE		0x02
#define AMI_LDCMD_RECONSTRUCTION	0x03
	u_int8_t	ano_ldopstatus;		/* status of the operation */
#define AMI_LDOP_SUCCESS		0x00
#define AMI_LDOP_FAILED			0x01
#define AMI_LDOP_ABORTED		0x02
#define AMI_LDOP_CORRECTED		0x03
#define AMI_LDOP_STARTED		0x04

	u_int8_t	ano_ldstatecounter;	/* change of ld state */
	u_int8_t	ano_ldstateid;		/* ld state changed */
	u_int8_t	ano_ldstatenew;		/* new state */
	u_int8_t	ano_ldstateold;		/* old state */
#define AMI_RDRV_OFFLINE		0
#define AMI_RDRV_DEGRADED		1
#define AMI_RDRV_OPTIMAL		2
#define AMI_RDRV_DELETED		3

	u_int8_t	ano_pdstatecounter;	/* change of pd state */
	u_int8_t	ano_pdstateid;		/* pd state changed */
	u_int8_t	ano_pdstatenew;		/* new state */
	u_int8_t	ano_pdstateold;		/* old state */
#define AMI_PD_UNCNF			0
#define AMI_PD_ONLINE			3
#define AMI_PD_FAILED			4
#define AMI_PD_RBLD			5
#define AMI_PD_HOTSPARE			6

	u_int8_t	ano_pdfmtcounter;	/* pd format started/over */
	u_int8_t	ano_pdfmtid;		/* pd id */
	u_int8_t	ano_pdfmtval;		/* format started/over */
#define AMI_PDFMT_START			0x01
#define AMI_PDFMT_OVER			0x02
	u_int8_t	ano_pdfmtrsvd;

	u_int8_t	ano_targxfercounter;	/* SCSI-2 Xfer rate change */
	u_int8_t	ano_targxferid;		/* pd that changed  */
	u_int8_t	ano_targxferval;	/* new xfer parameters */
	u_int8_t	ano_targxferrsvd;

	u_int8_t	ano_fclidchgcounter;	/* loop id changed */
	u_int8_t	ano_fclidpdid;		/* pd id */
	u_int8_t	ano_fclid0;		/* loop id on fc loop 0 */
	u_int8_t	ano_fclid1;		/* loop id on fc loop 1 */

	u_int8_t	ano_fclstatecounter;	/* loop state changed */
	u_int8_t	ano_fclstate0;		/* state of fc loop 0 */
	u_int8_t	ano_fclstate1;		/* state of fc loop 1 */
#define AMI_FCLOOP_FAILED		0
#define AMI_FCLOOP_ACTIVE		1
#define AMI_FCLOOP_TRANSIENT		2
	u_int8_t	ano_fclstatersvd;
} __packed;

struct ami_fc_einquiry {
	u_int32_t	ain_size;	/* size of this structure */

	/* notify */
	struct	ami_notify ain_notify;
	u_int8_t	ain_notifyrsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE];

	u_int8_t	ain_rbldrate;	/* rebuild rate %% */
	u_int8_t	ain_flushintvl;
	u_int8_t	ain_sensealert;
	u_int8_t	ain_drvinscnt;	/* drive insertion count */
	u_int8_t	ain_battery;	/* battery status */

	u_int8_t	ain_nlogdrv;
	u_int8_t	ain_recon[AMI_BIG_MAX_LDRIVES / 8];
	u_int16_t	ain_stat[AMI_BIG_MAX_LDRIVES / 8];

	u_int32_t	ain_ldsize[AMI_BIG_MAX_LDRIVES];
	u_int8_t	ain_ldprop[AMI_BIG_MAX_LDRIVES];
	u_int8_t	ain_ldstat[AMI_BIG_MAX_LDRIVES];

	u_int8_t	ain_pdstat[AMI_BIG_MAX_PDRIVES];
	u_int16_t	ain_pdfmtinp[AMI_BIG_MAX_PDRIVES / 16];
	u_int8_t	ain_pdrates[80];	/* pdrv xfer rates */
	u_int8_t	ain_pad[263];		/* pad to 1k */
} __packed;

struct ami_fc_prodinfo {
	u_int32_t	api_size;	/* size of this structure */
	u_int32_t	api_config;
	u_int8_t	api_fwver[16];
	u_int8_t	api_biosver[16];
	u_int8_t	api_product[80];
	u_int8_t	api_maxcmd;
	u_int8_t	api_channels;
	u_int8_t	api_fcloops;
	u_int8_t	api_memtype;
	u_int32_t	api_signature;
	u_int16_t	api_ramsize;
	u_int16_t	api_ssid;
	u_int16_t	api_ssvid;
	u_int8_t	api_nnotify;
} __packed;

struct ami_diskarray {
	u_int8_t	ada_nld;
	u_int8_t	ada_pad[3];
	struct {
		u_int8_t	adl_spandepth;
		u_int8_t	adl_raidlvl;
		u_int8_t	adl_rdahead;
		u_int8_t	adl_stripesz;
		u_int8_t	adl_status;
		u_int8_t	adl_wrpolicy;
		u_int8_t	adl_directio;
		u_int8_t	adl_nstripes;
		struct {
			u_int32_t	ads_start;
			u_int32_t	ads_length;	/* blocks */
			struct {
				u_int8_t	add_channel;
				u_int8_t	add_target;
			} __packed ads_devs[AMI_MAX_DEVDEPTH];
		} __packed adl_spans[AMI_MAX_SPANDEPTH];
	} __packed ada_ldrv[AMI_MAX_LDRIVES];
	struct {
		u_int8_t	adp_type;	/* SCSI device type */
		u_int8_t	adp_ostatus;	/* status during config */
		u_int8_t	adp_tagdepth;	/* level of tagging */
		u_int8_t	adp_sneg;	/* sync negotiation */
		u_int32_t	adp_size;
	} __packed ada_pdrv[AMI_MAX_PDRIVES];
} __packed;

struct ami_big_diskarray {
	u_int8_t	ada_nld;
	u_int8_t	ada_pad[3];
#define ald ada_ldrv
	struct {
		u_int8_t	adl_spandepth;
		u_int8_t	adl_raidlvl;
		u_int8_t	adl_rdahead;
		u_int8_t	adl_stripesz;
		u_int8_t	adl_status;
		u_int8_t	adl_wrpolicy;
		u_int8_t	adl_directio;
		u_int8_t	adl_nstripes;
#define 	asp adl_spans
		struct {
			u_int32_t	ads_start;
			u_int32_t	ads_length;	/* blocks */
#define 		adv ads_devs
			struct {
				u_int8_t	add_channel;
				u_int8_t	add_target;
			} __packed ads_devs[AMI_BIG_MAX_DEVDEPTH];
		} __packed adl_spans[AMI_BIG_MAX_SPANDEPTH];
	} __packed ada_ldrv[AMI_BIG_MAX_LDRIVES];
#define apd ada_pdrv
	struct {
		u_int8_t	adp_type;	/* SCSI device type */
		u_int8_t	adp_ostatus;	/* status during config */
		u_int8_t	adp_tagdepth;	/* level of tagging */
		u_int8_t	adp_sneg;	/* sync negotiation */
		u_int32_t	adp_size;
	} __packed ada_pdrv[AMI_BIG_MAX_PDRIVES];
} __packed;

struct ami_scsisense {
	u_int8_t	ase_end;
	struct {
		u_int8_t	asd_channel;
		u_int8_t	asd_target;
		u_int16_t	asd_errcode;
		u_int16_t	asd_sense;
		u_int16_t	asd_addarea1;
		u_int16_t	asd_addarea2;
		u_int16_t	asd_cmdspec0;
		u_int16_t	asd_cmdspec1;
		u_int16_t	asd_asc_ascq;
	} __packed ase_dump[5];
} __packed;

struct ami_escsisense {
	u_int8_t	ase_end;
	struct {
		u_int8_t	asd_channel;
		u_int8_t	asd_target;
		u_int16_t	asd_errcode;
		u_int16_t	asd_sense;
		u_int16_t	asd_addarea1;
		u_int16_t	asd_addarea2;
		u_int16_t	asd_cmdspec0;
		u_int16_t	asd_cmdspec1;
		u_int16_t	asd_asc_ascq;
		u_int16_t	asd_extarea;
	} __packed ase_dump[5];
} __packed;

struct ami_cachestats {
	u_int32_t	acs_total;
	u_int32_t	acs_hits;
} __packed;

struct ami_drivehistory {
	struct {
		u_int8_t	adh_error;
#define	AMI_ADHERR_TIMEOUT(e)	((e) & 15)
#define	AMI_ADHERR_PARITY(e)	(((e) >> 4) & 15)
		u_int8_t	adh_throttle;
	} __packed adh_err[3][16];	/* channels * drives */
	u_int8_t	adh_failidx;
	struct {
		u_int8_t	adh_tag;
#define	AMI_ADHTAG_CH(t)	((t) & 7)
#define	AMI_ADHTAG_TARG(t)	(((t) >> 3) & 15)
#define	AMI_ADHTAG_VALID(t)	((t) & 0x80)
		u_int8_t	reason;
#define	AMI_ADHERR_MEDIA	1
#define	AMI_ADHERR_NMEDIA	2
#define	AMI_ADHERR_CMDTMO	3
#define	AMI_ADHERR_SELTMO	4
#define	AMI_ADHERR_HAFAIL	5
#define	AMI_ADHERR_REASSIGN	6
#define	AMI_ADHERR_CMDFAIL	7
#define	AMI_ADHERR_OTHER	8

#define	AMI_FAILHISTORY		10
	} __packed adh_fail[AMI_FAILHISTORY];
} __packed;

struct ami_progress {
	u_int32_t	apr_progress;
} __packed;


code:
/*	$OpenBSD: amivar.h,v 1.58 2010/06/23 04:53:53 dlg Exp $	*/

/*
 * Copyright (c) 2001 Michael Shalayeff
 * Copyright (c) 2005 Marco Peereboom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

struct ami_mem {
	bus_dmamap_t		am_map;
	bus_dma_segment_t	am_seg;
	size_t			am_size;
	caddr_t			am_kva;
};

#define AMIMEM_MAP(_am)		((_am)->am_map)
#define AMIMEM_DVA(_am)		((_am)->am_map->dm_segs[0].ds_addr)
#define AMIMEM_KVA(_am)		((void *)(_am)->am_kva)

struct ami_ccbmem {
	struct ami_passthrough	cd_pt;
	struct ami_sgent	cd_sg[AMI_SGEPERCMD];
};

struct ami_softc;

struct ami_ccb {
	struct ami_softc	*ccb_sc;

	struct ami_iocmd	ccb_cmd;
	struct ami_passthrough	*ccb_pt;
	paddr_t			ccb_ptpa;
	struct ami_sgent	*ccb_sglist;
	paddr_t			ccb_sglistpa;
	int			ccb_offset;
	bus_dmamap_t		ccb_dmamap;

	struct scsi_xfer	*ccb_xs;
	void			(*ccb_done)(struct ami_softc *sc,
				    struct ami_ccb *ccb);

	volatile enum {
		AMI_CCB_FREE,
		AMI_CCB_READY,
		AMI_CCB_QUEUED,
		AMI_CCB_PREQUEUED
	}			ccb_state;
	int			ccb_flags;
#define AMI_CCB_F_ERR			(1<<0)
	int			ccb_status;
	TAILQ_ENTRY(ami_ccb)	ccb_link;
};

TAILQ_HEAD(ami_ccb_list, ami_ccb);

struct ami_rawsoftc {
	struct scsi_link	sc_link;
	struct ami_softc	*sc_softc;
	u_int8_t		sc_channel;

	int			sc_proctarget;	/* ses/safte target id */
	char			sc_procdev[16];	/* ses/safte device */
};

struct ami_softc {
	struct device		sc_dev;
	void			*sc_ih;
	struct scsi_link	sc_link;

	int			sc_flags;
#define AMI_CHECK_SIGN	0x0001
#define AMI_BROKEN 	0x0002
#define AMI_QUARTZ	0x0008

	/* low-level interface */
	int			(*sc_init)(struct ami_softc *sc);
	int			(*sc_exec)(struct ami_softc *sc,
				    struct ami_iocmd *);
	int			(*sc_done)(struct ami_softc *sc,
				    struct ami_iocmd *);
	int			(*sc_poll)(struct ami_softc *sc,
				    struct ami_iocmd *);
	int			(*sc_ioctl)(struct device *, u_long, caddr_t);

	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	bus_dma_tag_t		sc_dmat;

	struct ami_ccb		*sc_ccbs;
	struct ami_ccb_list	sc_ccb_freeq;
	struct mutex		sc_ccb_freeq_mtx;

	struct ami_mem		*sc_mbox_am;
	volatile struct ami_iocmd *sc_mbox;
	paddr_t			sc_mbox_pa;

	struct ami_ccb_list	sc_ccb_preq, sc_ccb_runq;
	struct mutex		sc_cmd_mtx;

	struct scsi_iopool	sc_iopool;

	struct ami_mem		*sc_ccbmem_am;

	int			sc_timeout;
	struct timeout		sc_run_tmo;
	int			sc_dis_poll;

	struct rwlock		sc_lock;

	char			sc_fwver[16];
	char			sc_biosver[16];
	int			sc_maxcmds;
	int			sc_memory;
	int			sc_targets;
	int			sc_channels;
	int			sc_maxunits;
	int			sc_nunits;
	struct {
		u_int8_t		hd_present;
		u_int8_t		hd_is_logdrv;
		u_int8_t		hd_prop;
		u_int8_t		hd_stat;
		u_int32_t		hd_size;
		char			dev[16];
	}			sc_hdr[AMI_BIG_MAX_LDRIVES];
	struct ami_rawsoftc	*sc_rawsoftcs;

	struct ksensor		*sc_sensors;
	struct ksensordev	sc_sensordev;
	struct ami_big_diskarray *sc_bd;

	/* bio stuff */
	struct bioc_inq		sc_bi;
	char			sc_plist[AMI_BIG_MAX_PDRIVES];

	struct ami_ccb		*sc_mgmtccb;
	int			sc_drainio;
	u_int8_t		sc_drvinscnt;
};

int  ami_attach(struct ami_softc *sc);
int  ami_intr(void *);

int ami_quartz_init(struct ami_softc *sc);
int ami_quartz_exec(struct ami_softc *sc, struct ami_iocmd *);
int ami_quartz_done(struct ami_softc *sc, struct ami_iocmd *);
int ami_quartz_poll(struct ami_softc *sc, struct ami_iocmd *);

int ami_schwartz_init(struct ami_softc *sc);
int ami_schwartz_exec(struct ami_softc *sc, struct ami_iocmd *);
int ami_schwartz_done(struct ami_softc *sc, struct ami_iocmd *);
int ami_schwartz_poll(struct ami_softc *sc, struct ami_iocmd *);

#ifdef AMI_DEBUG
void ami_print_mbox(struct ami_iocmd *);
#endif /* AMI_DEBUG */

license: unknown


code:
/*	$OpenBSD: an.c,v 1.65 2015/06/21 21:22:27 krw Exp $	*/
/*	$NetBSD: an.c,v 1.34 2005/06/20 02:49:18 atatat Exp $	*/
/*
 * Copyright (c) 1997, 1998, 1999
 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/dev/an/if_an.c,v 1.12 2000/11/13 23:04:12 wpaul Exp $
 */
/*
 * Copyright (c) 2004, 2005 David Young.  All rights reserved.
 * Copyright (c) 2004, 2005 OJC Technologies.  All rights reserved.
 * Copyright (c) 2004, 2005 Dayton Data Center Services, LLC.  All
 *     rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY David Young AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL David Young AND CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Aironet 4500/4800 802.11 PCMCIA/ISA/PCI driver for FreeBSD.
 *
 * Written by Bill Paul <wpaul@ctr.columbia.edu>
 * Electrical Engineering Department
 * Columbia University, New York City
 */

/*
 * Ported to NetBSD from FreeBSD by Atsushi Onoe at the San Diego
 * IETF meeting.
 */

#include "bpfilter.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/ucred.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <sys/device.h>
#include <sys/endian.h>
#include <sys/tree.h>

#include <machine/bus.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_llc.h>
#include <net/if_media.h>
#include <net/if_types.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_var.h>

#if NBPFILTER > 0
#include <net/bpf.h>
#endif

#include <dev/ic/anreg.h>
#include <dev/ic/anvar.h>

struct cfdriver an_cd = {
	NULL, "an", DV_IFNET
};

int	an_reset(struct an_softc *);
void	an_wait(struct an_softc *);
int	an_init(struct ifnet *);
void	an_stop(struct ifnet *, int);
void	an_start(struct ifnet *);
void	an_watchdog(struct ifnet *);
int	an_ioctl(struct ifnet *, u_long, caddr_t);
int	an_media_change(struct ifnet *);
void	an_media_status(struct ifnet *, struct ifmediareq *);

int	an_set_nwkey(struct an_softc *, struct ieee80211_nwkey *);
int	an_set_nwkey_wep(struct an_softc *, struct ieee80211_nwkey *);
int	an_get_nwkey(struct an_softc *, struct ieee80211_nwkey *);
int	an_write_wepkey(struct an_softc *, int, struct an_wepkey *,
				int);

void	an_rxeof(struct an_softc *);
void	an_txeof(struct an_softc *, u_int16_t);
void	an_linkstat_intr(struct an_softc *);

int	an_cmd(struct an_softc *, int, int);
int	an_seek_bap(struct an_softc *, int, int);
int	an_read_bap(struct an_softc *, int, int, void *, int, int);
int	an_write_bap(struct an_softc *, int, int, void *, int);
int	an_mwrite_bap(struct an_softc *, int, int, struct mbuf *, int);
int	an_read_rid(struct an_softc *, int, void *, int *);
int	an_write_rid(struct an_softc *, int, void *, int);

int	an_alloc_nicmem(struct an_softc *, int, int *);

int	an_newstate(struct ieee80211com *, enum ieee80211_state, int);

#ifdef AN_DEBUG
int an_debug = 0;

#define	DPRINTF(X)	if (an_debug) printf X
#define	DPRINTF2(X)	if (an_debug > 1) printf X
#else
#define	DPRINTF(X)
#define	DPRINTF2(X)
#endif

#if BYTE_ORDER == BIG_ENDIAN
static __inline void
an_swap16(u_int16_t *p, int cnt)
{
        for (; cnt--; p++)
                *p = swap16(*p);
}
#define an_switch32(val)	(val >> 16 | (val & 0xFFFF) << 16)
#else
#define an_swap16(p, cnt)
#define an_switch32(val)	val
#endif

int
an_attach(struct an_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifnet *ifp = &ic->ic_if;
	int i;
	struct an_rid_wepkey *akey;
	int buflen, kid, rid;
	int chan, chan_min, chan_max;

	sc->sc_invalid = 0;

	/* disable interrupts */
	CSR_WRITE_2(sc, AN_INT_EN, 0);
	CSR_WRITE_2(sc, AN_EVENT_ACK, 0xffff);

//	an_wait(sc);
	if (an_reset(sc) != 0) {
		sc->sc_invalid = 1;
		return 1;
	}

	/* Load factory config */
	if (an_cmd(sc, AN_CMD_READCFG, 0) != 0) {
		printf("%s: failed to load config data\n",
		    sc->sc_dev.dv_xname);
		return (EIO);
	}

	/* Read the current configuration */
	buflen = sizeof(sc->sc_config);
	if (an_read_rid(sc, AN_RID_GENCONFIG, &sc->sc_config, &buflen) != 0) {
		printf("%s: read config failed\n", sc->sc_dev.dv_xname);
		return(EIO);
	}

	an_swap16((u_int16_t *)&sc->sc_config.an_macaddr, 3);

	/* Read the card capabilities */
	buflen = sizeof(sc->sc_caps);
	if (an_read_rid(sc, AN_RID_CAPABILITIES, &sc->sc_caps, &buflen) != 0) {
		printf("%s: read caps failed\n", sc->sc_dev.dv_xname);
		return(EIO);
	}

	an_swap16((u_int16_t *)&sc->sc_caps.an_oemaddr, 3);
	an_swap16((u_int16_t *)&sc->sc_caps.an_rates, 4);

	/* Read WEP settings from persistent memory */
	akey = &sc->sc_buf.sc_wepkey;
	buflen = sizeof(struct an_rid_wepkey);
	rid = AN_RID_WEP_VOLATILE;	/* first persistent key */
	while (an_read_rid(sc, rid, akey, &buflen) == 0) {
		an_swap16((u_int16_t *)&akey->an_mac_addr, 3);
		an_swap16((u_int16_t *)&akey->an_key, 8);
		kid = akey->an_key_index;
		DPRINTF(("an_attach: wep rid=0x%x len=%d(%d) index=0x%04x "
		    "mac[0]=%02x keylen=%d\n",
		    rid, buflen, sizeof(*akey), kid,
		    akey->an_mac_addr[0], akey->an_key_len));
		if (kid == 0xffff) {
			sc->sc_tx_perskey = akey->an_mac_addr[0];
			sc->sc_tx_key = -1;
			break;
		}
		if (kid >= IEEE80211_WEP_NKID)
			break;
		sc->sc_perskeylen[kid] = akey->an_key_len;
		sc->sc_wepkeys[kid].an_wep_keylen = -1;
		rid = AN_RID_WEP_PERSISTENT;	/* for next key */
		buflen = sizeof(struct an_rid_wepkey);
	}

	IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_caps.an_oemaddr);
	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);

	printf("%s: Firmware %x.%02x.%02x, Radio: ", ifp->if_xname,
	    sc->sc_caps.an_fwrev >> 8,
	    sc->sc_caps.an_fwrev & 0xff,
	    sc->sc_caps.an_fwsubrev);

	if (sc->sc_config.an_radiotype & AN_RADIOTYPE_80211_FH)
		printf("802.11 FH");
	else if (sc->sc_config.an_radiotype & AN_RADIOTYPE_80211_DS)
		printf("802.11 DS");
	else if (sc->sc_config.an_radiotype & AN_RADIOTYPE_LM2000_DS)
		printf("LM2000 DS");
	else
		printf("unknown (%x)", sc->sc_config.an_radiotype);

	printf(", address %s\n", ether_sprintf(ic->ic_myaddr));

	ifp->if_softc = sc;
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	ifp->if_ioctl = an_ioctl;
	ifp->if_start = an_start;
	ifp->if_watchdog = an_watchdog;
	IFQ_SET_READY(&ifp->if_snd);

	ic->ic_phytype = IEEE80211_T_DS;
	ic->ic_opmode = IEEE80211_M_STA;
	ic->ic_caps = IEEE80211_C_WEP | IEEE80211_C_PMGT | IEEE80211_C_MONITOR;
#ifndef IEEE80211_STA_ONLY
	ic->ic_caps |= IEEE80211_C_IBSS;
#endif
	ic->ic_state = IEEE80211_S_INIT;
	IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_caps.an_oemaddr);

	switch (sc->sc_caps.an_regdomain) {
	default:
	case AN_REGDOMAIN_USA:
	case AN_REGDOMAIN_CANADA:
		chan_min = 1; chan_max = 11; break;
	case AN_REGDOMAIN_EUROPE:
	case AN_REGDOMAIN_AUSTRALIA:
		chan_min = 1; chan_max = 13; break;
	case AN_REGDOMAIN_JAPAN:
		chan_min = 14; chan_max = 14; break;
	case AN_REGDOMAIN_SPAIN:
		chan_min = 10; chan_max = 11; break;
	case AN_REGDOMAIN_FRANCE:
		chan_min = 10; chan_max = 13; break;
	case AN_REGDOMAIN_JAPANWIDE:
		chan_min = 1; chan_max = 14; break;
	}

	for (chan = chan_min; chan <= chan_max; chan++) {
		ic->ic_channels[chan].ic_freq =
		    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
		ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_B;
	}
	ic->ic_ibss_chan = &ic->ic_channels[chan_min];

	/* Find supported rate */
	for (i = 0; i < sizeof(sc->sc_caps.an_rates); i++) {
		if (sc->sc_caps.an_rates[i] == 0)
			continue;
		ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[
		    ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates++] =
		    sc->sc_caps.an_rates[i];
	}

	/*
	 * Call MI attach routine.
	 */
	if_attach(ifp);
	ieee80211_ifattach(ifp);

	sc->sc_newstate = ic->ic_newstate;
	ic->ic_newstate = an_newstate;

	ieee80211_media_init(ifp, an_media_change, an_media_status);

#if NBPFILTER > 0
	bzero(&sc->sc_rxtapu, sizeof(sc->sc_rxtapu));
	sc->sc_rxtap.ar_ihdr.it_len = sizeof(sc->sc_rxtapu);
	sc->sc_rxtap.ar_ihdr.it_present = AN_RX_RADIOTAP_PRESENT;

	bzero(&sc->sc_txtapu, sizeof(sc->sc_txtapu));
	sc->sc_txtap.at_ihdr.it_len = sizeof(sc->sc_txtapu);
	sc->sc_txtap.at_ihdr.it_present = AN_TX_RADIOTAP_PRESENT;

	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
	    sizeof(struct ieee80211_frame) + 64);
#endif

	sc->sc_attached = 1;

	return(0);
}

void
an_rxeof(struct an_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifnet *ifp = &ic->ic_if;
	struct ieee80211_frame *wh;
	struct ieee80211_rxinfo rxi;
	struct ieee80211_node *ni;
	struct an_rxframe frmhdr;
	struct mbuf *m;
	u_int16_t status;
	int fid, gaplen, len, off;
	uint8_t *gap;

	fid = CSR_READ_2(sc, AN_RX_FID);

	/* First read in the frame header */
	if (an_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr), sizeof(frmhdr)) != 0) {
		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
		ifp->if_ierrors++;
		DPRINTF(("an_rxeof: read fid %x failed\n", fid));
		return;
	}
	an_swap16((u_int16_t *)&frmhdr.an_whdr, sizeof(struct ieee80211_frame)/2);

	status = frmhdr.an_rx_status;
	if ((status & AN_STAT_ERRSTAT) != 0 &&
	    ic->ic_opmode != IEEE80211_M_MONITOR) {
		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
		ifp->if_ierrors++;
		DPRINTF(("an_rxeof: fid %x status %x\n", fid, status));
		return;
	}

	/* the payload length field includes a 16-bit "mystery field" */
	len = frmhdr.an_rx_payload_len - sizeof(uint16_t);
	off = ALIGN(sizeof(struct ieee80211_frame));

	if (off + len > MCLBYTES) {
		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
			CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
			ifp->if_ierrors++;
			DPRINTF(("an_rxeof: oversized packet %d\n", len));
			return;
		}
		len = 0;
	}

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL) {
		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
		ifp->if_ierrors++;
		DPRINTF(("an_rxeof: MGET failed\n"));
		return;
	}
	if (off + len + AN_GAPLEN_MAX > MHLEN) {
		MCLGET(m, M_DONTWAIT);
		if ((m->m_flags & M_EXT) == 0) {
			CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
			m_freem(m);
			ifp->if_ierrors++;
			DPRINTF(("an_rxeof: MCLGET failed\n"));
			return;
		}
	}
	m->m_data += off - sizeof(struct ieee80211_frame);

	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
		gaplen = frmhdr.an_gaplen;
		if (gaplen > AN_GAPLEN_MAX) {
			CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
			m_freem(m);
			ifp->if_ierrors++;
			DPRINTF(("%s: gap too long\n", __func__));
			return;
		}
		/*
		 * We don't need the 16-bit mystery field (payload length?),
		 * so read it into the region reserved for the 802.11 header.
		 *
		 * When Cisco Aironet 350 cards w/ firmware version 5 or
		 * greater operate with certain Cisco 350 APs,
		 * the "gap" is filled with the SNAP header.  Read
		 * it in after the 802.11 header.
		 */
		gap = m->m_data + sizeof(struct ieee80211_frame) -
		    sizeof(uint16_t);
		an_read_bap(sc, fid, -1, gap, gaplen + sizeof(u_int16_t),
		    gaplen + sizeof(u_int16_t));
	} else
		gaplen = 0;

	an_read_bap(sc, fid, -1,
	    m->m_data + sizeof(struct ieee80211_frame) + gaplen, len, len);
	an_swap16((u_int16_t *)(m->m_data + sizeof(struct ieee80211_frame) + gaplen), (len+1)/2);
	m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + gaplen +
	    len;

	memcpy(m->m_data, &frmhdr.an_whdr, sizeof(struct ieee80211_frame));
	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);

#if NBPFILTER > 0
	if (sc->sc_drvbpf) {
		struct mbuf mb;
		struct an_rx_radiotap_header *tap = &sc->sc_rxtap;

		tap->ar_rate = frmhdr.an_rx_rate;
		tap->ar_antsignal = frmhdr.an_rx_signal_strength;
		tap->ar_chan_freq = ic->ic_bss->ni_chan->ic_freq;
		tap->ar_chan_flags = ic->ic_bss->ni_chan->ic_flags;

		mb.m_data = (caddr_t)tap;
		mb.m_len = sizeof(sc->sc_rxtapu);
		mb.m_next = m;
		mb.m_nextpkt = NULL;
		mb.m_type = 0;
		mb.m_flags = 0;
		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
	}
#endif /* NBPFILTER > 0 */

	wh = mtod(m, struct ieee80211_frame *);
	rxi.rxi_flags = 0;
	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
		/*
		 * WEP is decrypted by hardware. Clear WEP bit
		 * header for ieee80211_input().
		 */
		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;

		rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
	}

	ni = ieee80211_find_rxnode(ic, wh);
	rxi.rxi_rssi = frmhdr.an_rx_signal_strength;
	rxi.rxi_tstamp = an_switch32(frmhdr.an_rx_time);
	ieee80211_input(ifp, m, ni, &rxi);
	ieee80211_release_node(ic, ni);
}

void
an_txeof(struct an_softc *sc, u_int16_t status)
{
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	int cur, id;

	sc->sc_tx_timer = 0;
	ifp->if_flags &= ~IFF_OACTIVE;

	id = CSR_READ_2(sc, AN_TX_CMP_FID);
	CSR_WRITE_2(sc, AN_EVENT_ACK, status & (AN_EV_TX | AN_EV_TX_EXC));

	if (status & AN_EV_TX_EXC)
		ifp->if_oerrors++;
	else
		ifp->if_opackets++;

	cur = sc->sc_txcur;
	if (sc->sc_txd[cur].d_fid == id) {
		sc->sc_txd[cur].d_inuse = 0;
		DPRINTF2(("an_txeof: sent %x/%d\n", id, cur));
		AN_INC(cur, AN_TX_RING_CNT);
		sc->sc_txcur = cur;
	} else {
		for (cur = 0; cur < AN_TX_RING_CNT; cur++) {
			if (id == sc->sc_txd[cur].d_fid) {
				sc->sc_txd[cur].d_inuse = 0;
				break;
			}
		}
		if (ifp->if_flags & IFF_DEBUG)
			printf("%s: tx mismatch: "
			    "expected %x(%d), actual %x(%d)\n",
			    sc->sc_dev.dv_xname,
			    sc->sc_txd[sc->sc_txcur].d_fid, sc->sc_txcur,
			    id, cur);
	}
}

int
an_intr(void *arg)
{
	struct an_softc *sc = arg;
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	int i;
	u_int16_t status;

	if (!sc->sc_enabled || sc->sc_invalid ||
	    (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0 ||
	    (ifp->if_flags & IFF_RUNNING) == 0)
		return 0;

	if ((ifp->if_flags & IFF_UP) == 0) {
		CSR_WRITE_2(sc, AN_INT_EN, 0);
		CSR_WRITE_2(sc, AN_EVENT_ACK, ~0);
		return 1;
	}

	/* maximum 10 loops per interrupt */
	for (i = 0; i < 10; i++) {
		if (!sc->sc_enabled || sc->sc_invalid)
			return 1;
		if (CSR_READ_2(sc, AN_SW0) != AN_MAGIC) {
			DPRINTF(("an_intr: magic number changed: %x\n",
			    CSR_READ_2(sc, AN_SW0)));
			sc->sc_invalid = 1;
			return 1;
		}
		status = CSR_READ_2(sc, AN_EVENT_STAT);
		CSR_WRITE_2(sc, AN_EVENT_ACK, status & ~(AN_INTRS));
		if ((status & AN_INTRS) == 0)
			break;

		if (status & AN_EV_RX)
			an_rxeof(sc);

		if (status & (AN_EV_TX | AN_EV_TX_EXC))
			an_txeof(sc, status);

		if (status & AN_EV_LINKSTAT)
			an_linkstat_intr(sc);

		if ((ifp->if_flags & IFF_OACTIVE) == 0 &&
		    sc->sc_ic.ic_state == IEEE80211_S_RUN &&
		    !IFQ_IS_EMPTY(&ifp->if_snd))
			an_start(ifp);
	}

	return 1;
}

/* Must be called at proper protection level! */
int
an_cmd(struct an_softc *sc, int cmd, int val)
{
	int i, stat;

	/* make sure previous command completed */
	if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) {
		if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
			printf("%s: command 0x%x busy\n", sc->sc_dev.dv_xname,
			    CSR_READ_2(sc, AN_COMMAND));
		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY);
	}

	CSR_WRITE_2(sc, AN_PARAM0, val);
	CSR_WRITE_2(sc, AN_PARAM1, 0);
	CSR_WRITE_2(sc, AN_PARAM2, 0);
	CSR_WRITE_2(sc, AN_COMMAND, cmd);

	if (cmd == AN_CMD_FW_RESTART) {
		/* XXX: should sleep here */
		DELAY(100*1000);
	}

	for (i = 0; i < AN_TIMEOUT; i++) {
		if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD)
			break;
		DELAY(10);
	}

	stat = CSR_READ_2(sc, AN_STATUS);

	/* clear stuck command busy if necessary */
	if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY)
		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY);

	/* Ack the command */
	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD);

	if (i == AN_TIMEOUT) {
		if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
			printf("%s: command 0x%x param 0x%x timeout\n",
			    sc->sc_dev.dv_xname, cmd, val);
		return ETIMEDOUT;
	}
	if (stat & AN_STAT_CMD_RESULT) {
		if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
			printf("%s: command 0x%x param 0x%x status 0x%x "
			    "resp 0x%x 0x%x 0x%x\n",
			    sc->sc_dev.dv_xname, cmd, val, stat,
			    CSR_READ_2(sc, AN_RESP0), CSR_READ_2(sc, AN_RESP1),
			    CSR_READ_2(sc, AN_RESP2));
		return EIO;
	}

	return 0;
}

int
an_reset(struct an_softc *sc)
{

	DPRINTF(("an_reset\n"));

	if (!sc->sc_enabled)
		return ENXIO;

	an_cmd(sc, AN_CMD_ENABLE, 0);
	an_cmd(sc, AN_CMD_FW_RESTART, 0);
	an_cmd(sc, AN_CMD_NOOP2, 0);

	if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) {
		printf("%s: reset failed\n", sc->sc_dev.dv_xname);
		return ETIMEDOUT;
	}

	an_cmd(sc, AN_CMD_DISABLE, 0);
	return 0;
}

void
an_linkstat_intr(struct an_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	u_int16_t status;

	status = CSR_READ_2(sc, AN_LINKSTAT);
	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT);
	DPRINTF(("an_linkstat_intr: status 0x%x\n", status));

	if (status == AN_LINKSTAT_ASSOCIATED) {
		if (ic->ic_state != IEEE80211_S_RUN
#ifndef IEEE80211_STA_ONLY
		    || ic->ic_opmode == IEEE80211_M_IBSS
#endif
		    )
			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
	} else {
		if (ic->ic_opmode == IEEE80211_M_STA)
			ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
	}
}

/*
 * Wait for firmware come up after power enabled.
 */
void
an_wait(struct an_softc *sc)
{
	int i;

	CSR_WRITE_2(sc, AN_COMMAND, AN_CMD_NOOP2);
	for (i = 0; i < 3*hz; i++) {
		if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD)
			break;
		(void)tsleep(sc, PWAIT, "anatch", 1);
	}
	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD);
}

int
an_read_bap(struct an_softc *sc, int id, int off, void *buf, int len, int blen)
{
	int error, cnt, cnt2;

	if (len == 0 || blen == 0)
		return 0;
	if (off == -1)
		off = sc->sc_bap_off;
	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
		if ((error = an_seek_bap(sc, id, off)) != 0)
			return EIO;
	}

	cnt = (blen + 1) / 2;
	CSR_READ_MULTI_STREAM_2(sc, AN_DATA0, (u_int16_t *)buf, cnt);
	for (cnt2 = (len + 1) / 2; cnt < cnt2; cnt++)
		(void) CSR_READ_2(sc, AN_DATA0);
	sc->sc_bap_off += cnt * 2;

	return 0;
}

int
an_write_bap(struct an_softc *sc, int id, int off, void *buf, int buflen)
{
	int error, cnt;

	if (buflen == 0)
		return 0;
	if (off == -1)
		off = sc->sc_bap_off;
	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
		if ((error = an_seek_bap(sc, id, off)) != 0)
			return EIO;
	}

	cnt = (buflen + 1) / 2;
	CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0, (u_int16_t *)buf, cnt);
	sc->sc_bap_off += cnt * 2;
	return 0;
}

int
an_seek_bap(struct an_softc *sc, int id, int off)
{
	int i, status;

	CSR_WRITE_2(sc, AN_SEL0, id);
	CSR_WRITE_2(sc, AN_OFF0, off);

	for (i = 0; ; i++) {
		status = CSR_READ_2(sc, AN_OFF0);
		if ((status & AN_OFF_BUSY) == 0)
			break;
		if (i == AN_TIMEOUT) {
			printf("%s: timeout in an_seek_bap to 0x%x/0x%x\n",
			    sc->sc_dev.dv_xname, id, off);
			sc->sc_bap_off = AN_OFF_ERR;	/* invalidate */
			return ETIMEDOUT;
		}
		DELAY(10);
	}
	if (status & AN_OFF_ERR) {
		printf("%s: failed in an_seek_bap to 0x%x/0x%x\n",
		    sc->sc_dev.dv_xname, id, off);
		sc->sc_bap_off = AN_OFF_ERR;	/* invalidate */
		return EIO;
	}
	sc->sc_bap_id = id;
	sc->sc_bap_off = off;
	return 0;
}

int
an_mwrite_bap(struct an_softc *sc, int id, int off, struct mbuf *m, int totlen)
{
	int error, len, cnt;

	if (off == -1)
		off = sc->sc_bap_off;
	if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
		if ((error = an_seek_bap(sc, id, off)) != 0)
			return EIO;
	}

	for (len = 0; m != NULL; m = m->m_next) {
		if (m->m_len == 0)
			continue;
		len = min(m->m_len, totlen);

		if ((mtod(m, u_long) & 0x1) || (len & 0x1)) {
			m_copydata(m, 0, totlen, (caddr_t)&sc->sc_buf.sc_txbuf);
			cnt = (totlen + 1) / 2;
			an_swap16((u_int16_t *)&sc->sc_buf.sc_txbuf, cnt);
			CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0,
			    sc->sc_buf.sc_val, cnt);
			off += cnt * 2;
			break;
		}
		cnt = len / 2;
		an_swap16((u_int16_t *)mtod(m, u_int16_t *), cnt);
		CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0, mtod(m, u_int16_t *),
		    cnt);
		off += len;
		totlen -= len;
	}
	sc->sc_bap_off = off;
	return 0;
}

int
an_alloc_nicmem(struct an_softc *sc, int len, int *idp)
{
	int i;

	if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) {
		printf("%s: failed to allocate %d bytes on NIC\n",
		    sc->sc_dev.dv_xname, len);
		return(ENOMEM);
	}

	for (i = 0; i < AN_TIMEOUT; i++) {
		if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC)
			break;
		if (i == AN_TIMEOUT) {
			printf("%s: timeout in alloc\n", sc->sc_dev.dv_xname);
			return ETIMEDOUT;
		}
		DELAY(10);
	}

	*idp = CSR_READ_2(sc, AN_ALLOC_FID);
	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC);
	return 0;
}

int
an_read_rid(struct an_softc *sc, int rid, void *buf, int *buflenp)
{
	int error;
	u_int16_t len;

	/* Tell the NIC to enter record read mode. */
	error = an_cmd(sc, AN_CMD_ACCESS | AN_ACCESS_READ, rid);
	if (error)
		return error;

	/* length in byte, including length itself */
	error = an_read_bap(sc, rid, 0, &len, sizeof(len), sizeof(len));
	if (error)
		return error;

	len -= 2;
	return an_read_bap(sc, rid, sizeof(len), buf, len, *buflenp);
}

int
an_write_rid(struct an_softc *sc, int rid, void *buf, int buflen)
{
	int error;
	u_int16_t len;

	/* length in byte, including length itself */
	len = buflen + 2;

	error = an_write_bap(sc, rid, 0, &len, sizeof(len));
	if (error)
		return error;
	error = an_write_bap(sc, rid, sizeof(len), buf, buflen);
	if (error)
		return error;

	return an_cmd(sc, AN_CMD_ACCESS | AN_ACCESS_WRITE, rid);
}

int
an_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
	struct an_softc *sc = ifp->if_softc;
	struct ifaddr *ifa = (struct ifaddr *)data;
	int s, error = 0;

	if ((sc->sc_dev.dv_flags & DVF_ACTIVE) == 0)
		return ENXIO;

	s = splnet();

	switch(command) {
	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		switch (ifa->ifa_addr->sa_family) {
		case AF_INET:
			error = an_init(ifp);
			arp_ifinit(&sc->sc_ic.ic_ac, ifa);
			break;
		default:
			error = an_init(ifp);
			break;
		}
		break;
	case SIOCSIFFLAGS:
		if (ifp->if_flags & IFF_UP) {
			if (sc->sc_enabled) {
				/*
				 * To avoid rescanning another access point,
				 * do not call an_init() here.  Instead, only
				 * reflect promisc mode settings.
				 */
				error = an_cmd(sc, AN_CMD_SET_MODE,
				    (ifp->if_flags & IFF_PROMISC) ? 0xffff : 0);
			} else
				error = an_init(ifp);
		} else if (sc->sc_enabled)
			an_stop(ifp, 1);
		break;
	case SIOCADDMULTI:
	case SIOCDELMULTI:
		/* The Aironet has no multicast filter. */
		error = 0;
		break;
	case SIOCS80211NWKEY:
		error = an_set_nwkey(sc, (struct ieee80211_nwkey *)data);
			break;
	case SIOCG80211NWKEY:
		error = an_get_nwkey(sc, (struct ieee80211_nwkey *)data);
		break;
	default:
		error = ieee80211_ioctl(ifp, command, data);
		break;
	}
	if (error == ENETRESET) {
		if (sc->sc_enabled)
			error = an_init(ifp);
		else
			error = 0;
	}
	splx(s);
	return(error);
}

int
an_init(struct ifnet *ifp)
{
	struct an_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	int i, error, fid;

	DPRINTF(("an_init: enabled %d\n", sc->sc_enabled));
	if (!sc->sc_enabled) {
		if (sc->sc_enable)
			(*sc->sc_enable)(sc);
		an_wait(sc);
		sc->sc_enabled = 1;
	} else {
		an_stop(ifp, 0);
		if ((error = an_reset(sc)) != 0) {
			printf("%s: failed to reset\n", ifp->if_xname);
			an_stop(ifp, 1);
			return error;
		}
	}
	CSR_WRITE_2(sc, AN_SW0, AN_MAGIC);

	/* Allocate the TX buffers */
	for (i = 0; i < AN_TX_RING_CNT; i++) {
		if ((error = an_alloc_nicmem(sc, AN_TX_MAX_LEN, &fid)) != 0) {
			printf("%s: failed to allocate nic memory\n",
			    ifp->if_xname);
			an_stop(ifp, 1);
			return error;
		}
		DPRINTF2(("an_init: txbuf %d allocated %x\n", i, fid));
		sc->sc_txd[i].d_fid = fid;
		sc->sc_txd[i].d_inuse = 0;
	}
	sc->sc_txcur = sc->sc_txnext = 0;

	IEEE80211_ADDR_COPY(sc->sc_config.an_macaddr, ic->ic_myaddr);
	an_swap16((u_int16_t *)&sc->sc_config.an_macaddr, 3);
	sc->sc_config.an_scanmode = AN_SCANMODE_ACTIVE;
	sc->sc_config.an_authtype = AN_AUTHTYPE_OPEN;	/*XXX*/
	if (ic->ic_flags & IEEE80211_F_WEPON) {
		sc->sc_config.an_authtype |=
		    AN_AUTHTYPE_PRIVACY_IN_USE;
	}
	sc->sc_config.an_listen_interval = ic->ic_lintval;
	sc->sc_config.an_beacon_period = ic->ic_lintval;
	if (ic->ic_flags & IEEE80211_F_PMGTON)
		sc->sc_config.an_psave_mode = AN_PSAVE_PSP;
	else
		sc->sc_config.an_psave_mode = AN_PSAVE_CAM;
	sc->sc_config.an_ds_channel =
	    ieee80211_chan2ieee(ic, ic->ic_ibss_chan);

	switch (ic->ic_opmode) {
	case IEEE80211_M_STA:
		sc->sc_config.an_opmode =
		    AN_OPMODE_INFRASTRUCTURE_STATION;
		sc->sc_config.an_rxmode = AN_RXMODE_BC_MC_ADDR;
		break;
#ifndef IEEE80211_STA_ONLY
	case IEEE80211_M_IBSS:
		sc->sc_config.an_opmode = AN_OPMODE_IBSS_ADHOC;
		sc->sc_config.an_rxmode = AN_RXMODE_BC_MC_ADDR;
		break;
#endif
	case IEEE80211_M_MONITOR:
		sc->sc_config.an_opmode =
		    AN_OPMODE_INFRASTRUCTURE_STATION;
		sc->sc_config.an_rxmode =
		    AN_RXMODE_80211_MONITOR_ANYBSS;
		sc->sc_config.an_authtype = AN_AUTHTYPE_NONE;
		if (ic->ic_flags & IEEE80211_F_WEPON)
			sc->sc_config.an_authtype |=
			    AN_AUTHTYPE_PRIVACY_IN_USE |
		            AN_AUTHTYPE_ALLOW_UNENCRYPTED;
		break;
	default:
		printf("%s: bad opmode %d\n", ifp->if_xname, ic->ic_opmode);
		an_stop(ifp, 1);
		return EIO;
	}
	sc->sc_config.an_rxmode |= AN_RXMODE_NO_8023_HEADER;

	/* Set the ssid list */
	memset(&sc->sc_buf, 0, sizeof(sc->sc_buf.sc_ssidlist));
	sc->sc_buf.sc_ssidlist.an_entry[0].an_ssid_len =
	    ic->ic_des_esslen;
	if (ic->ic_des_esslen)
		memcpy(sc->sc_buf.sc_ssidlist.an_entry[0].an_ssid,
		    ic->ic_des_essid, ic->ic_des_esslen);
	an_swap16((u_int16_t *)&sc->sc_buf.sc_ssidlist.an_entry[0].an_ssid, 16);
	if ((error = an_write_rid(sc, AN_RID_SSIDLIST, &sc->sc_buf,
	    sizeof(sc->sc_buf.sc_ssidlist)))) {
		printf("%s: failed to write ssid list\n", ifp->if_xname);
		an_stop(ifp, 1);
		return error;
	}

	/* Set the AP list */
	memset(&sc->sc_buf, 0, sizeof(sc->sc_buf.sc_aplist));
	(void)an_write_rid(sc, AN_RID_APLIST, &sc->sc_buf,
	    sizeof(sc->sc_buf.sc_aplist));

	/* Set the encapsulation */
	for (i = 0; i < AN_ENCAP_NENTS; i++) {
		sc->sc_buf.sc_encap.an_entry[i].an_ethertype = 0;
		sc->sc_buf.sc_encap.an_entry[i].an_action =
		    AN_RXENCAP_RFC1024 | AN_TXENCAP_RFC1024;
	}
	(void)an_write_rid(sc, AN_RID_ENCAP, &sc->sc_buf,
	    sizeof(sc->sc_buf.sc_encap));

	/* Set the WEP Keys */
	if (ic->ic_flags & IEEE80211_F_WEPON)
		an_write_wepkey(sc, AN_RID_WEP_VOLATILE, sc->sc_wepkeys,
		    sc->sc_tx_key);

	/* Set the configuration */
	if ((error = an_write_rid(sc, AN_RID_GENCONFIG, &sc->sc_config,
	    sizeof(sc->sc_config)))) {
		printf("%s: failed to write config\n", ifp->if_xname);
		an_stop(ifp, 1);
		return error;
	}

	/* Enable the MAC */
	if (an_cmd(sc, AN_CMD_ENABLE, 0)) {
		printf("%s: failed to enable MAC\n", sc->sc_dev.dv_xname);
		an_stop(ifp, 1);
		return ENXIO;
	}
	if (ifp->if_flags & IFF_PROMISC)
		an_cmd(sc, AN_CMD_SET_MODE, 0xffff);

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;
	ic->ic_state = IEEE80211_S_INIT;
	if (ic->ic_opmode == IEEE80211_M_MONITOR)
		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);

	/* enable interrupts */
	CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS);
	return 0;
}

void
an_start(struct ifnet *ifp)
{
	struct an_softc *sc = (struct an_softc *)ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	struct ieee80211_node *ni;
	struct ieee80211_frame *wh;
	struct an_txframe frmhdr;
	struct mbuf *m;
	u_int16_t len;
	int cur, fid;

	if (!sc->sc_enabled || sc->sc_invalid) {
		DPRINTF(("an_start: noop: enabled %d invalid %d\n",
		    sc->sc_enabled, sc->sc_invalid));
		return;
	}

	memset(&frmhdr, 0, sizeof(frmhdr));
	cur = sc->sc_txnext;
	for (;;) {
		if (ic->ic_state != IEEE80211_S_RUN) {
			DPRINTF(("an_start: not running %d\n", ic->ic_state));
			break;
		}
		IFQ_POLL(&ifp->if_snd, m);
		if (m == NULL) {
			DPRINTF2(("an_start: no pending mbuf\n"));
			break;
		}
		if (sc->sc_txd[cur].d_inuse) {
			DPRINTF2(("an_start: %x/%d busy\n",
			    sc->sc_txd[cur].d_fid, cur));
			ifp->if_flags |= IFF_OACTIVE;
			break;
		}
		IFQ_DEQUEUE(&ifp->if_snd, m);
		ifp->if_opackets++;
#if NBPFILTER > 0
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif
		if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) {
			ifp->if_oerrors++;
			continue;
		}
		if (ni != NULL)
			ieee80211_release_node(ic, ni);
#if NBPFILTER > 0
		if (ic->ic_rawbpf)
			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
#endif

		wh = mtod(m, struct ieee80211_frame *);
		if (ic->ic_flags & IEEE80211_F_WEPON)
			wh->i_fc[1] |= IEEE80211_FC1_WEP;
		m_copydata(m, 0, sizeof(struct ieee80211_frame),
		    (caddr_t)&frmhdr.an_whdr);
		an_swap16((u_int16_t *)&frmhdr.an_whdr, sizeof(struct ieee80211_frame)/2);

		/* insert payload length in front of llc/snap */
		len = htons(m->m_pkthdr.len - sizeof(struct ieee80211_frame));
		m_adj(m, sizeof(struct ieee80211_frame) - sizeof(len));
		if (mtod(m, u_long) & 0x01)
			memcpy(mtod(m, caddr_t), &len, sizeof(len));
		else
			*mtod(m, u_int16_t *) = len;

		/*
		 * XXX Aironet firmware apparently convert the packet
		 * with longer than 1500 bytes in length into LLC/SNAP.
		 * If we have 1500 bytes in ethernet payload, it is
		 * 1508 bytes including LLC/SNAP and will be inserted
		 * additional LLC/SNAP header with 1501-1508 in its
		 * ethertype !!
		 * So we skip LLC/SNAP header and force firmware to
		 * convert it to LLC/SNAP again.
		 */
		m_adj(m, sizeof(struct llc));

		frmhdr.an_tx_ctl = AN_TXCTL_80211;
		frmhdr.an_tx_payload_len = m->m_pkthdr.len;
		frmhdr.an_gaplen = AN_TXGAP_802_11;

		if (ic->ic_fixed_rate != -1)
			frmhdr.an_tx_rate =
			    ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[
			    ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
		else
			frmhdr.an_tx_rate = 0;

		if (sizeof(frmhdr) + AN_TXGAP_802_11 + sizeof(len) +
		    m->m_pkthdr.len > AN_TX_MAX_LEN) {
			ifp->if_oerrors++;
			m_freem(m);
			continue;
		}

#if NBPFILTER > 0
		if (sc->sc_drvbpf) {
			struct mbuf mb;
			struct an_tx_radiotap_header *tap = &sc->sc_txtap;

			tap->at_rate =
			    ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate];
			tap->at_chan_freq =
			    ic->ic_bss->ni_chan->ic_freq;
			tap->at_chan_flags =
			    ic->ic_bss->ni_chan->ic_flags;

			mb.m_data = (caddr_t)tap;
			mb.m_len = sizeof(sc->sc_txtapu);
			mb.m_next = m;
			mb.m_nextpkt = NULL;
			mb.m_type = 0;
			mb.m_flags = 0;
			bpf_mtap(sc->sc_drvbpf, m, BPF_DIRECTION_OUT);
		}
#endif

		fid = sc->sc_txd[cur].d_fid;
		if (an_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0) {
			ifp->if_oerrors++;
			m_freem(m);
			continue;
		}
		/* dummy write to avoid seek. */
		an_write_bap(sc, fid, -1, &frmhdr, AN_TXGAP_802_11);
		an_mwrite_bap(sc, fid, -1, m, m->m_pkthdr.len);
		m_freem(m);

		DPRINTF2(("an_start: send %d byte via %x/%d\n",
		    ntohs(len) + sizeof(struct ieee80211_frame),
		    fid, cur));
		sc->sc_txd[cur].d_inuse = 1;
		if (an_cmd(sc, AN_CMD_TX, fid)) {
			printf("%s: xmit failed\n", ifp->if_xname);
			sc->sc_txd[cur].d_inuse = 0;
			continue;
		}
		sc->sc_tx_timer = 5;
		ifp->if_timer = 1;
		AN_INC(cur, AN_TX_RING_CNT);
		sc->sc_txnext = cur;
	}
}

void
an_stop(struct ifnet *ifp, int disable)
{
	struct an_softc *sc = ifp->if_softc;
	int i, s;

	if (!sc->sc_enabled)
		return;

	DPRINTF(("an_stop: disable %d\n", disable));

	s = splnet();
	ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
	if (!sc->sc_invalid) {
		an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0);
		CSR_WRITE_2(sc, AN_INT_EN, 0);
		an_cmd(sc, AN_CMD_DISABLE, 0);

		for (i = 0; i < AN_TX_RING_CNT; i++)
			an_cmd(sc, AN_CMD_DEALLOC_MEM, sc->sc_txd[i].d_fid);
	}

	sc->sc_tx_timer = 0;
	ifp->if_timer = 0;
	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);

	if (disable) {
		if (sc->sc_disable)
			(*sc->sc_disable)(sc);
		sc->sc_enabled = 0;
	}
	splx(s);
}

void
an_watchdog(struct ifnet *ifp)
{
	struct an_softc *sc = ifp->if_softc;

	if (!sc->sc_enabled)
		return;

	if (sc->sc_tx_timer) {
		if (--sc->sc_tx_timer == 0) {
			printf("%s: device timeout\n", ifp->if_xname);
			ifp->if_oerrors++;
			an_init(ifp);
			return;
		}
		ifp->if_timer = 1;
	}
	ieee80211_watchdog(ifp);
}

/* TBD factor with ieee80211_media_change */
int
an_media_change(struct ifnet *ifp)
{
	struct an_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifmedia_entry *ime;
	enum ieee80211_opmode newmode;
	int i, rate, error = 0;

	ime = ic->ic_media.ifm_cur;
	if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) {
		i = -1;
	} else {
		struct ieee80211_rateset *rs =
		    &ic->ic_sup_rates[IEEE80211_MODE_11B];
		rate = ieee80211_media2rate(ime->ifm_media);
		if (rate == 0)
			return EINVAL;
		for (i = 0; i < rs->rs_nrates; i++) {
			if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate)
				break;
		}
		if (i == rs->rs_nrates)
			return EINVAL;
	}
	if (ic->ic_fixed_rate != i) {
		ic->ic_fixed_rate = i;
		error = ENETRESET;
	}

#ifndef IEEE80211_STA_ONLY
	if (ime->ifm_media & IFM_IEEE80211_ADHOC)
		newmode = IEEE80211_M_IBSS;
	else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
		newmode = IEEE80211_M_HOSTAP;
	else
#endif
	if (ime->ifm_media & IFM_IEEE80211_MONITOR)
		newmode = IEEE80211_M_MONITOR;
	else
		newmode = IEEE80211_M_STA;
	if (ic->ic_opmode != newmode) {
		ic->ic_opmode = newmode;
		error = ENETRESET;
	}
	if (error == ENETRESET) {
		if (sc->sc_enabled)
			error = an_init(ifp);
		else
			error = 0;
	}
	ifp->if_baudrate = ifmedia_baudrate(ic->ic_media.ifm_cur->ifm_media);

	return error;
}

void
an_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
	struct an_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	int rate, buflen;

	if (sc->sc_enabled == 0) {
		imr->ifm_active = IFM_IEEE80211 | IFM_NONE;
		imr->ifm_status = 0;
		return;
	}

	imr->ifm_status = IFM_AVALID;
	imr->ifm_active = IFM_IEEE80211;
	if (ic->ic_state == IEEE80211_S_RUN)
		imr->ifm_status |= IFM_ACTIVE;
	buflen = sizeof(sc->sc_buf);
	if (ic->ic_fixed_rate != -1)
		rate = ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[
		    ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
	else if (an_read_rid(sc, AN_RID_STATUS, &sc->sc_buf, &buflen) != 0)
		rate = 0;
	else
		rate = sc->sc_buf.sc_status.an_current_tx_rate;
	imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
	switch (ic->ic_opmode) {
	case IEEE80211_M_STA:
		break;
#ifndef IEEE80211_STA_ONLY
	case IEEE80211_M_IBSS:
		imr->ifm_active |= IFM_IEEE80211_ADHOC;
		break;
	case IEEE80211_M_HOSTAP:
		imr->ifm_active |= IFM_IEEE80211_HOSTAP;
		break;
#endif
	case IEEE80211_M_MONITOR:
		imr->ifm_active |= IFM_IEEE80211_MONITOR;
		break;
	default:
		break;
	}
}

int
an_set_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey)
{
	int error;
	struct ieee80211com *ic = &sc->sc_ic;
	u_int16_t prevauth;

	error = 0;
	prevauth = sc->sc_config.an_authtype;

	switch (nwkey->i_wepon) {
	case IEEE80211_NWKEY_OPEN:
		sc->sc_config.an_authtype = AN_AUTHTYPE_OPEN;
		ic->ic_flags &= ~IEEE80211_F_WEPON;
		break;

	case IEEE80211_NWKEY_WEP:
	case IEEE80211_NWKEY_WEP | IEEE80211_NWKEY_PERSIST:
		error = an_set_nwkey_wep(sc, nwkey);
		if (error == 0 || error == ENETRESET) {
			sc->sc_config.an_authtype =
			    AN_AUTHTYPE_OPEN | AN_AUTHTYPE_PRIVACY_IN_USE;
			ic->ic_flags |= IEEE80211_F_WEPON;
		}
		break;

	default:
		error = EINVAL;
		break;
	}
	if (error == 0 && prevauth != sc->sc_config.an_authtype)
		error = ENETRESET;
	return error;
}

int
an_set_nwkey_wep(struct an_softc *sc, struct ieee80211_nwkey *nwkey)
{
	int i, txkey, anysetkey, needreset, error;
	struct an_wepkey keys[IEEE80211_WEP_NKID];

	error = 0;
	memset(keys, 0, sizeof(keys));
	anysetkey = needreset = 0;

	/* load argument and sanity check */
	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
		keys[i].an_wep_keylen = nwkey->i_key[i].i_keylen;
		if (keys[i].an_wep_keylen < 0)
			continue;
		if (keys[i].an_wep_keylen != 0 &&
		    keys[i].an_wep_keylen < IEEE80211_WEP_KEYLEN)
			return EINVAL;
		if (keys[i].an_wep_keylen > sizeof(keys[i].an_wep_key))
			return EINVAL;
		if ((error = copyin(nwkey->i_key[i].i_keydat,
		    keys[i].an_wep_key, keys[i].an_wep_keylen)) != 0)
			return error;
		anysetkey++;
	}
	txkey = nwkey->i_defkid - 1;
	if (txkey >= 0) {
		if (txkey >= IEEE80211_WEP_NKID)
			return EINVAL;
		/* default key must have a valid value */
		if (keys[txkey].an_wep_keylen == 0 ||
		    (keys[txkey].an_wep_keylen < 0 &&
		    sc->sc_perskeylen[txkey] == 0))
			return EINVAL;
		anysetkey++;
	}
	DPRINTF(("an_set_nwkey_wep: %s: %sold(%d:%d,%d,%d,%d) "
	    "pers(%d:%d,%d,%d,%d) new(%d:%d,%d,%d,%d)\n",
	    sc->sc_dev.dv_xname,
	    ((nwkey->i_wepon & IEEE80211_NWKEY_PERSIST) ? "persist: " : ""),
	    sc->sc_tx_key,
	    sc->sc_wepkeys[0].an_wep_keylen, sc->sc_wepkeys[1].an_wep_keylen,
	    sc->sc_wepkeys[2].an_wep_keylen, sc->sc_wepkeys[3].an_wep_keylen,
	    sc->sc_tx_perskey,
	    sc->sc_perskeylen[0], sc->sc_perskeylen[1],
	    sc->sc_perskeylen[2], sc->sc_perskeylen[3],
	    txkey,
	    keys[0].an_wep_keylen, keys[1].an_wep_keylen,
	    keys[2].an_wep_keylen, keys[3].an_wep_keylen));
	if (!(nwkey->i_wepon & IEEE80211_NWKEY_PERSIST)) {
		/* set temporary keys */
		sc->sc_tx_key = txkey;
		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
			if (keys[i].an_wep_keylen < 0)
				continue;
			memcpy(&sc->sc_wepkeys[i], &keys[i], sizeof(keys[i]));
		}
	} else {
		/* set persist keys */
		if (anysetkey) {
			/* prepare to write nvram */
			if (!sc->sc_enabled) {
				if (sc->sc_enable)
					(*sc->sc_enable)(sc);
				an_wait(sc);
				sc->sc_enabled = 1;
				error = an_write_wepkey(sc,
				    AN_RID_WEP_PERSISTENT, keys, txkey);
				if (sc->sc_disable)
					(*sc->sc_disable)(sc);
				sc->sc_enabled = 0;
			} else {
				an_cmd(sc, AN_CMD_DISABLE, 0);
				error = an_write_wepkey(sc,
				    AN_RID_WEP_PERSISTENT, keys, txkey);
				an_cmd(sc, AN_CMD_ENABLE, 0);
			}
			if (error)
				return error;
		}
		if (txkey >= 0)
			sc->sc_tx_perskey = txkey;
		if (sc->sc_tx_key >= 0) {
			sc->sc_tx_key = -1;
			needreset++;
		}
		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
			if (sc->sc_wepkeys[i].an_wep_keylen >= 0) {
				memset(&sc->sc_wepkeys[i].an_wep_key, 0,
				    sizeof(sc->sc_wepkeys[i].an_wep_key));
				sc->sc_wepkeys[i].an_wep_keylen = -1;
				needreset++;
			}
			if (keys[i].an_wep_keylen >= 0)
				sc->sc_perskeylen[i] = keys[i].an_wep_keylen;
		}
	}
	if (needreset) {
		/* firmware restart to reload persistent key */
		an_reset(sc);
	}
	if (anysetkey || needreset)
		error = ENETRESET;
	return error;
}

int
an_get_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey)
{
	int i, error;

	error = 0;
	if (sc->sc_config.an_authtype & AN_AUTHTYPE_LEAP)
		nwkey->i_wepon = IEEE80211_NWKEY_EAP;
	else if (sc->sc_config.an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE)
		nwkey->i_wepon = IEEE80211_NWKEY_WEP;
	else
		nwkey->i_wepon = IEEE80211_NWKEY_OPEN;
	if (sc->sc_tx_key == -1)
		nwkey->i_defkid = sc->sc_tx_perskey + 1;
	else
		nwkey->i_defkid = sc->sc_tx_key + 1;
	if (nwkey->i_key[0].i_keydat == NULL)
		return 0;
	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
		if (nwkey->i_key[i].i_keydat == NULL)
			continue;
		/* do not show any keys to non-root user */
		if ((error = suser(curproc, 0)) != 0)
			break;
		nwkey->i_key[i].i_keylen = sc->sc_wepkeys[i].an_wep_keylen;
		if (nwkey->i_key[i].i_keylen < 0) {
			if (sc->sc_perskeylen[i] == 0)
				nwkey->i_key[i].i_keylen = 0;
			continue;
		}
		if ((error = copyout(sc->sc_wepkeys[i].an_wep_key,
		    nwkey->i_key[i].i_keydat,
		    sc->sc_wepkeys[i].an_wep_keylen)) != 0)
			break;
	}
	return error;
}

int
an_write_wepkey(struct an_softc *sc, int type, struct an_wepkey *keys, int kid)
{
	int i, error;
	struct an_rid_wepkey *akey;

	error = 0;
	akey = &sc->sc_buf.sc_wepkey;
	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
		memset(akey, 0, sizeof(struct an_rid_wepkey));
		if (keys[i].an_wep_keylen < 0 ||
		    keys[i].an_wep_keylen > sizeof(akey->an_key))
			continue;
		akey->an_key_len = keys[i].an_wep_keylen;
		akey->an_key_index = i;
		akey->an_mac_addr[0] = 1;	/* default mac */
		an_swap16((u_int16_t *)&akey->an_mac_addr, 3);
		memcpy(akey->an_key, keys[i].an_wep_key, keys[i].an_wep_keylen);
		an_swap16((u_int16_t *)&akey->an_key, 8);
		if ((error = an_write_rid(sc, type, akey, sizeof(*akey))) != 0)
			return error;
	}
	if (kid >= 0) {
		memset(akey, 0, sizeof(struct an_rid_wepkey));
		akey->an_key_index = 0xffff;
		akey->an_mac_addr[0] = kid;
		an_swap16((u_int16_t *)&akey->an_mac_addr, 3);
		akey->an_key_len = 0;
		memset(akey->an_key, 0, sizeof(akey->an_key));
		error = an_write_rid(sc, type, akey, sizeof(*akey));
	}
	return error;
}

int
an_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
	struct an_softc *sc = ic->ic_softc;
	struct ieee80211_node *ni = ic->ic_bss;
	enum ieee80211_state ostate;
	int buflen;

	ostate = ic->ic_state;
	DPRINTF(("an_newstate: %s -> %s\n", ieee80211_state_name[ostate],
	    ieee80211_state_name[nstate]));

	switch (nstate) {
	case IEEE80211_S_INIT:
		ic->ic_flags &= ~IEEE80211_F_IBSSON;
		return (*sc->sc_newstate)(ic, nstate, arg);

	case IEEE80211_S_RUN:
		buflen = sizeof(sc->sc_buf);
		an_read_rid(sc, AN_RID_STATUS, &sc->sc_buf, &buflen);
		an_swap16((u_int16_t *)&sc->sc_buf.sc_status.an_cur_bssid, 3);
		an_swap16((u_int16_t *)&sc->sc_buf.sc_status.an_ssid, 16);
		IEEE80211_ADDR_COPY(ni->ni_bssid,
		    sc->sc_buf.sc_status.an_cur_bssid);
		IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
		ni->ni_chan = &ic->ic_channels[
		    sc->sc_buf.sc_status.an_cur_channel];
		ni->ni_esslen = sc->sc_buf.sc_status.an_ssidlen;
		if (ni->ni_esslen > IEEE80211_NWID_LEN)
			ni->ni_esslen = IEEE80211_NWID_LEN;	/*XXX*/
		memcpy(ni->ni_essid, sc->sc_buf.sc_status.an_ssid,
		    ni->ni_esslen);
		ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];	/*XXX*/
		if (ic->ic_if.if_flags & IFF_DEBUG) {
			printf("%s: ", sc->sc_dev.dv_xname);
			if (ic->ic_opmode == IEEE80211_M_STA)
				printf("associated ");
			else
				printf("synchronized ");
			printf("with %s ssid ", ether_sprintf(ni->ni_bssid));
			ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
			printf(" channel %u start %uMb\n",
			    sc->sc_buf.sc_status.an_cur_channel,
			    sc->sc_buf.sc_status.an_current_tx_rate/2);
		}
		break;

	default:
		break;
	}
	ic->ic_state = nstate;
	/* skip standard ieee80211 handling */
	return 0;
}

int
an_detach(struct an_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	int s;

	if (!sc->sc_attached)
		return 0;

	s = splnet();
	sc->sc_invalid = 1;
	an_stop(ifp, 1);
	ifmedia_delete_instance(&sc->sc_ic.ic_media, IFM_INST_ANY);
	ieee80211_ifdetach(ifp);
	if_detach(ifp);
	splx(s);
	return 0;
}


code:
/*	$OpenBSD: anreg.h,v 1.12 2007/10/05 23:23:55 jsg Exp $	*/
/*	$NetBSD: anreg.h,v 1.11 2005/01/15 11:01:46 dyoung Exp $	*/
/*
 * Copyright (c) 1997, 1998, 1999
 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/dev/an/if_anreg.h,v 1.3 2000/11/13 23:04:12 wpaul Exp $
 */

#ifndef _DEV_IC_ANREG_H
#define	_DEV_IC_ANREG_H

/*
 * Size of Aironet I/O space.
 */
#define AN_IOSIZ		0x40

/*
 * Hermes register definitions and what little I know about them.
 */

/* Hermes command/status registers. */
#define AN_COMMAND		0x00
#define AN_PARAM0		0x02
#define AN_PARAM1		0x04
#define AN_PARAM2		0x06
#define AN_STATUS		0x08
#define AN_RESP0		0x0A
#define AN_RESP1		0x0C
#define AN_RESP2		0x0E
#define AN_LINKSTAT		0x10

/* Command register */
#define AN_CMD_BUSY		0x8000 /* busy bit */
#define AN_CMD_NO_ACK		0x0080 /* don't acknowledge command */
#define AN_CMD_CODE_MASK	0x003F
#define AN_CMD_QUAL_MASK	0x7F00

/* Command codes */
#define AN_CMD_NOOP		0x0000 /* no-op */
#define AN_CMD_ENABLE		0x0001 /* enable */
#define AN_CMD_DISABLE		0x0002 /* disable */
#define AN_CMD_FORCE_SYNCLOSS	0x0003 /* force loss of sync */
#define AN_CMD_FW_RESTART	0x0004 /* firmware resrart */
#define AN_CMD_HOST_SLEEP	0x0005
#define AN_CMD_MAGIC_PKT	0x0006
#define AN_CMD_READCFG		0x0008
#define AN_CMD_SET_MODE		0x0009
#define AN_CMD_ALLOC_MEM	0x000A /* allocate NIC memory */
#define AN_CMD_TX		0x000B /* transmit */
#define AN_CMD_DEALLOC_MEM	0x000C
#define AN_CMD_NOOP2		0x0010
#define AN_CMD_ACCESS		0x0021
#define AN_CMD_ALLOC_BUF	0x0028
#define AN_CMD_PSP_NODES	0x0030
#define AN_CMD_SET_PHYREG	0x003E
#define AN_CMD_TX_TEST		0x003F
#define AN_CMD_SLEEP		0x0085
#define AN_CMD_SAVECFG		0x0108

/*
 * Reclaim qualifier bit, applicable to the
 * TX command.
 */
#define AN_RECLAIM		0x0100 /* reclaim NIC memory */

/*
 * ACCESS command qualifier bits.
 */
#define AN_ACCESS_READ		0x0000
#define AN_ACCESS_WRITE		0x0100

/*
 * PROGRAM command qualifier bits.
 */
#define AN_PROGRAM_DISABLE	0x0000
#define AN_PROGRAM_ENABLE_RAM	0x0100
#define AN_PROGRAM_ENABLE_NVRAM	0x0200
#define AN_PROGRAM_NVRAM	0x0300

/* Status register values */
#define AN_STAT_CMD_CODE	0x003F
#define AN_STAT_CMD_RESULT	0x7F00

/* Linkstat register */
#define AN_LINKSTAT_ASSOCIATED		0x0400
#define AN_LINKSTAT_AUTHFAIL		0x0300
#define AN_LINKSTAT_ASSOC_FAIL		0x8400
#define AN_LINKSTAT_DISASSOC		0x8200
#define AN_LINKSTAT_DEAUTH		0x8100
#define AN_LINKSTAT_SYNCLOST_TSF	0x8004
#define AN_LINKSTAT_SYNCLOST_HOSTREQ	0x8003
#define AN_LINKSTAT_SYNCLOST_AVGRETRY	0x8002
#define AN_LINKSTAT_SYNCLOST_MAXRETRY	0x8001
#define AN_LINKSTAT_SYNCLOST_MISSBEACON	0x8000

/* memory handle management registers */
#define AN_RX_FID		0x20
#define AN_ALLOC_FID		0x22
#define AN_TX_CMP_FID		0x24

/*
 * Buffer Access Path (BAP) registers.
 * These are I/O channels. I believe you can use each one for
 * any desired purpose independently of the other. In general
 * though, we use BAP1 for reading and writing LTV records and
 * reading received data frames, and BAP0 for writing transmit
 * frames. This is a convention though, not a rule.
 */
#define AN_SEL0			0x18
#define AN_SEL1			0x1A
#define AN_OFF0			0x1C
#define AN_OFF1			0x1E
#define AN_DATA0		0x36
#define AN_DATA1		0x38
#define AN_BAP0			AN_DATA0
#define AN_BAP1			AN_DATA1

#define AN_OFF_BUSY		0x8000
#define AN_OFF_ERR		0x4000
#define AN_OFF_DONE		0x2000
#define AN_OFF_DATAOFF		0x0FFF

/* Event registers */
#define AN_EVENT_STAT		0x30	/* Event status */
#define AN_INT_EN		0x32	/* Interrupt enable/disable */
#define AN_EVENT_ACK		0x34	/* Ack event */

/* Events */
#define AN_EV_CLR_STUCK_BUSY	0x4000	/* clear stuck busy bit */
#define AN_EV_WAKEREQUEST	0x2000	/* awaken from PSP mode */
#define AN_EV_MIC		0x1000	/* Message Integrity Check*/
#define AN_EV_TX_CPY		0x0400
#define AN_EV_AWAKE		0x0100	/* station woke up from PSP mode*/
#define AN_EV_LINKSTAT		0x0080	/* link status available */
#define AN_EV_CMD		0x0010	/* command completed */
#define AN_EV_ALLOC		0x0008	/* async alloc/reclaim completed */
#define AN_EV_TX_EXC		0x0004	/* async xmit completed with failure */
#define AN_EV_TX		0x0002	/* async xmit completed successfully */
#define AN_EV_RX		0x0001	/* async rx completed */

/* Host software registers */
#define AN_SW0			0x28
#define AN_SW1			0x2A
#define AN_SW2			0x2C
#define AN_SW3			0x2E

#define AN_CNTL			0x14

#define AN_CNTL_AUX_ENA		0xC000
#define AN_CNTL_AUX_ENA_STAT	0xC000
#define AN_CNTL_AUX_DIS_STAT	0x0000
#define AN_CNTL_AUX_ENA_CNTL	0x8000
#define AN_CNTL_AUX_DIS_CNTL	0x4000

#define AN_AUX_PAGE		0x3A
#define AN_AUX_OFFSET		0x3C
#define AN_AUX_DATA		0x3E

/*
 * General configuration information.
 */
#define AN_RID_GENCONFIG	0xFF10
struct an_rid_genconfig {
	/* General configuration. */
	u_int16_t		an_opmode;		/* 0x02 */
	u_int16_t		an_rxmode;		/* 0x04 */
	u_int16_t		an_fragthresh;		/* 0x06 */
	u_int16_t		an_rtsthresh;		/* 0x08 */
	u_int8_t		an_macaddr[6];		/* 0x0A */
	u_int8_t		an_rates[8];		/* 0x10 */
	u_int16_t		an_shortretry_limit;	/* 0x18 */
	u_int16_t		an_longretry_limit;	/* 0x1A */
	u_int16_t		an_tx_msdu_lifetime;	/* 0x1C */
	u_int16_t		an_rx_msdu_lifetime;	/* 0x1E */
	u_int16_t		an_stationary;		/* 0x20 */
	u_int16_t		an_ordering;		/* 0x22 */
	u_int16_t		an_devtype;		/* 0x24 */
	u_int16_t		an_rsvd0[5];		/* 0x26 */
	/* Scanning associating. */
	u_int16_t		an_scanmode;		/* 0x30 */
	u_int16_t		an_probedelay;		/* 0x32 */
	u_int16_t		an_probe_energy_timeout;/* 0x34 */
	u_int16_t		an_probe_response_timeout;/*0x36 */
	u_int16_t		an_beacon_listen_timeout;/*0x38 */
	u_int16_t		an_ibss_join_net_timeout;/*0x3A */
	u_int16_t		an_auth_timeout;	/* 0x3C */
	u_int16_t		an_authtype;		/* 0x3E */
	u_int16_t		an_assoc_timeout;	/* 0x40 */
	u_int16_t		an_specified_ap_timeout;/* 0x42 */
	u_int16_t		an_offline_scan_interval;/*0x44 */
	u_int16_t		an_offline_scan_duration;/*0x46 */
	u_int16_t		an_link_loss_delay;	/* 0x48 */
	u_int16_t		an_max_beacon_lost_time;/* 0x4A */
	u_int16_t		an_refresh_interval;	/* 0x4C */
	u_int16_t		an_rsvd1;		/* 0x4E */
	/* Power save operation */
	u_int16_t		an_psave_mode;		/* 0x50 */
	u_int16_t		an_sleep_for_dtims;	/* 0x52 */
	u_int16_t		an_listen_interval;	/* 0x54 */
	u_int16_t		an_fast_listen_interval;/* 0x56 */
	u_int16_t		an_listen_decay;	/* 0x58 */
	u_int16_t		an_fast_listen_decay;	/* 0x5A */
	u_int16_t		an_rsvd2[2];		/* 0x5C */
	/* Ad-hoc (or AP) operation. */
	u_int16_t		an_beacon_period;	/* 0x60 */
	u_int16_t		an_atim_duration;	/* 0x62 */
	u_int16_t		an_rsvd3;		/* 0x64 */
	u_int16_t		an_ds_channel;		/* 0x66 */
	u_int16_t		an_rsvd4;		/* 0x68 */
	u_int16_t		an_dtim_period;		/* 0x6A */
	u_int16_t		an_rsvd5[2];		/* 0x6C */
	/* Radio operation. */
	u_int16_t		an_radiotype;		/* 0x70 */
	u_int16_t		an_diversity;		/* 0x72 */
	u_int16_t		an_tx_power;		/* 0x74 */
	u_int16_t		an_rss_thresh;		/* 0x76 */
	u_int16_t		an_modulation_type;	/* 0x78 */
	u_int16_t		an_short_preamble;	/* 0x7A */
	u_int16_t		an_home_product;	/* 0x7C */
	u_int16_t		an_rsvd6;		/* 0x7E */
	/* Aironet extensions. */
	u_int8_t		an_nodename[16];	/* 0x80 */
	u_int16_t		an_arl_thresh;		/* 0x90 */
	u_int16_t		an_arl_decay;		/* 0x92 */
	u_int16_t		an_arl_delay;		/* 0x94 */
	u_int8_t		an_rsvd7;		/* 0x96 */
	u_int8_t		an_rsvd8;		/* 0x97 */
	u_int8_t		an_magic_packet_action;	/* 0x98 */
	u_int8_t		an_magic_packet_ctl;	/* 0x99 */
	u_int16_t		an_rsvd9;
	u_int16_t               an_spare[24];
} __packed;

#define AN_OPMODE_IBSS_ADHOC			0x0000
#define AN_OPMODE_INFRASTRUCTURE_STATION	0x0001
#define AN_OPMODE_AP				0x0002
#define AN_OPMODE_AP_REPEATER			0x0003
#define AN_OPMODE_UNMODIFIED_PAYLOAD		0x0100
#define AN_OPMODE_AIRONET_EXTENSIONS		0x0200
#define AN_OPMODE_AP_EXTENSIONS			0x0400
#define	AN_OPMODE_ANTENNA_ALIGN			0x0800
#define	AN_OPMODE_ETHER_LLC			0x1000
#define	AN_OPMODE_LEAF_NODE			0x2000
#define	AN_OPMODE_CF_POLLABLE			0x4000
#define	AN_OPMODE_MIC				0x8000

#define AN_RXMODE_BC_MC_ADDR			0x0000
#define AN_RXMODE_BC_ADDR			0x0001
#define AN_RXMODE_ADDR				0x0002
#define AN_RXMODE_80211_MONITOR_CURBSS		0x0003
#define AN_RXMODE_80211_MONITOR_ANYBSS		0x0004
#define AN_RXMODE_LAN_MONITOR_CURBSS		0x0005
#define AN_RXMODE_NO_8023_HEADER		0x0100
#define AN_RXMODE_NORMALIZED_RSSI		0x0200

#define AN_RATE_1MBPS				0x0002
#define AN_RATE_2MBPS				0x0004
#define AN_RATE_5_5MBPS				0x000B
#define AN_RATE_11MBPS				0x0016

#define AN_DEVTYPE_PC4500			0x0065
#define AN_DEVTYPE_PC4800			0x006D

#define AN_SCANMODE_ACTIVE			0x0000
#define AN_SCANMODE_PASSIVE			0x0001
#define AN_SCANMODE_AIRONET_ACTIVE		0x0002

#define AN_AUTHTYPE_NONE			0x0000
#define AN_AUTHTYPE_OPEN			0x0001
#define AN_AUTHTYPE_SHAREDKEY			0x0002
#define AN_AUTHTYPE_MASK			0x00ff
#define AN_AUTHTYPE_PRIVACY_IN_USE		0x0100
#define AN_AUTHTYPE_ALLOW_UNENCRYPTED		0x0200
#define AN_AUTHTYPE_LEAP			0x1000

#define AN_PSAVE_CAM				0x0000
#define AN_PSAVE_PSP				0x0001
#define AN_PSAVE_PSP_CAM			0x0002

#define AN_RADIOTYPE_80211_FH			0x0001
#define AN_RADIOTYPE_80211_DS			0x0002
#define AN_RADIOTYPE_LM2000_DS			0x0004

#define AN_DIVERSITY_FACTORY_DEFAULT		0x0000
#define AN_DIVERSITY_ANTENNA_1_ONLY		0x0001
#define AN_DIVERSITY_ANTENNA_2_ONLY		0x0002
#define AN_DIVERSITY_ANTENNA_1_AND_2		0x0003

#define AN_TXPOWER_FACTORY_DEFAULT		0x0000
#define AN_TXPOWER_50MW				50
#define AN_TXPOWER_100MW			100
#define AN_TXPOWER_250MW			250

/*
 * Valid SSID list. You can specify up to three SSIDs denoting
 * the service sets that you want to join. The first SSID always
 * defaults to "tsunami" which is a handy way to detect the
 * card.
 */
#define AN_RID_SSIDLIST		0xFF11
struct an_rid_ssidlist {
	struct an_rid_ssid_entry {
		u_int16_t	an_ssid_len;
		char		an_ssid[32];
	} __packed an_entry[3];	/* 25 for fwver.5 */
} __packed;

/*
 * Valid AP list.
 */
#define AN_RID_APLIST		0xFF12
struct an_rid_aplist {
	u_int8_t		an_ap1[8];
	u_int8_t		an_ap2[8];
	u_int8_t		an_ap3[8];
	u_int8_t		an_ap4[8];
} __packed;

/*
 * Driver name.
 */
#define AN_RID_DRVNAME		0xFF13
struct an_rid_drvname {
	u_int8_t		an_drvname[16];
} __packed;

/*
 * Frame encapsulation.
 */
#define AN_RID_ENCAP		0xFF14
#define	AN_ENCAP_NENTS		8
struct an_rid_encap {
	struct an_rid_encap_entry {
		u_int16_t	an_ethertype;
		u_int16_t	an_action;
	} __packed an_entry[AN_ENCAP_NENTS];
} __packed;

#define AN_ENCAP_ACTION_RX	0x0001
#define AN_ENCAP_ACTION_TX	0x0002

#define AN_RXENCAP_NONE		0x0000
#define AN_RXENCAP_RFC1024	0x0001

#define AN_TXENCAP_RFC1024	0x0000
#define AN_TXENCAP_80211	0x0002

/*
 * Actual config, same structure as general config (read only).
 */
#define AN_RID_ACTUALCFG	0xFF20

/*
 * Card capabilities (read only).
 */
#define AN_RID_CAPABILITIES	0xFF00
struct an_rid_caps {
	u_int8_t		an_oui[3];		/* 0x02 */
	u_int8_t		an_rsvd0;		/* 0x05 */
	u_int16_t		an_prodnum;		/* 0x06 */
	u_int8_t		an_manufname[32];	/* 0x08 */
	u_int8_t		an_prodname[16];	/* 0x28 */
	u_int8_t		an_prodvers[8];		/* 0x38 */
	u_int8_t		an_oemaddr[6];		/* 0x40 */
	u_int8_t		an_aironetaddr[6];	/* 0x46 */
	u_int16_t		an_radiotype;		/* 0x4C */
	u_int16_t		an_regdomain;		/* 0x4E */
	u_int8_t		an_callid[6];		/* 0x50 */
	u_int8_t		an_rates[8];		/* 0x56 */
	u_int8_t		an_rx_diversity;	/* 0x5E */
	u_int8_t		an_tx_diversity;	/* 0x5F */
	u_int16_t		an_tx_powerlevels[8];	/* 0x60 */
	u_int16_t		an_hwrev;		/* 0x70 */
	u_int16_t		an_hwcaps;		/* 0x72 */
	u_int16_t		an_temprange;		/* 0x74 */
	u_int16_t		an_fwrev;		/* 0x76 */
	u_int16_t		an_fwsubrev;		/* 0x78 */
	u_int16_t		an_ifacerev;		/* 0x7A */
	u_int16_t		an_softcaps;		/* 0x7C */
	u_int16_t		an_bootblockrev;	/* 0x7E */
	u_int16_t		an_req_hw_support;	/* 0x80 */
	/* extended capabilities */
	u_int16_t		an_ext_softcaps;	/* 0x82 */
	u_int16_t		an_spare[34];
} __packed;

#define	AN_REGDOMAIN_USA	0
#define	AN_REGDOMAIN_EUROPE	1
#define	AN_REGDOMAIN_JAPAN	2
#define	AN_REGDOMAIN_SPAIN	3
#define	AN_REGDOMAIN_FRANCE	4
#define	AN_REGDOMAIN_BELGIUM	5
#define	AN_REGDOMAIN_ISRAEL	6
#define	AN_REGDOMAIN_CANADA	7
#define	AN_REGDOMAIN_AUSTRALIA	8
#define	AN_REGDOMAIN_JAPANWIDE	9

#define	AN_SOFTCAPS_WEP		0x0002
#define	AN_SOFTCAPS_RSSIMAP	0x0008
#define	AN_SOFTCAPS_WEP128	0x0100

#define	AN_EXT_SOFTCAPS_MIC	0x0001

/*
 * Access point (read only)
 */
#define AN_RID_APINFO		0xFF01
struct an_rid_apinfo {
	u_int16_t		an_tim_addr;
	u_int16_t		an_airo_addr;
} __packed;

/*
 * Radio info (read only).
 */
#define AN_RID_RADIOINFO	0xFF02

/*
 * Status (read only). Note: the manual claims this RID is 108 bytes
 * long (0x6A is the last datum, which is 2 bytes long) however when
 * this RID is read from the NIC, it returns a length of 110 or 112.
 * To be on the safe side, this structure is padded with 4 extra 16-bit
 * words. (There is a misprint in the manual which says the macaddr
 * field is 8 bytes long.)
 *
 * Also, the channel_set and current_channel fields appear to be
 * reversed. Either that, or the hop_period field is unused.
 */
#define AN_RID_STATUS		0xFF50
struct an_rid_status {
	u_int8_t		an_macaddr[6];		/* 0x02 */
	u_int16_t		an_opmode;		/* 0x08 */
	u_int16_t		an_errcode;		/* 0x0A */
	u_int16_t		an_cur_signal_strength;	/* 0x0C */
	u_int16_t		an_ssidlen;		/* 0x0E */
	u_int8_t		an_ssid[32];		/* 0x10 */
	u_int8_t		an_ap_name[16];		/* 0x30 */
	u_int8_t		an_cur_bssid[6];	/* 0x40 */
	u_int8_t		an_prev_bssid1[6];	/* 0x46 */
	u_int8_t		an_prev_bssid2[6];	/* 0x4C */
	u_int8_t		an_prev_bssid3[6];	/* 0x52 */
	u_int16_t		an_beacon_period;	/* 0x58 */
	u_int16_t		an_dtim_period;		/* 0x5A */
	u_int16_t		an_atim_duration;	/* 0x5C */
	u_int16_t		an_hop_period;		/* 0x5E */
	u_int16_t		an_cur_channel;		/* 0x62 */
	u_int16_t		an_channel_set;		/* 0x60 */
	u_int16_t		an_hops_to_backbone;	/* 0x64 */
	u_int16_t		an_ap_total_load;	/* 0x66 */
	u_int16_t		an_our_generated_load;	/* 0x68 */
	u_int16_t		an_accumulated_arl;	/* 0x6A */
	u_int16_t		an_cur_signal_quality;	/* 0x6C */
	u_int16_t		an_current_tx_rate;	/* 0x6E */
	u_int16_t		an_ap_device;		/* 0x70 */
	u_int16_t		an_normalized_rssi;	/* 0x72 */
	u_int16_t		an_short_pre_in_use;	/* 0x74 */
	u_int8_t		an_ap_ip_addr[4];	/* 0x76 */
	u_int16_t		an_max_noise_prev_sec;	/* 0x7A */
	u_int16_t		an_avg_noise_prev_min;	/* 0x7C */
	u_int16_t		an_max_noise_prev_min;	/* 0x7E */
	u_int16_t		an_spare[11];
} __packed;

#define AN_STATUS_OPMODE_CONFIGURED		0x0001
#define AN_STATUS_OPMODE_MAC_ENABLED		0x0002
#define AN_STATUS_OPMODE_RX_ENABLED		0x0004
#define AN_STATUS_OPMODE_IN_SYNC		0x0010
#define AN_STATUS_OPMODE_ASSOCIATED		0x0020
#define AN_STATUS_OPMODE_ERROR			0x8000

/*
 * Statistics
 */
#define AN_RID_16BITS_CUM	0xFF60	/* Cumulative 16-bit stats counters */
#define AN_RID_16BITS_DELTA	0xFF61	/* 16-bit stats (since last clear) */
#define AN_RID_16BITS_DELTACLR	0xFF62	/* 16-bit stats, clear on read */
#define AN_RID_32BITS_CUM	0xFF68	/* Cumulative 32-bit stats counters */
#define AN_RID_32BITS_DELTA	0xFF69	/* 32-bit stats (since last clear) */
#define AN_RID_32BITS_DELTACLR	0xFF6A	/* 32-bit stats, clear on read */

/*
 * Grrr. The manual says the statistics record is 384 bytes in length,
 * but the card says the record is 404 bytes. There's some padding left
 * at the end of this structure to account for any discrepancies.
 */
struct an_rid_stats {
	u_int16_t		an_spacer;		/* 0x02 */
	u_int32_t		an_rx_overruns;		/* 0x04 */
	u_int32_t		an_rx_plcp_csum_errs;	/* 0x08 */
	u_int32_t		an_rx_plcp_format_errs;	/* 0x0C */
	u_int32_t		an_rx_plcp_len_errs;	/* 0x10 */
	u_int32_t		an_rx_mac_crc_errs;	/* 0x14 */
	u_int32_t		an_rx_mac_crc_ok;	/* 0x18 */
	u_int32_t		an_rx_wep_errs;		/* 0x1C */
	u_int32_t		an_rx_wep_ok;		/* 0x20 */
	u_int32_t		an_retry_long;		/* 0x24 */
	u_int32_t		an_retry_short;		/* 0x28 */
	u_int32_t		an_retry_max;		/* 0x2C */
	u_int32_t		an_no_ack;		/* 0x30 */
	u_int32_t		an_no_cts;		/* 0x34 */
	u_int32_t		an_rx_ack_ok;		/* 0x38 */
	u_int32_t		an_rx_cts_ok;		/* 0x3C */
	u_int32_t		an_tx_ack_ok;		/* 0x40 */
	u_int32_t		an_tx_rts_ok;		/* 0x44 */
	u_int32_t		an_tx_cts_ok;		/* 0x48 */
	u_int32_t		an_tx_lmac_mcasts;	/* 0x4C */
	u_int32_t		an_tx_lmac_bcasts;	/* 0x50 */
	u_int32_t		an_tx_lmac_ucast_frags;	/* 0x54 */
	u_int32_t		an_tx_lmac_ucasts;	/* 0x58 */
	u_int32_t		an_tx_beacons;		/* 0x5C */
	u_int32_t		an_rx_beacons;		/* 0x60 */
	u_int32_t		an_tx_single_cols;	/* 0x64 */
	u_int32_t		an_tx_multi_cols;	/* 0x68 */
	u_int32_t		an_tx_defers_no;	/* 0x6C */
	u_int32_t		an_tx_defers_prot;	/* 0x70 */
	u_int32_t		an_tx_defers_energy;	/* 0x74 */
	u_int32_t		an_rx_dups;		/* 0x78 */
	u_int32_t		an_rx_partial;		/* 0x7C */
	u_int32_t		an_tx_too_old;		/* 0x80 */
	u_int32_t		an_rx_too_old;		/* 0x84 */
	u_int32_t		an_lostsync_max_retries;/* 0x88 */
	u_int32_t		an_lostsync_missed_beacons;/* 0x8C */
	u_int32_t		an_lostsync_arl_exceeded;/*0x90 */
	u_int32_t		an_lostsync_deauthed;	/* 0x94 */
	u_int32_t		an_lostsync_disassociated;/*0x98 */
	u_int32_t		an_lostsync_tsf_timing;	/* 0x9C */
	u_int32_t		an_tx_host_mcasts;	/* 0xA0 */
	u_int32_t		an_tx_host_bcasts;	/* 0xA4 */
	u_int32_t		an_tx_host_ucasts;	/* 0xA8 */
	u_int32_t		an_tx_host_failed;	/* 0xAC */
	u_int32_t		an_rx_host_mcasts;	/* 0xB0 */
	u_int32_t		an_rx_host_bcasts;	/* 0xB4 */
	u_int32_t		an_rx_host_ucasts;	/* 0xB8 */
	u_int32_t		an_rx_host_discarded;	/* 0xBC */
	u_int32_t		an_tx_hmac_mcasts;	/* 0xC0 */
	u_int32_t		an_tx_hmac_bcasts;	/* 0xC4 */
	u_int32_t		an_tx_hmac_ucasts;	/* 0xC8 */
	u_int32_t		an_tx_hmac_failed;	/* 0xCC */
	u_int32_t		an_rx_hmac_mcasts;	/* 0xD0 */
	u_int32_t		an_rx_hmac_bcasts;	/* 0xD4 */
	u_int32_t		an_rx_hmac_ucasts;	/* 0xD8 */
	u_int32_t		an_rx_hmac_discarded;	/* 0xDC */
	u_int32_t		an_tx_hmac_accepted;	/* 0xE0 */
	u_int32_t		an_ssid_mismatches;	/* 0xE4 */
	u_int32_t		an_ap_mismatches;	/* 0xE8 */
	u_int32_t		an_rates_mismatches;	/* 0xEC */
	u_int32_t		an_auth_rejects;	/* 0xF0 */
	u_int32_t		an_auth_timeouts;	/* 0xF4 */
	u_int32_t		an_assoc_rejects;	/* 0xF8 */
	u_int32_t		an_assoc_timeouts;	/* 0xFC */
	u_int32_t		an_reason_outside_table;/* 0x100 */
	u_int32_t		an_reason1;		/* 0x104 */
	u_int32_t		an_reason2;		/* 0x108 */
	u_int32_t		an_reason3;		/* 0x10C */
	u_int32_t		an_reason4;		/* 0x110 */
	u_int32_t		an_reason5;		/* 0x114 */
	u_int32_t		an_reason6;		/* 0x118 */
	u_int32_t		an_reason7;		/* 0x11C */
	u_int32_t		an_reason8;		/* 0x120 */
	u_int32_t		an_reason9;		/* 0x124 */
	u_int32_t		an_reason10;		/* 0x128 */
	u_int32_t		an_reason11;		/* 0x12C */
	u_int32_t		an_reason12;		/* 0x130 */
	u_int32_t		an_reason13;		/* 0x134 */
	u_int32_t		an_reason14;		/* 0x138 */
	u_int32_t		an_reason15;		/* 0x13C */
	u_int32_t		an_reason16;		/* 0x140 */
	u_int32_t		an_reason17;		/* 0x144 */
	u_int32_t		an_reason18;		/* 0x148 */
	u_int32_t		an_reason19;		/* 0x14C */
	u_int32_t		an_rx_mgmt_pkts;	/* 0x150 */
	u_int32_t		an_tx_mgmt_pkts;	/* 0x154 */
	u_int32_t		an_rx_refresh_pkts;	/* 0x158 */
	u_int32_t		an_tx_refresh_pkts;	/* 0x15C */
	u_int32_t		an_rx_poll_pkts;	/* 0x160 */
	u_int32_t		an_tx_poll_pkts;	/* 0x164 */
	u_int32_t		an_host_retries;	/* 0x168 */
	u_int32_t		an_lostsync_hostreq;	/* 0x16C */
	u_int32_t		an_host_tx_bytes;	/* 0x170 */
	u_int32_t		an_host_rx_bytes;	/* 0x174 */
	u_int32_t		an_uptime_usecs;	/* 0x178 */
	u_int32_t		an_uptime_secs;		/* 0x17C */
	u_int32_t		an_lostsync_better_ap;	/* 0x180 */
	u_int32_t		an_rsvd[10];
} __packed;

/*
 * Volatile WEP Key
 */
#define AN_RID_WEP_VOLATILE	0xFF15	/* Volatile WEP Key */
struct an_rid_wepkey {
	u_int16_t		an_key_index;		/* 0x02 */
	u_int8_t		an_mac_addr[6];		/* 0x04 */
	u_int16_t		an_key_len;		/* 0x0A */
	u_int8_t		an_key[16];		/* 0x0C */
} __packed;

/*
 * Persistent WEP Key
 */
#define AN_RID_WEP_PERSISTENT	0xFF16	/* Persistent WEP Key */

/*
 * LEAP Key
 */
#define AN_RID_LEAP_USER	0xFF23	/* User Name for LEAP */
#define AN_RID_LEAP_PASS	0xFF24	/* Password for LEAP */
struct an_rid_leapkey {
	u_int16_t		an_key_len;		/* 0x02 */
	u_int8_t		an_key[32];		/* 0x04 */
} __packed;

/*
 * MIC
 */
#define AN_RID_MIC		0xFF57	/* Message Integrity Check */
struct an_rid_mic {
	u_int16_t		an_mic_state;		/* 0x02 */
	u_int16_t		an_mic_mcast_valid;	/* 0x04 */
	u_int8_t		an_mic_mcast[16];	/* 0x06 */
	u_int16_t		an_mic_ucast_valid;	/* 0x16 */
	u_int8_t		an_mic_ucast[16];	/* 0x18 */
} __packed;

/*
 * Receive frame structure.
 */
struct an_rxframe {
	u_int32_t		an_rx_time;		/* 0x00 */
	u_int16_t		an_rx_status;		/* 0x04 */
	u_int16_t		an_rx_payload_len;	/* 0x06 */
	u_int8_t		an_rsvd0;		/* 0x08 */
	u_int8_t		an_rx_signal_strength;	/* 0x09 */
	u_int8_t		an_rx_rate;		/* 0x0A */
	u_int8_t		an_rx_chan;		/* 0x0B */
	u_int8_t		an_rx_assoc_cnt;	/* 0x0C */
	u_int8_t		an_rsvd1[3];		/* 0x0D */
	u_int8_t		an_plcp_hdr[4];		/* 0x10 */
	struct ieee80211_frame_addr4	an_whdr;
	u_int16_t		an_gaplen;		/* 0x32 */
} __packed;
#define AN_RXGAP_MAX	8

/*
 * Transmit frame structure.
 */
struct an_txframe {
	u_int32_t		an_tx_sw;		/* 0x00 */
	u_int16_t		an_tx_status;		/* 0x04 */
	u_int16_t		an_tx_payload_len;	/* 0x06 */
	u_int16_t		an_tx_ctl;		/* 0x08 */
	u_int16_t		an_tx_assoc_id;		/* 0x0A */
	u_int16_t		an_tx_retry;		/* 0x0C */
	u_int8_t		an_tx_assoc_cnt;	/* 0x0E */
	u_int8_t		an_tx_rate;		/* 0x0F */
	u_int8_t		an_tx_max_long_retries;	/* 0x10 */
	u_int8_t		an_tx_max_short_retries; /*0x11 */
	u_int8_t		an_rsvd0[2];		/* 0x12 */
	struct ieee80211_frame_addr4	an_whdr;
	u_int16_t		an_gaplen;		/* 0x32 */
} __packed;

#define	AN_TXGAP_802_3	0
#define	AN_TXGAP_802_11	6

struct an_802_3_hdr {
	u_int16_t		an_802_3_status;
	u_int16_t		an_802_3_payload_len;
	u_int8_t		an_dst_addr[6];
	u_int8_t		an_src_addr[6];
} __packed;

#define AN_TXSTAT_EXCESS_RETRY	0x0002
#define AN_TXSTAT_LIFE_EXCEEDED	0x0004
#define AN_TXSTAT_AID_FAIL	0x0008
#define AN_TXSTAT_MAC_DISABLED	0x0010
#define AN_TXSTAT_ASSOC_LOST	0x0020

#define AN_TXCTL_RSVD		0x0001
#define AN_TXCTL_TXOK_INTR	0x0002
#define AN_TXCTL_TXERR_INTR	0x0004
#define AN_TXCTL_HEADER_TYPE	0x0008
#define AN_TXCTL_PAYLOAD_TYPE	0x0010
#define AN_TXCTL_NORELEASE	0x0020
#define AN_TXCTL_NORETRIES	0x0040
#define AN_TXCTL_CLEAR_AID	0x0080
#define AN_TXCTL_STRICT_ORDER	0x0100
#define AN_TXCTL_USE_RTS	0x0200

#define AN_HEADERTYPE_8023	0x0000
#define AN_HEADERTYPE_80211	0x0008

#define AN_PAYLOADTYPE_ETHER	0x0000
#define AN_PAYLOADTYPE_LLC	0x0010

#define AN_TXCTL_80211	\
	(AN_TXCTL_TXOK_INTR|AN_TXCTL_TXERR_INTR|AN_HEADERTYPE_80211|	\
	AN_PAYLOADTYPE_LLC|AN_TXCTL_NORELEASE)

#define AN_TXCTL_8023	\
	(AN_TXCTL_TXOK_INTR|AN_TXCTL_TXERR_INTR|AN_HEADERTYPE_8023|	\
	AN_PAYLOADTYPE_ETHER|AN_TXCTL_NORELEASE)

#define AN_STAT_BADCRC		0x0001
#define AN_STAT_UNDECRYPTABLE	0x0002
#define AN_STAT_ERRSTAT		0x0003
#define AN_STAT_MAC_PORT	0x0700
#define AN_STAT_1042		0x2000	/* RFC1042 encoded */
#define AN_STAT_TUNNEL		0x4000	/* Bridge-tunnel encoded */
#define AN_STAT_WMP_MSG		0x6000	/* WaveLAN-II management protocol */
#define AN_RXSTAT_MSG_TYPE	0xE000

#define AN_ENC_TX_802_3		0x00
#define AN_ENC_TX_802_11	0x11
#define AN_ENC_TX_E_II		0x0E

#define AN_ENC_TX_1042		0x00
#define AN_ENC_TX_TUNNEL	0xF8

#define AN_TXCNTL_MACPORT	0x00FF
#define AN_TXCNTL_STRUCTTYPE	0xFF00

#endif	/* _DEV_IC_ANREG_H */