From b50e475927408ade03eb1e4c3fd6b5c8eab2212e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sun, 6 Mar 2005 05:02:03 +0000 Subject: [PATCH] Bring in the IWI driver from FreeBSD and merge in ALTQ support. Bring in a number of ifconfig updates from FreeBSD. Submitted-by: Andrew Atrens Original-Author: Damien Bergamini (IWI) --- sbin/ifconfig/ifconfig.8 | 220 ++- sbin/ifconfig/ifconfig.c | 147 +- sbin/ifconfig/ifconfig.h | 10 +- sbin/ifconfig/ifieee80211.c | 154 +- sbin/ifconfig/ifmedia.c | 203 +- sbin/ifconfig/ifvlan.c | 4 +- share/man/man4/Makefile | 3 +- sys/dev/netif/Makefile | 4 +- sys/dev/netif/iwi/Makefile | 17 + sys/dev/netif/iwi/if_iwi.c | 3053 ++++++++++++++++++++++++++++++ sys/dev/netif/iwi/if_iwireg.h | 508 +++++ sys/dev/netif/iwi/if_iwivar.h | 210 ++ usr.sbin/Makefile | 3 +- usr.sbin/iwicontrol/Makefile | 8 + usr.sbin/iwicontrol/iwicontrol.8 | 102 + usr.sbin/iwicontrol/iwicontrol.c | 340 ++++ 16 files changed, 4819 insertions(+), 167 deletions(-) create mode 100644 sys/dev/netif/iwi/Makefile create mode 100644 sys/dev/netif/iwi/if_iwi.c create mode 100644 sys/dev/netif/iwi/if_iwireg.h create mode 100644 sys/dev/netif/iwi/if_iwivar.h create mode 100644 usr.sbin/iwicontrol/Makefile create mode 100644 usr.sbin/iwicontrol/iwicontrol.8 create mode 100644 usr.sbin/iwicontrol/iwicontrol.c diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 1edfd60e34..c42b9bd850 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -9,10 +9,6 @@ .\" 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 acknowledgment: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. @@ -30,10 +26,10 @@ .\" SUCH DAMAGE. .\" .\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 -.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.27.2.22 2003/01/26 03:33:56 keramida Exp $ -.\" $DragonFly: src/sbin/ifconfig/ifconfig.8,v 1.4 2004/11/13 04:13:22 cpressey Exp $ +.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.85 2004/07/27 09:51:49 yar Exp $ +.\" $DragonFly: src/sbin/ifconfig/ifconfig.8,v 1.5 2005/03/06 05:01:59 dillon Exp $ .\" -.Dd July 2, 2001 +.Dd July 26, 2004 .Dt IFCONFIG 8 .Os .Sh NAME @@ -140,7 +136,7 @@ The link-level address is specified as a series of colon-separated hex digits. This can be used to -e.g. set a new MAC address on an ethernet interface, though the +e.g.\& set a new MAC address on an ethernet interface, though the mechanism used is not ethernet-specific. If the interface is already up when this option is used, it will be briefly brought down and @@ -201,9 +197,7 @@ Usually .Li 0xffffffff is most appropriate. .It Fl alias -Remove the specified network address from this interface -(if none is specified, all network addresses for the interface -are removed.) +Remove the network address specified. This would be used if you incorrectly specified an alias, or it was no longer needed. If you have incorrectly set an NS address having the side effect @@ -271,6 +265,12 @@ automatically. .\" IP encapsulation of .\" .Tn CLNP .\" packets is done differently. + .It Cm maclabel Ar label + If Mandatory Access Control support is enabled in the kernel, + set the MAC label to + .Ar label . +.\" (see +.\" .Xr maclabel 7 ) . .It Cm media Ar type If the driver supports the media selection system, set the media type of the interface to @@ -282,10 +282,10 @@ interface might support the use of either .Tn AUI or twisted pair connectors. Setting the media type to -.Dq 10base5/AUI +.Dq Li 10base5/AUI would change the currently active connector to the AUI port. Setting it to -.Dq 10baseT/UTP +.Dq Li 10baseT/UTP would activate twisted pair. Refer to the interfaces' driver specific documentation or man page for a complete list of the @@ -302,6 +302,40 @@ list of available options. .It Fl mediaopt Ar opts If the driver supports the media selection system, disable the specified media options on the interface. +.It Cm mode Ar mode +If the driver supports the media selection system, set the specified +operating mode on the interface to +.Ar mode . +For IEEE 802.11 wireless interfaces that support multiple operating modes +this directive is used to select between 802.11a +.Pq Dq Li 11a , +802.11b +.Pq Dq Li 11b , +and 802.11g +.Pq Dq Li 11g +operating modes. +.It Cm name Ar name +Set the interface name to +.Ar name . +.It Cm rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +enable receive (or transmit) checksum offloading on the interface. +Some drivers may not be able to enable these flags independently +of each other, so setting one may also set the other. +The driver will offload as much checksum work as it can reliably +support, the exact level of offloading varies between drivers. +.\".It Fl rxcsum , Fl txcsum +.\"If the driver supports user-configurable checksum offloading, +.\"disable receive (or transmit) checksum offloading on the interface. +.\"These settings may not always be independent of each other. +.\".It Cm polling +.\"If the driver has user-configurable +.\".Xr polling 4 +.\"support, select the polling mode on the interface. +.\".It Fl polling +.\"If the driver has user-configurable +.\".Xr polling 4 +.\"support, select the interrupt mode on the interface. .It Cm tunnel Ar src_addr dest_addr (IP tunnel devices only.) Configure the physical source and destination address for IP tunnel @@ -340,55 +374,88 @@ Included for .Tn Solaris compatibility. .It Cm vlan Ar vlan_tag -If the interface is a vlan pseudo interface, set the vlan tag value +If the interface is a +.Xr vlan 4 +pseudo interface, set the VLAN tag value to .Ar vlan_tag . This value is a 16-bit number which is used to create an 802.1Q -vlan header for packets sent from the vlan interface. +VLAN header for packets sent from the +.Xr vlan 4 +interface. Note that .Cm vlan and .Cm vlandev must both be set at the same time. .It Cm vlandev Ar iface -If the interface is a vlan pseudo device, associate physical interface +If the interface is a +.Xr vlan 4 +pseudo device, associate physical interface .Ar iface with it. -Packets transmitted through the vlan interface will be +Packets transmitted through the +.Xr vlan 4 +interface will be diverted to the specified physical interface .Ar iface -with 802.1Q vlan encapsulation. +with 802.1Q VLAN encapsulation. Packets with 802.1Q encapsulation received -by the parent interface with the correct vlan tag will be diverted to -the associated vlan pseudo-interface. -The vlan interface is assigned a +by the parent interface with the correct VLAN tag will be diverted to +the associated +.Xr vlan 4 +pseudo-interface. +The +.Xr vlan 4 +interface is assigned a copy of the parent interface's flags and the parent's ethernet address. The .Cm vlandev and .Cm vlan must both be set at the same time. -If the vlan interface already has +If the +.Xr vlan 4 +interface already has a physical interface associated with it, this command will fail. To change the association to another physical interface, the existing association must be cleared first. .Pp -Note: if the -.Cm link0 -flag is set on the vlan interface, the vlan pseudo -interface's behavior changes: the -.Cm link0 -tells the vlan interface that the -parent interface supports insertion and extraction of vlan tags on its +Note: if the hardware tagging capability +is set on the parent interface, the +.Xr vlan 4 +pseudo +interface's behavior changes: +the +.Xr vlan 4 +interface recognizes that the +parent interface supports insertion and extraction of VLAN tags on its own (usually in firmware) and that it should pass packets to and from the parent unaltered. .It Fl vlandev Ar iface -If the driver is a vlan pseudo device, disassociate the physical interface +If the driver is a +.Xr vlan 4 +pseudo device, disassociate the physical interface .Ar iface from it. -This breaks the link between the vlan interface and its parent, -clears its vlan tag, flags and its link address and shuts the interface down. +This breaks the link between the +.Xr vlan 4 +interface and its parent, +clears its VLAN tag, flags and its link address and shuts the interface down. +.It Cm vlanmtu , vlanhwtag +If the driver offers user-configurable VLAN support, enable +reception of extended frames or tag processing in hardware, +respectively. +Note that this must be issued on a physical interface associated with +.Xr vlan 4 , +not on a +.Xr vlan 4 +interface itself. +.It Fl vlanmtu , Fl vlanhwtag +If the driver offers user-configurable VLAN support, disable +reception of extended frames or tag processing in hardware, +respectively. .It Cm metric Ar n Set the routing metric of the interface to .Ar n , @@ -479,7 +546,7 @@ of the form .Ar startnet Ns - Ns Ar endnet . Appletalk uses this scheme instead of netmasks though -.Dx +.Fx implements it internally as a set of netmasks. .It Cm remove Another name for the @@ -507,6 +574,13 @@ for more information. .It Fl link Op Cm 0 No - Cm 2 .Sm on Disable special processing at the link level with the specified interface. +.\".It Cm monitor +.\"Put the interface in monitor mode. +.\"No packets are transmitted, and received packets are discarded after +.\".Xr bpf 4 +.\"processing. +.\".It Fl monitor +.\"Take the interface out of monitor mode. .It Cm up Mark an interface .Dq up . @@ -559,10 +633,10 @@ in infrastructure mode. Not all adaptors support all modes. The set of valid modes is -.Dq none , -.Dq open , +.Dq Li none , +.Dq Li open , and -.Dq shared . +.Dq Li shared . Modes are case insensitive. .It Cm powersave For IEEE 802.11 wireless interfaces, enable powersave mode. @@ -571,24 +645,58 @@ For IEEE 802.11 wireless interfaces, disable powersave mode. .It Cm powersavesleep Ar sleep For IEEE 802.11 wireless interfaces, set the desired max powersave sleep time in milliseconds. +.It Cm protmode Ar technique +For IEEE 802.11 wireless interfaces operating in 11g, use the specified +.Ar technique +for protecting OFDM frames in a mixed 11b/11g network. +The set of valid techniques is +.Dq Li off , +.Dq Li cts +(CTS to self), +and +.Dq Li rtscts +(RTS/CTS). +Technique names are case insensitive. +.It Cm rtsthreshold Ar length +For IEEE 802.11 wireless interfaces, set the threshold for which +transmitted frames are preceded by transmission of an +RTS +control frame. +The +.Ar length +argument +is the frame size in bytes and must be in the range 1 to 2312. +Not all adaptors support setting the RTS threshold. +.It Cm txpower Ar power +For IEEE 802.11 wireless interfaces, set the power used to transmit frames. +The +.Ar power +argument +is a unitless value in the range 0 to 100 that is interpreted +by drivers to derive a device-specific value. +Out of range values are truncated. +Typically only a few discreet power settings are available and +the driver will use the setting closest to the specified value. +Not all adaptors support changing the transmit power. .It Cm wepmode Ar mode For IEEE 802.11 wireless interfaces, set the desired WEP mode. Not all adaptors support all modes. The set of valid modes is -.Dq off , -.Dq on , +.Dq Li off , +.Dq Li on , and -.Dq mixed . -.Dq Mixed +.Dq Li mixed . +The +.Dq Li mixed mode explicitly tells the adaptor to allow association with access points which allow both encrypted and unencrypted traffic. On these adaptors, -.Dq on +.Dq Li on means that the access point must only allow encrypted connections. On other adaptors, -.Dq on +.Dq Li on is generally another name for -.Dq mixed . +.Dq Li mixed . Modes are case insensitive. .It Cm weptxkey Ar index For IEEE 802.11 wireless interfaces, set the WEP key to be used for @@ -609,7 +717,7 @@ the mapping of text keys to WEP encryption is usually driver-specific. In particular, the .Tn Windows drivers do this mapping differently to -.Dx . +.Fx . A key may be cleared by setting it to .Ql - . If WEP is supported then there are at least four keys. @@ -673,7 +781,7 @@ If the .Fl m flag is passed before an interface name, .Nm -will display all +will display the capability list and all of the supported media for the specified interface. If .Fl L @@ -722,18 +830,26 @@ Messages indicating the specified interface does not exist, the requested address is unknown, or the user is not privileged and tried to alter an interface's configuration. .Sh BUGS -IPv6 link-local addresses are required for several basic communication -between IPv6 node. -If they are deleted by -.Nm -manually, the kernel might show very strange behavior. -So, such manual deletions are strongly discouraged. +Basic IPv6 node operation requires a link-local address on each +interface configured for IPv6. +Normally, such an address is automatically configured by the +kernel on each interface added to the system; this behaviour may +be disabled by setting the sysctl MIB variable +.Va net.inet6.ip6.auto_linklocal +to 0. +.Pp +If you delete such an address using +.Nm , +the kernel may act very oddly. +Do this at your own risk. .Sh SEE ALSO .Xr netstat 1 , .Xr netintro 4 , +.Xr vlan 4 , .\" .Xr eon 5 , .Xr rc 8 , -.Xr routed 8 +.Xr routed 8 , +.Xr sysctl 8 .Sh HISTORY The .Nm diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index da0562a100..0c17588a2e 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -10,10 +10,6 @@ * 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 the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -33,7 +29,7 @@ * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved. * @(#)ifconfig.c 8.2 (Berkeley) 2/16/94 * $FreeBSD: src/sbin/ifconfig/ifconfig.c,v 1.96 2004/02/27 06:43:14 kan Exp $ - * $DragonFly: src/sbin/ifconfig/ifconfig.c,v 1.19 2005/03/04 02:37:44 cpressey Exp $ + * $DragonFly: src/sbin/ifconfig/ifconfig.c,v 1.20 2005/03/06 05:01:59 dillon Exp $ */ #include @@ -99,6 +95,11 @@ #define NI_WITHSCOPEID 0 #endif +/* + * Since "struct ifreq" is composed of various union members, callers + * should pay special attention to interprete the value. + * (.e.g. little/big endian difference in the structure.) + */ struct ifreq ifr, ridreq; struct ifaliasreq addreq; #ifdef INET6 @@ -235,8 +236,16 @@ struct cmd { { "-link1", -IFF_LINK1, setifflags, NULL }, { "link2", IFF_LINK2, setifflags, NULL }, { "-link2", -IFF_LINK2, setifflags, NULL }, +#if notyet + { "monitor", IFF_MONITOR, setifflags, NULL }, + { "-monitor", -IFF_MONITOR, setifflags, NULL }, + { "staticarp", IFF_STATICARP, setifflags, NULL }, + { "-staticarp", -IFF_STATICARP, setifflags, NULL }, +#endif + #ifdef USE_IF_MEDIA { "media", NEXTARG, setmedia, NULL }, + { "mode", NEXTARG, setmediamode, NULL }, { "mediaopt", NEXTARG, setmediaopt, NULL }, { "-mediaopt", NEXTARG, unsetmediaopt, NULL }, #endif @@ -255,21 +264,27 @@ struct cmd { #ifdef USE_IEEE80211 { "ssid", NEXTARG, set80211ssid, NULL }, { "nwid", NEXTARG, set80211ssid, NULL }, - { "stationname", NEXTARG, set80211stationname, NULL }, - { "station", NEXTARG, set80211stationname, NULL },/* BSD/OS */ - { "channel", NEXTARG, set80211channel, NULL }, - { "authmode", NEXTARG, set80211authmode, NULL }, - { "powersavemode", NEXTARG, set80211powersavemode, NULL }, - { "powersave", 1, set80211powersave, NULL }, - { "-powersave", 0, set80211powersave, NULL }, - { "powersavesleep", NEXTARG, set80211powersavesleep, NULL }, - { "wepmode", NEXTARG, set80211wepmode, NULL }, + { "stationname", NEXTARG, set80211stationname, NULL }, + { "station", NEXTARG, set80211stationname, NULL }, /* BSD/OS */ + { "channel", NEXTARG, set80211channel, NULL }, + { "authmode", NEXTARG, set80211authmode, NULL }, + { "powersavemode", NEXTARG, set80211powersavemode, NULL }, + { "powersave", 1, set80211powersave, NULL }, + { "-powersave", 0, set80211powersave, NULL }, + { "powersavesleep", NEXTARG, set80211powersavesleep, NULL }, + { "wepmode", NEXTARG, set80211wepmode, NULL }, { "wep", 1, set80211wep, NULL }, { "-wep", 0, set80211wep, NULL }, - { "weptxkey", NEXTARG, set80211weptxkey, NULL }, + { "weptxkey", NEXTARG, set80211weptxkey, NULL }, { "wepkey", NEXTARG, set80211wepkey, NULL }, { "nwkey", NEXTARG, set80211nwkey, NULL }, /* NetBSD */ - { "-nwkey", 0, set80211wep, NULL }, /* NetBSD */ + { "-nwkey", 0, set80211wep, NULL }, /* NetBSD */ + { "rtsthreshold",NEXTARG, set80211rtsthreshold, NULL }, + { "protmode", NEXTARG, set80211protmode, NULL }, + { "txpower", NEXTARG, set80211txpower, NULL }, +#endif +#ifdef USE_MAC + { "maclabel", NEXTARG, setifmaclabel, NULL }, #endif { "rxcsum", IFCAP_RXCSUM, setifcap, NULL }, { "-rxcsum", -IFCAP_RXCSUM, setifcap, NULL }, @@ -279,6 +294,10 @@ struct cmd { { "-netcons", -IFCAP_NETCONS, setifcap, NULL }, { "polling", IFCAP_POLLING, setifcap, NULL }, { "-polling", -IFCAP_POLLING, setifcap, NULL }, + { "vlanmtu", IFCAP_VLAN_MTU, setifcap, NULL }, + { "-vlanmtu", -IFCAP_VLAN_MTU, setifcap, NULL }, + { "vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap, NULL }, + { "-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap, NULL }, { "normal", -IFF_LINK0, setifflags, NULL }, { "compress", IFF_LINK0, setifflags, NULL }, { "noicmp", IFF_LINK1, setifflags, NULL }, @@ -351,17 +370,6 @@ struct afswtch { 0, SIOCSIFLLADDR, NULL, C(ridreq) }, { "lladdr", AF_LINK, link_status, link_getaddr, NULL, 0, SIOCSIFLLADDR, NULL, C(ridreq) }, -#if 0 /* XXX conflicts with the media command */ -#ifdef USE_IF_MEDIA - { "media", AF_UNSPEC, media_status, NULL, NULL, }, /* XXX not real!! */ -#endif -#ifdef USE_VLANS - { "vlan", AF_UNSPEC, vlan_status, NULL, NULL, }, /* XXX not real!! */ -#endif -#ifdef USE_IEEE80211 - { "ieee80211", AF_UNSPEC, ieee80211_status, NULL, NULL, }, /* XXX not real!! */ -#endif -#endif { NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL } }; @@ -371,22 +379,22 @@ struct afswtch { */ #define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) void rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo) { - struct sockaddr *sa; - int i; - - memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); - for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { - if ((rtinfo->rti_addrs & (1 << i)) == 0) - continue; - rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; - ADVANCE(cp, sa); - } + struct sockaddr *sa; + int i; + + memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); + for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { + if ((rtinfo->rti_addrs & (1 << i)) == 0) + continue; + rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; + ADVANCE(cp, sa); + } } @@ -420,7 +428,7 @@ main(int argc, char * const *argv) { int c; int all, namesonly, downonly, uponly; - int need_nl = 0; + int need_nl = 0, count = 0; const struct afswtch *afp = 0; int addrcount, ifindex; struct if_msghdr *ifm, *nextifm; @@ -547,12 +555,13 @@ main(int argc, char * const *argv) afp = NULL; /* not a family, NULL */ } +retry: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; - mib[3] = 0; /* address family */ + mib[3] = 0; /* address family */ mib[4] = NET_RT_IFLIST; - mib[5] = ifindex; /* interface index */ + mib[5] = ifindex; /* interface index */ /* if particular family specified, only ask about it */ if (afp) @@ -562,8 +571,15 @@ main(int argc, char * const *argv) errx(1, "iflist-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(1, "malloc"); - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + if (errno == ENOMEM && count++ < 10) { + warnx("Routing table grew, retrying"); + free(buf); + sleep(1); + goto retry; + } errx(1, "actual retrieval of interface table"); + } lim = buf + needed; next = buf; @@ -572,7 +588,14 @@ main(int argc, char * const *argv) ifm = (struct if_msghdr *)next; if (ifm->ifm_type == RTM_IFINFO) { - sdl = (struct sockaddr_dl *)(ifm + 1); +#if notyet + if (ifm->ifm_data.ifi_datalen == 0) + ifm->ifm_data.ifi_datalen = sizeof(struct if_data); + sdl = (struct sockaddr_dl *)((char *)ifm + sizeof(struct if_msghdr) - + sizeof(struct if_data) + ifm->ifm_data.ifi_datalen); +#else + sdl = (struct sockaddr_dl *)(ifm + 1); +#endif flags = ifm->ifm_flags; } else { fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n"); @@ -600,8 +623,11 @@ main(int argc, char * const *argv) addrcount++; next += nextifm->ifm_msglen; } - strncpy(name, sdl->sdl_data, sdl->sdl_nlen); - name[sdl->sdl_nlen] = '\0'; + memcpy(name, sdl->sdl_data, + sizeof(name) < sdl->sdl_nlen ? + sizeof(name)-1 : sdl->sdl_nlen); + name[sizeof(name) < sdl->sdl_nlen ? + sizeof(name)-1 : sdl->sdl_nlen] = '\0'; if (all || namesonly) { size_t len; @@ -842,7 +868,6 @@ void deletetunnel(const char *vname __unused, int param __unused, int s, const struct afswtch *afp __unused) { - if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } @@ -976,9 +1001,10 @@ notealias(const char *addr __unused, int param, int s __unused, const struct afswtch *afp) { if (setaddr && doalias == 0 && param < 0) - bcopy((caddr_t)rqtosa(af_addreq), - (caddr_t)rqtosa(af_ridreq), - rqtosa(af_addreq)->sa_len); + if (afp->af_addreq != NULL && afp->af_ridreq != NULL) + bcopy((caddr_t)rqtosa(af_addreq), + (caddr_t)rqtosa(af_ridreq), + rqtosa(af_addreq)->sa_len); doalias = param; if (param < 0) { clearaddr = 1; @@ -1096,10 +1122,10 @@ setifname(const char *val, int dummy __unused, int s, #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ -"\20MULTICAST" +"\20MULTICAST\21POLLING\23MONITOR\24STATICARP" #define IFCAPBITS \ -"\003\1rxcsum\2txcsum\3netcons\7polling" +"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" /* * Print the status of the interface. If an address family was @@ -1181,12 +1207,19 @@ status(const struct afswtch *afp, int addrcount, struct sockaddr_dl *sdl, #ifdef USE_IEEE80211 if (allfamilies || afp->af_status == ieee80211_status) ieee80211_status(s, NULL); +#endif +#ifdef USE_MAC + if (allfamilies || afp->af_status == maclabel_status) + maclabel_status(s, NULL); #endif strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); - if (!allfamilies && !p && afp->af_status != media_status && + if (!allfamilies && !p && +#ifdef USE_IF_MEDIA + afp->af_status != media_status && +#endif afp->af_status != link_status #ifdef USE_VLANS && afp->af_status != vlan_status @@ -1300,7 +1333,7 @@ in_status(int s __unused, struct rt_addrinfo *info) #ifdef INET6 #if defined(__KAME__) && defined(KAME_SCOPEID) void -in6_fillscopeid(struct sockaddr_in6 *sin6) +in6_fillscopeid(struct sockaddr_in6 *sin6 __unused) { if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = @@ -1760,9 +1793,9 @@ void setatrange(const char *range, int dummy __unused, int s __unused, const struct afswtch *afp __unused) { - u_short first = 123, last = 123; + u_int first = 123, last = 123; - if (sscanf(range, "%hu-%hu", &first, &last) != 2 + if (sscanf(range, "%u-%u", &first, &last) != 2 || first == 0 || first > 0xffff || last == 0 || last > 0xffff || first > last) errx(1, "%s: illegal net range: %u-%u", range, first, last); @@ -1972,7 +2005,7 @@ clone_create(void) err(1, "socket"); memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFCREATE, &ifr) < 0) err(1, "SIOCIFCREATE"); @@ -1994,7 +2027,7 @@ clone_destroy(const char *val __unused, int d __unused, int s, const struct afswtch *rafp __unused) { - strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + (void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) err(1, "SIOCIFDESTROY"); /* diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index de90fba837..01c5ee91fc 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -31,8 +31,8 @@ * * so there! * - * $FreeBSD: src/sbin/ifconfig/ifconfig.h,v 1.5.2.2 2001/07/04 20:49:20 brooks Exp $ - * $DragonFly: src/sbin/ifconfig/ifconfig.h,v 1.3 2004/03/15 22:39:37 hmp Exp $ + * $FreeBSD: src/sbin/ifconfig/ifconfig.h,v 1.12 2004/03/30 22:59:22 sam Exp $ + * $DragonFly: src/sbin/ifconfig/ifconfig.h,v 1.4 2005/03/06 05:01:59 dillon Exp $ */ extern struct ifreq ifr; @@ -43,6 +43,7 @@ extern int supmedia; struct afswtch; extern void setmedia(const char *, int, int, const struct afswtch *rafp); +extern void setmediamode(const char *, int, int, const struct afswtch *rafp); extern void setmediaopt(const char *, int, int, const struct afswtch *rafp); extern void unsetmediaopt(const char *, int, int, const struct afswtch *rafp); extern void media_status(int s, struct rt_addrinfo *); @@ -64,4 +65,9 @@ extern void set80211wep(const char *, int, int, const struct afswtch *rafp); extern void set80211weptxkey(const char *, int, int, const struct afswtch *rafp); extern void set80211wepkey(const char *, int, int, const struct afswtch *rafp); extern void set80211nwkey(const char *, int, int, const struct afswtch *rafp); +extern void set80211rtsthreshold(const char *, int, int, const struct afswtch *rafp); +extern void set80211protmode(const char *, int, int, const struct afswtch *rafp); +extern void set80211txpower(const char *, int, int, const struct afswtch *rafp); extern void ieee80211_status(int s, struct rt_addrinfo *); +extern void maclabel_status(int s, struct rt_addrinfo *); +extern void setifmaclabel(const char *, int, int, const struct afswtch *rafp); diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index ec1be43f14..0ff8f20568 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -9,6 +9,8 @@ * 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. The name of The Aerospace Corporation may not be used to endorse or + * promote products derived from this software. * * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -23,7 +25,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sbin/ifconfig/ifieee80211.c,v 1.1.2.3 2002/02/07 15:12:37 ambrisko Exp $ - * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.7 2005/03/04 00:11:11 cpressey Exp $ + * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.8 2005/03/06 05:01:59 dillon Exp $ */ /*- @@ -75,6 +77,7 @@ #include #include #include +#include #include #include @@ -102,7 +105,7 @@ set80211ssid(const char *val, int d __unused, int s, u_int8_t data[33]; ssid = 0; - len = sizeof(val); + len = strlen(val); if (len > 2 && isdigit(val[0]) && val[1] == ':') { ssid = atoi(val)-1; val += 2; @@ -133,7 +136,10 @@ void set80211channel(const char *val, int d __unused, int s, const struct afswtch *rafp __unused) { - set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL); + if (strcmp(val, "-") == 0) + set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL); + else + set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL); } void @@ -142,11 +148,11 @@ set80211authmode(const char *val, int d __unused, int s, { int mode; - if(strcasecmp(val, "none") == 0) { + if (strcasecmp(val, "none") == 0) { mode = IEEE80211_AUTH_NONE; - } else if(strcasecmp(val, "open") == 0) { + } else if (strcasecmp(val, "open") == 0) { mode = IEEE80211_AUTH_OPEN; - } else if(strcasecmp(val, "shared") == 0) { + } else if (strcasecmp(val, "shared") == 0) { mode = IEEE80211_AUTH_SHARED; } else { err(1, "unknown authmode"); @@ -161,15 +167,15 @@ set80211powersavemode(const char *val, int d __unused, int s, { int mode; - if(strcasecmp(val, "off") == 0) { + if (strcasecmp(val, "off") == 0) { mode = IEEE80211_POWERSAVE_OFF; - } else if(strcasecmp(val, "on") == 0) { + } else if (strcasecmp(val, "on") == 0) { mode = IEEE80211_POWERSAVE_ON; - } else if(strcasecmp(val, "cam") == 0) { + } else if (strcasecmp(val, "cam") == 0) { mode = IEEE80211_POWERSAVE_CAM; - } else if(strcasecmp(val, "psp") == 0) { + } else if (strcasecmp(val, "psp") == 0) { mode = IEEE80211_POWERSAVE_PSP; - } else if(strcasecmp(val, "psp-cam") == 0) { + } else if (strcasecmp(val, "psp-cam") == 0) { mode = IEEE80211_POWERSAVE_PSP_CAM; } else { err(1, "unknown powersavemode"); @@ -203,11 +209,11 @@ set80211wepmode(const char *val, int d __unused, int s, { int mode; - if(strcasecmp(val, "off") == 0) { + if (strcasecmp(val, "off") == 0) { mode = IEEE80211_WEP_OFF; - } else if(strcasecmp(val, "on") == 0) { + } else if (strcasecmp(val, "on") == 0) { mode = IEEE80211_WEP_ON; - } else if(strcasecmp(val, "mixed") == 0) { + } else if (strcasecmp(val, "mixed") == 0) { mode = IEEE80211_WEP_MIXED; } else { err(1, "unknown wep mode"); @@ -236,9 +242,9 @@ set80211wepkey(const char *val, int d __unused, int s, { int key = 0; int len; - u_int8_t data[14]; + u_int8_t data[IEEE80211_KEYBUF_SIZE]; - if(isdigit(val[0]) && val[1] == ':') { + if (isdigit(val[0]) && val[1] == ':') { key = atoi(val)-1; val += 2; } @@ -251,7 +257,7 @@ set80211wepkey(const char *val, int d __unused, int s, } /* - * This function is purly a NetBSD compatibility interface. The NetBSD + * This function is purly a NetBSD compatability interface. The NetBSD * iterface is too inflexable, but it's there so we'll support it since * it's not all that hard. */ @@ -261,15 +267,15 @@ set80211nwkey(const char *val, int d __unused, int s, { int txkey; int i, len; - u_int8_t data[14]; + u_int8_t data[IEEE80211_KEYBUF_SIZE]; set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL); - if(isdigit(val[0]) && val[1] == ':') { + if (isdigit(val[0]) && val[1] == ':') { txkey = val[0]-'0'-1; val += 2; - for(i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) { bzero(data, sizeof(data)); len = sizeof(data); val = get_string(val, ",", data, &len); @@ -285,13 +291,46 @@ set80211nwkey(const char *val, int d __unused, int s, set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data); bzero(data, sizeof(data)); - for(i = 1; i < 4; i++) + for (i = 1; i < 4; i++) set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data); } set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL); } +void +set80211rtsthreshold(const char *val, int d __unused, int s, + const struct afswtch *rafp __unused) +{ + set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL); +} + +void +set80211protmode(const char *val, int d __unused, int s, + const struct afswtch *rafp __unused) +{ + int mode; + + if (strcasecmp(val, "off") == 0) { + mode = IEEE80211_PROTMODE_OFF; + } else if (strcasecmp(val, "cts") == 0) { + mode = IEEE80211_PROTMODE_CTS; + } else if (strcasecmp(val, "rtscts") == 0) { + mode = IEEE80211_PROTMODE_RTSCTS; + } else { + err(1, "unknown protection mode"); + } + + set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL); +} + +void +set80211txpower(const char *val, int d __unused, int s, + const struct afswtch *rafp __unused) +{ + set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL); +} + void ieee80211_status (int s, struct rt_addrinfo *info __unused) { @@ -301,8 +340,8 @@ ieee80211_status (int s, struct rt_addrinfo *info __unused) u_int8_t data[32]; char spacer; - memset(&ireq, 0, sizeof(ireq)); - strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); ireq.i_data = &data; ireq.i_type = IEEE80211_IOC_SSID; @@ -380,13 +419,49 @@ ieee80211_status (int s, struct rt_addrinfo *info __unused) ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; if (ioctl(s, SIOCG80211, &ireq) != -1) { - if(ireq.i_val) + if (ireq.i_val) printf(" powersavesleep %d", ireq.i_val); } } printf("\n"); + spacer = '\t'; + ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + printf("%crtsthreshold %d", spacer, ireq.i_val); + spacer = ' '; + } + + ireq.i_type = IEEE80211_IOC_PROTMODE; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + printf("%cprotmode", spacer); + switch (ireq.i_val) { + case IEEE80211_PROTMODE_OFF: + printf(" OFF"); + break; + case IEEE80211_PROTMODE_CTS: + printf(" CTS"); + break; + case IEEE80211_PROTMODE_RTSCTS: + printf(" RTSCTS"); + break; + default: + printf(" UNKNOWN"); + break; + } + spacer = ' '; + } + + ireq.i_type = IEEE80211_IOC_TXPOWER; + if (ioctl(s, SIOCG80211, &ireq) != -1) { + printf("%ctxpower %d", spacer, ireq.i_val); + spacer = ' '; + } + + if (spacer != '\t') + printf("\n"); + ireq.i_type = IEEE80211_IOC_WEP; if (ioctl(s, SIOCG80211, &ireq) != -1 && ireq.i_val != IEEE80211_WEP_NOSUP) { @@ -409,7 +484,7 @@ ieee80211_status (int s, struct rt_addrinfo *info __unused) /* * If we get here then we've got WEP support so we need * to print WEP status. - */ + */ ireq.i_type = IEEE80211_IOC_WEPTXKEY; if (ioctl(s, SIOCG80211, &ireq) < 0) { @@ -429,17 +504,19 @@ ieee80211_status (int s, struct rt_addrinfo *info __unused) ireq.i_type = IEEE80211_IOC_WEPKEY; spacer = '\t'; - for(i = 0; i < num; i++) { + for (i = 0; i < num; i++) { ireq.i_val = i; if (ioctl(s, SIOCG80211, &ireq) < 0) { warn("WEP support, but can get keys!"); goto end; } - if(ireq.i_len == 0 || ireq.i_len > 13) + if (ireq.i_len == 0 || + ireq.i_len > IEEE80211_KEYBUF_SIZE) continue; printf("%cwepkey %d:%s", spacer, i+1, - ireq.i_len <= 5 ? "64-bit" : "128-bit"); - if(spacer == '\t') + ireq.i_len <= 5 ? "40-bit" : + ireq.i_len <= 13 ? "104-bit" : "128-bit"); + if (spacer == '\t') spacer = ' '; } if (spacer == ' ') @@ -455,13 +532,13 @@ set80211(int s, int type, int val, int len, u_int8_t *data) { struct ieee80211req ireq; - memset(&ireq, 0, sizeof(ireq)); - strncpy(ireq.i_name, name, sizeof(ireq.i_name)); + (void) memset(&ireq, 0, sizeof(ireq)); + (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); ireq.i_type = type; ireq.i_val = val; ireq.i_len = len; ireq.i_data = data; - if(ioctl(s, SIOCS80211, &ireq) < 0) + if (ioctl(s, SIOCS80211, &ireq) < 0) err(1, "SIOCS80211"); } @@ -485,17 +562,20 @@ get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) break; } if (hexstr) { - if (!isxdigit((u_char)val[0]) || - !isxdigit((u_char)val[1])) { + if (!isxdigit((u_char)val[0])) { warnx("bad hexadecimal digits"); return NULL; } + if (!isxdigit((u_char)val[1])) { + warnx("odd count hexadecimal digits"); + return NULL; + } } - if (p > buf + len) { + if (p >= buf + len) { if (hexstr) warnx("hexadecimal digits too long"); else - warnx("strings too long"); + warnx("string too long"); return NULL; } if (hexstr) { @@ -525,7 +605,7 @@ print_string(const u_int8_t *buf, int len) i = 0; hasspc = 0; - for(; i < len; i++) { + for (; i < len; i++) { if (!isprint(buf[i]) && buf[i] != '\0') break; if (isspace(buf[i])) diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c index 9cf1db6487..e95f8c3656 100644 --- a/sbin/ifconfig/ifmedia.c +++ b/sbin/ifconfig/ifmedia.c @@ -1,6 +1,6 @@ /* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ /* $FreeBSD: src/sbin/ifconfig/ifmedia.c,v 1.6.2.3 2001/11/14 04:35:07 yar Exp $ */ -/* $DragonFly: src/sbin/ifconfig/ifmedia.c,v 1.7 2005/03/04 00:11:11 cpressey Exp $ */ +/* $DragonFly: src/sbin/ifconfig/ifmedia.c,v 1.8 2005/03/06 05:01:59 dillon Exp $ */ /* * Copyright (c) 1997 Jason R. Thorpe. @@ -90,8 +90,27 @@ #include "ifconfig.h" +#ifndef IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS + #define IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS { \ + { IFM_AUTO, "autoselect" }, \ + { IFM_IEEE80211_11A, "11a" }, \ + { IFM_IEEE80211_11B, "11b" }, \ + { IFM_IEEE80211_11G, "11g" }, \ + { IFM_IEEE80211_FH, "fh" }, \ + { 0, NULL }, \ +} +#endif + +#ifndef IFM_SUBTYPE_IEEE80211_MODE_ALIASES + #define IFM_SUBTYPE_IEEE80211_MODE_ALIASES { \ + { IFM_AUTO, "auto" }, \ + { 0, NULL }, \ +} +#endif + static void domediaopt(const char *, int, int); static int get_media_subtype(int, const char *); +static int get_media_mode(int, const char *); static int get_media_options(int, const char *); static int lookup_media_word(struct ifmedia_description *, const char *); static void print_media_word(int, int); @@ -108,8 +127,8 @@ media_status(int s, struct rt_addrinfo *info __unused) struct ifmediareq ifmr; int *media_list, i; - memset(&ifmr, 0, sizeof(ifmr)); - strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { /* @@ -159,6 +178,16 @@ media_status(int s, struct rt_addrinfo *info __unused) else printf("no ring"); break; + +#if notyet + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; +#endif + case IFM_IEEE80211: /* XXX: Different value for adhoc? */ if (ifmr.ifm_status & IFM_ACTIVE) @@ -189,8 +218,8 @@ setmedia(const char *val, int d __unused, int s, struct ifmediareq ifmr; int first_type, subtype; - memset(&ifmr, 0, sizeof(ifmr)); - strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); ifmr.ifm_count = 1; ifmr.ifm_ulist = &first_type; @@ -222,7 +251,7 @@ setmedia(const char *val, int d __unused, int s, IFM_TYPE(first_type) | subtype; if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) - err(1, "SIOCSIFMEDIA"); + err(1, "SIOCSIFMEDIA (media)"); } void @@ -247,8 +276,8 @@ domediaopt(const char *val, int clear, int s) struct ifmediareq ifmr; int *mwords, options; - memset(&ifmr, 0, sizeof(ifmr)); - strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); /* * We must go through the motions of reading all @@ -282,7 +311,49 @@ domediaopt(const char *val, int clear, int s) ifr.ifr_media |= options; if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) - err(1, "SIOCSIFMEDIA"); + err(1, "SIOCSIFMEDIA (mediaopt)"); +} + + +void +setmediamode(const char *val, int d __unused, int s, + const struct afswtch *afp __unused) +{ + struct ifmediareq ifmr; + int *mwords, mode; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + if (ifmr.ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr.ifm_ulist = mwords; + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + mode = get_media_mode(IFM_TYPE(mwords[0]), val); + + free(mwords); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr.ifm_current & ~IFM_MMASK) | mode; + + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (mode)"); } /********************************************************************** @@ -328,6 +399,23 @@ static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +#if notyet +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; +#endif + static struct ifmedia_description ifm_subtype_shared_descriptions[] = IFM_SUBTYPE_SHARED_DESCRIPTIONS; @@ -346,6 +434,10 @@ struct ifmedia_type_to_subtype { struct ifmedia_description *desc; int alias; } options[3]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; }; /* must be in the same order as IFM_TYPE_DESCRIPTIONS */ @@ -363,6 +455,9 @@ static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_ethernet_option_descriptions[0], 0 }, { NULL, 0 }, }, + { + { NULL, 0 }, + }, }, { { @@ -377,6 +472,9 @@ static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_tokenring_option_descriptions[0], 0 }, { NULL, 0 }, }, + { + { NULL, 0 }, + }, }, { { @@ -391,6 +489,9 @@ static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_fddi_option_descriptions[0], 0 }, { NULL, 0 }, }, + { + { NULL, 0 }, + }, }, { { @@ -405,6 +506,32 @@ static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, { NULL, 0 }, }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, +#if notyet + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, +#endif + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, +#if notyet + { &ifm_subtype_atm_option_descriptions[0], 0 }, +#endif + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, }, }; @@ -429,7 +556,30 @@ get_media_subtype(int type, const char *val) return (rval); } errx(1, "unknown media subtype: %s", val); - /* NOTREACHED */ + /*NOTREACHED*/ +} + +static int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; } static int @@ -508,8 +658,8 @@ static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) return ttos; } -static struct ifmedia_description *get_subtype_desc(int ifmw, - struct ifmedia_type_to_subtype *ttos) +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) { int i; struct ifmedia_description *desc; @@ -527,6 +677,25 @@ static struct ifmedia_description *get_subtype_desc(int ifmw, return NULL; } +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + static void print_media_word(int ifmw, int print_toptype) { @@ -564,6 +733,12 @@ print_media_word(int ifmw, int print_toptype) printf("%s", desc->ifmt_string); + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } + /* Find options. */ for (i = 0; ttos->options[i].desc != NULL; i++) { if (ttos->options[i].alias) @@ -613,6 +788,10 @@ print_media_word_ifconfig(int ifmw) got_subtype: printf("media %s", desc->ifmt_string); + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); + /* Find options. */ for (i = 0; ttos->options[i].desc != NULL; i++) { if (ttos->options[i].alias) diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index a85c425963..931beb8053 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -30,9 +30,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sbin/ifconfig/ifvlan.c,v 1.2 1999/08/28 00:13:09 peter Exp $ - * $DragonFly: src/sbin/ifconfig/ifvlan.c,v 1.5 2005/03/04 00:11:11 cpressey Exp $ - * - * $FreeBSD: src/sbin/ifconfig/ifvlan.c,v 1.2 1999/08/28 00:13:09 peter Exp $ + * $DragonFly: src/sbin/ifconfig/ifvlan.c,v 1.6 2005/03/06 05:01:59 dillon Exp $ */ #include diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 3dcaa85606..a501fcf25c 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/share/man/man4/Makefile,v 1.15 2005/02/26 12:00:54 swildner Exp $ +# $DragonFly: src/share/man/man4/Makefile,v 1.16 2005/03/06 05:02:01 dillon Exp $ MAN= aac.4 \ adv.4 \ @@ -83,6 +83,7 @@ MAN= aac.4 \ ips.4 \ isp.4 \ ispfw.4 \ + iwi.4 \ joy.4 \ kame.4 \ keyboard.4 \ diff --git a/sys/dev/netif/Makefile b/sys/dev/netif/Makefile index 9bbb79b8fe..768d3caed8 100644 --- a/sys/dev/netif/Makefile +++ b/sys/dev/netif/Makefile @@ -1,7 +1,7 @@ -# $DragonFly: src/sys/dev/netif/Makefile,v 1.11 2005/01/10 19:37:23 joerg Exp $ +# $DragonFly: src/sys/dev/netif/Makefile,v 1.12 2005/03/06 05:01:52 dillon Exp $ # -SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx kue lge lnc \ +SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx iwi kue lge lnc \ mii_layer my nge nv owi pcn ray re rl sbni sbsh sf sis sk snc \ sr ste ti tl tx txp vr vx wb wi xe xl diff --git a/sys/dev/netif/iwi/Makefile b/sys/dev/netif/iwi/Makefile new file mode 100644 index 0000000000..3326aac768 --- /dev/null +++ b/sys/dev/netif/iwi/Makefile @@ -0,0 +1,17 @@ +# $DragonFly: src/sys/dev/netif/iwi/Makefile,v 1.1 2005/03/06 05:02:02 dillon Exp $ +# +KMOD = if_iwi +SRCS = if_iwi.c \ + device_if.h \ + bus_if.h \ + pci_if.h \ + opt_inet.h + +CFLAGS += -DIWI_DEBUG -DNBPFILTER=1 -DALTQ + +#WARNS?=6 + +opt_inet.h: + echo "#define INET 1" > ${.TARGET} + +.include diff --git a/sys/dev/netif/iwi/if_iwi.c b/sys/dev/netif/iwi/if_iwi.c new file mode 100644 index 0000000000..49f282fec4 --- /dev/null +++ b/sys/dev/netif/iwi/if_iwi.c @@ -0,0 +1,3053 @@ +/* + * Copyright (c) 2004, 2005 + * Damien Bergamini . + * Copyright (c) 2004, 2005 + * Andrew Atrens . + * + * 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 unmodified, 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 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 THE AUTHOR OR 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. + * + * $DragonFly: src/sys/dev/netif/iwi/if_iwi.c,v 1.1 2005/03/06 05:02:02 dillon Exp $ + */ + +#include "opt_inet.h" + +#include + +/*- + * Intel(R) PRO/Wireless 2200BG/2915ABG driver + * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef IPX +#include +#include +#endif + +#include +#include +#include +#include + +#include "if_iwireg.h" +#include "if_iwivar.h" + +#ifdef IWI_DEBUG +#define DPRINTF(x) if (sc->debug_level > 0) printf x +#define DPRINTFN(n, x) if (sc->debug_level >= (n)) printf x + +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +MODULE_DEPEND(iwi, pci, 1, 1, 1); +MODULE_DEPEND(iwi, wlan, 1, 1, 1); + +struct iwi_dump_buffer { + u_int32_t buf[128]; +}; + +struct iwi_ident { + u_int16_t vendor; + u_int16_t device; + const char *name; +}; + +static const struct iwi_ident iwi_ident_table[] = { + { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG MiniPCI" }, + { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG MiniPCI" }, + { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG MiniPCI" }, + + { 0, 0, NULL } +}; + +static const struct ieee80211_rateset iwi_rateset_11a = + { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; + +static const struct ieee80211_rateset iwi_rateset_11b = + { 4, { 2, 4, 11, 22 } }; + +static const struct ieee80211_rateset iwi_rateset_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + +static int iwi_dma_alloc(struct iwi_softc *); +static void iwi_release(struct iwi_softc *); +static int iwi_media_change(struct ifnet *); +static void iwi_media_status(struct ifnet *, struct ifmediareq *); +static u_int16_t iwi_read_prom_word(struct iwi_softc *, u_int8_t); +static int iwi_newstate(struct ieee80211com *, + enum ieee80211_state, int); +static void iwi_fix_channel(struct iwi_softc *, struct mbuf *); +static void iwi_frame_intr(struct iwi_softc *, + struct iwi_rx_buf *, int, struct iwi_frame *); +static void iwi_notification_intr(struct iwi_softc *, + struct iwi_notif *); +static void iwi_rx_intr(struct iwi_softc *); +static void iwi_tx_intr(struct iwi_softc *); +static void iwi_intr(void *); +static void iwi_dma_map_buf(void *, bus_dma_segment_t *, int, + bus_size_t, int); +static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); +static int iwi_cmd(struct iwi_softc *, u_int8_t, void *, u_int8_t, + int); +static int iwi_tx_start(struct ifnet *, struct mbuf *, + struct ieee80211_node *); +static void iwi_start(struct ifnet *); +static void iwi_watchdog(struct ifnet *); +static int iwi_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *cr); +static void iwi_stop_master(struct iwi_softc *); +static int iwi_reset(struct iwi_softc *); +static int iwi_load_ucode(struct iwi_softc *, void *, int); +static int iwi_load_firmware(struct iwi_softc *, void *, int); +static int iwi_cache_firmware(struct iwi_softc *, void *, int); +static void iwi_free_firmware(struct iwi_softc *); +static int iwi_config(struct iwi_softc *); +static int iwi_scan(struct iwi_softc *); +static int iwi_auth_and_assoc(struct iwi_softc *); +static void iwi_init(void *); +static void iwi_init_locked(void *); +static void iwi_stop(void *); +static void iwi_dump_fw_event_log(struct iwi_softc *sc); +static void iwi_dump_fw_error_log(struct iwi_softc *sc); +static u_int8_t iwi_find_station(struct iwi_softc *sc, u_int8_t *mac); +static int8_t iwi_cache_station(struct iwi_softc *sc, u_int8_t *mac); +static int iwi_adapter_config(struct iwi_softc *sc, int is_a, int cmd_wait); + +static int iwi_sysctl_bt_coexist(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_bg_autodetect(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_cts_to_self(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_antenna_diversity(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_dump_logs(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_neg_best_rates_first(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_disable_unicast_decryption(SYSCTL_HANDLER_ARGS); +static int iwi_sysctl_disable_multicast_decryption(SYSCTL_HANDLER_ARGS); + +static __inline u_int8_t MEM_READ_1(struct iwi_softc *sc, u_int32_t addr) +{ + CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); + return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); +} + +static __inline u_int32_t MEM_READ_4(struct iwi_softc *sc, u_int32_t addr) +{ + CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); + return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); +} + +static int iwi_probe(device_t); +static int iwi_attach(device_t); +static int iwi_detach(device_t); +static int iwi_shutdown(device_t); +static int iwi_suspend(device_t); +static int iwi_resume(device_t); + +static device_method_t iwi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, iwi_probe), + DEVMETHOD(device_attach, iwi_attach), + DEVMETHOD(device_detach, iwi_detach), + DEVMETHOD(device_shutdown, iwi_shutdown), + DEVMETHOD(device_suspend, iwi_suspend), + DEVMETHOD(device_resume, iwi_resume), + + { 0, 0 } +}; + +static driver_t iwi_driver = { + "iwi", + iwi_methods, + sizeof (struct iwi_softc), + 0, /* baseclasses */ + 0, /* refs */ + 0 /* ops */ +}; + +static devclass_t iwi_devclass; + +DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0); + +static int +iwi_probe(device_t dev) +{ + const struct iwi_ident *ident; + + for (ident = iwi_ident_table; ident->name != NULL; ident++) { + if (pci_get_vendor(dev) == ident->vendor && + pci_get_device(dev) == ident->device) { + device_set_desc(dev, ident->name); + return 0; + } + } + return ENXIO; +} + +static void +iwi_fw_monitor(void *arg) +{ + struct iwi_softc *sc = (struct iwi_softc *)arg; + int error, boff; + for ( ;; ) { + error = tsleep(IWI_FW_WAKE_MONITOR(sc), 0, "iwifwm", 0 ); + if ( error == 0 ) { + if ( sc->flags & IWI_FLAG_EXIT ) { + sc->flags &= ~( IWI_FLAG_EXIT ); + break; + } else if ( sc->flags & IWI_FLAG_RESET ) { + device_printf(sc->sc_dev, "firmware reset\n"); + for ( boff = 1; sc->flags & IWI_FLAG_RESET ; boff++ ) { + if ( sc->debug_level > 0 ) + iwi_dump_fw_error_log(sc); + iwi_init_locked(sc); + if ((sc->flags & IWI_FLAG_FW_INITED)) + sc->flags &= ~( IWI_FLAG_RESET ); + error = tsleep( IWI_FW_CMD_ACKED(sc), 0, + "iwirun", boff * hz ); + } + } + } + } + wakeup(IWI_FW_MON_EXIT(sc)); + kthread_exit(); +} + +static int +iwi_start_fw_monitor_thread( struct iwi_softc *sc ) +{ + if (kthread_create(iwi_fw_monitor, sc, &sc->event_thread, + "%s%d:fw-monitor", device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev))) { + device_printf (sc->sc_dev, + "unable to create firmware monitor thread.\n"); + return -1; + } + return 0; +} + +/* Base Address Register */ +#define IWI_PCI_BAR0 0x10 + +static int +iwi_attach(device_t dev) +{ + struct iwi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + u_int16_t val; + int error, rid, i; + + sc->sc_dev = dev; + + IWI_LOCK_INIT( &sc->sc_lock ); + IWI_LOCK_INIT( &sc->sc_intrlock ); + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + device_printf(dev, "chip is in D%d power mode " + "-- setting to D0\n", pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + } + + pci_write_config(dev, 0x41, 0, 1); + + /* enable bus-mastering */ + pci_enable_busmaster(dev); + + sc->num_stations = 0; + + /* map the register window */ + rid = IWI_PCI_BAR0; + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (sc->mem == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + goto fail; + } + + sc->sc_st = rman_get_bustag(sc->mem); + sc->sc_sh = rman_get_bushandle(sc->mem); + + rid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | + RF_SHAREABLE); + if (sc->irq == NULL) { + device_printf(dev, "could not allocate interrupt resource\n"); + goto fail; + } + + if (iwi_reset(sc) != 0) { + device_printf(dev, "could not reset adapter\n"); + goto fail; + } + + if (iwi_start_fw_monitor_thread(sc) ) { + device_printf(dev, "could not start f/w reset thread\n"); + goto fail; + } + + if (iwi_dma_alloc(sc) != 0) { + device_printf(dev, "could not allocate DMA resources\n"); + goto fail; + } + + ic->ic_phytype = IEEE80211_T_OFDM; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_state = IEEE80211_S_INIT; + + /* set device capabilities */ + ic->ic_caps = IEEE80211_C_IBSS | IEEE80211_C_PMGT | IEEE80211_C_WEP | + IEEE80211_C_TXPMGT | IEEE80211_C_SHPREAMBLE; + + /* read MAC address from EEPROM */ + val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); + ic->ic_myaddr[0] = val >> 8; + ic->ic_myaddr[1] = val & 0xff; + val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); + ic->ic_myaddr[2] = val >> 8; + ic->ic_myaddr[3] = val & 0xff; + val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); + ic->ic_myaddr[4] = val >> 8; + ic->ic_myaddr[5] = val & 0xff; + + if (pci_get_device(dev) != 0x4220) { + /* set supported .11a rates */ + ic->ic_sup_rates[IEEE80211_MODE_11A] = iwi_rateset_11a; + + /* set supported .11a channels */ + for (i = 36; i <= 64; i += 4) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + } + for (i = 149; i <= 165; i += 4) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + } + } + + /* set supported .11b and .11g rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = iwi_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = iwi_rateset_11g; + + /* set supported .11b and .11g channels (1 through 14) */ + for (i = 1; i <= 14; i++) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_channels[i].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + + /* default to authmode OPEN */ + sc->authmode = IEEE80211_AUTH_OPEN; + + /* IBSS channel undefined for now */ + ic->ic_ibss_chan = &ic->ic_channels[0]; + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = iwi_init_locked; + ifp->if_ioctl = iwi_ioctl; + ifp->if_start = iwi_start; + ifp->if_watchdog = iwi_watchdog; + ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); + ifq_set_ready(&ifp->if_snd); + + ieee80211_ifattach(ifp); + /* override state transition machine */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = iwi_newstate; + ieee80211_media_init(ifp, iwi_media_change, iwi_media_status); + + bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf); + + sc->sc_rxtap_len = sizeof sc->sc_rxtapu; + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof sc->sc_txtapu; + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT); + + /* + * Hook our interrupt after all initialization is complete + */ + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, + iwi_intr, sc, &sc->sc_ih); + if (error != 0) { + device_printf(dev, "could not set up interrupt\n"); + goto fail; + } + + /* + * Add sysctl knobs + * + * use -1 to indicate 'default / not set' + */ + + sc->enable_bg_autodetect = -1; + sc->enable_bt_coexist = -1; + sc->enable_cts_to_self = -1; + sc->antenna_diversity = -1; + sc->enable_neg_best_first = -1; + sc->disable_unicast_decryption = -1; + sc->disable_multicast_decryption = -1; + + sysctl_ctx_init(&sc->sysctl_ctx); + sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_hw), + OID_AUTO, + device_get_nameunit(dev), + CTLFLAG_RD, + 0, ""); + + if (sc->sysctl_tree == NULL) { + error = EIO; + goto fail; + } + + SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "debug", CTLFLAG_RW, &sc->debug_level, 0, + "Set driver debug level (0 = off)"); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "cts_to_self", CTLTYPE_INT|CTLFLAG_RW, + (void *)sc, 0, iwi_sysctl_cts_to_self, "I", + "Enable cts to self [0 = Off] [1 = On] [-1 = Auto]" ); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "antenna_diversity", CTLTYPE_INT|CTLFLAG_RW, + (void *)sc, 0, iwi_sysctl_antenna_diversity, + "I", "Set antenna diversity [0 = Both] " + "[1 = Antenna A] [3 = Antenna B] [-1 = Auto]" ); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "bluetooth_coexist", CTLTYPE_INT|CTLFLAG_RW, + (void *)sc, 0, iwi_sysctl_bt_coexist, + "I", "Enable bluetooth coexistence heuristics " + "[0 = Off] [1 = On] [-1 = Auto]" ); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "bg_autodetect", CTLTYPE_INT|CTLFLAG_RW, + (void *)sc, 0, iwi_sysctl_bg_autodetect, + "I", "Set b/g autodetect [0 = Off] [1 = On] [-1 = Auto]" ); + + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "radio", + CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", + "Radio transmitter switch"); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "stats", + CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, + "S,iwi_dump_buffer", "statistics"); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "firmware_logs", CTLTYPE_INT|CTLFLAG_RW, + (void *)sc, 0, iwi_sysctl_dump_logs, "I", "Dump firmware logs"); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "neg_best_rates_first", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, + iwi_sysctl_neg_best_rates_first, "I", + "Negotiate highest rates first."); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "disable_unicast_decrypt", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, + iwi_sysctl_disable_unicast_decryption, "I", + "Disable unicast decryption."); + + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, + "disable_multicast_decrypt", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, + iwi_sysctl_disable_multicast_decryption, "I", + "Disable multicast decryption."); + + return 0; + +fail: iwi_detach(dev); + return ENXIO; +} + +static int +iwi_detach(device_t dev) +{ + struct iwi_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->sc_ic.ic_if; + IWI_LOCK_INFO; + IWI_IPLLOCK_INFO; + + sc->flags |= IWI_FLAG_EXIT; + wakeup(IWI_FW_WAKE_MONITOR(sc)); /* Stop firmware monitor. */ + + (void) tsleep(IWI_FW_MON_EXIT(sc), 0, "iwiexi", 10 * hz ); + + IWI_LOCK(sc); + IWI_IPLLOCK(sc); + + iwi_stop(sc); + iwi_free_firmware(sc); + + if ( sc->sysctl_tree ) { + crit_enter(); + sysctl_ctx_free(&sc->sysctl_ctx); + crit_exit(); + sc->sysctl_tree = 0; + } + + IWI_IPLUNLOCK(sc); + IWI_UNLOCK(sc); + + bpfdetach(ifp); + + ieee80211_ifdetach(ifp); + + iwi_release(sc); + + if (sc->irq != NULL) { + bus_teardown_intr(dev, sc->irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + } + + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, IWI_PCI_BAR0, + sc->mem); + + IWI_LOCK_DESTROY(&(sc->sc_lock)); + IWI_LOCK_DESTROY(&(sc->sc_intrlock)); + + return 0; +} + +static int +iwi_dma_alloc(struct iwi_softc *sc) +{ + int i, error; + + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + MAXBSIZE, 128, + BUS_SPACE_MAXSIZE_32BIT, + BUS_DMA_ALLOCNOW, + &sc->iwi_parent_tag ); + if (error != 0) { + device_printf(sc->sc_dev, "could not create parent tag\n"); + goto fail; + } + /* + * Allocate and map Tx ring + */ + error = bus_dma_tag_create(sc->iwi_parent_tag, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, + sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, 1, + sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, + BUS_DMA_ALLOCNOW, &sc->tx_ring_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(sc->tx_ring_dmat,(void **) &sc->tx_desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx_ring_map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not allocate tx ring DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(sc->tx_ring_dmat, sc->tx_ring_map, + sc->tx_desc, sizeof (struct iwi_tx_desc) * IWI_TX_RING_SIZE, + iwi_dma_map_addr, &sc->tx_ring_pa, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load tx ring DMA map\n"); + goto fail; + } + + /* + * Allocate and map command ring + */ + error = bus_dma_tag_create(sc->iwi_parent_tag, 4, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, + sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, 1, + sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, + BUS_DMA_ALLOCNOW, + &sc->cmd_ring_dmat); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create command ring DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(sc->cmd_ring_dmat, (void **)&sc->cmd_desc, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->cmd_ring_map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not allocate command ring DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(sc->cmd_ring_dmat, sc->cmd_ring_map, + sc->cmd_desc, sizeof (struct iwi_cmd_desc) * IWI_CMD_RING_SIZE, + iwi_dma_map_addr, &sc->cmd_ring_pa, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load command ring DMA map\n"); + goto fail; + } + + /* + * Allocate Tx buffers DMA maps + */ + error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWI_MAX_NSEG, MCLBYTES, + BUS_DMA_ALLOCNOW, &sc->tx_buf_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); + goto fail; + } + + for (i = 0; i < IWI_TX_RING_SIZE; i++) { + error = bus_dmamap_create(sc->tx_buf_dmat, 0, + &sc->tx_buf[i].map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create tx buf DMA map"); + goto fail; + } + } + + /* + * Allocate and map Rx buffers + */ + error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, + BUS_DMA_ALLOCNOW, &sc->rx_buf_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); + goto fail; + } + + for (i = 0; i < IWI_RX_RING_SIZE; i++) { + + error = bus_dmamap_create(sc->rx_buf_dmat, 0, + &sc->rx_buf[i].map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create rx buf DMA map"); + goto fail; + } + + sc->rx_buf[i].m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (sc->rx_buf[i].m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(sc->rx_buf_dmat, sc->rx_buf[i].map, + mtod(sc->rx_buf[i].m, void *), MCLBYTES, iwi_dma_map_addr, + &sc->rx_buf[i].physaddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load rx buf DMA map"); + goto fail; + } + } + + return 0; + +fail: iwi_release(sc); + return error; +} + +static void +iwi_release(struct iwi_softc *sc) +{ + int i; + + if (sc->tx_ring_dmat != NULL) { + if (sc->tx_desc != NULL) { + bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx_ring_dmat, sc->tx_ring_map); + bus_dmamem_free(sc->tx_ring_dmat, sc->tx_desc, + sc->tx_ring_map); + } + bus_dma_tag_destroy(sc->tx_ring_dmat); + } + + if (sc->cmd_ring_dmat != NULL) { + if (sc->cmd_desc != NULL) { + bus_dmamap_sync(sc->cmd_ring_dmat, sc->cmd_ring_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->cmd_ring_dmat, sc->cmd_ring_map); + bus_dmamem_free(sc->cmd_ring_dmat, sc->cmd_desc, + sc->cmd_ring_map); + } + bus_dma_tag_destroy(sc->cmd_ring_dmat); + } + + if (sc->tx_buf_dmat != NULL) { + for (i = 0; i < IWI_TX_RING_SIZE; i++) { + if (sc->tx_buf[i].m != NULL) { + bus_dmamap_sync(sc->tx_buf_dmat, + sc->tx_buf[i].map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx_buf_dmat, + sc->tx_buf[i].map); + m_freem(sc->tx_buf[i].m); + } + bus_dmamap_destroy(sc->tx_buf_dmat, sc->tx_buf[i].map); + } + bus_dma_tag_destroy(sc->tx_buf_dmat); + } + + if (sc->rx_buf_dmat != NULL) { + for (i = 0; i < IWI_RX_RING_SIZE; i++) { + if (sc->rx_buf[i].m != NULL) { + bus_dmamap_sync(sc->rx_buf_dmat, + sc->rx_buf[i].map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->rx_buf_dmat, + sc->rx_buf[i].map); + m_freem(sc->rx_buf[i].m); + } + bus_dmamap_destroy(sc->rx_buf_dmat, sc->rx_buf[i].map); + } + bus_dma_tag_destroy(sc->rx_buf_dmat); + } + if ( sc->iwi_parent_tag != NULL ) { + bus_dma_tag_destroy(sc->iwi_parent_tag); + } +} + +static int +iwi_shutdown(device_t dev) +{ + struct iwi_softc *sc = device_get_softc(dev); + IWI_LOCK_INFO; + + IWI_LOCK(sc); + + iwi_stop(sc); + + IWI_UNLOCK(sc); + + return 0; +} + +static int +iwi_suspend(device_t dev) +{ + struct iwi_softc *sc = device_get_softc(dev); + + IWI_LOCK_INFO; + + IWI_LOCK(sc); + + iwi_stop(sc); + + IWI_UNLOCK(sc); + + return 0; +} + +static int +iwi_resume(device_t dev) +{ + struct iwi_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->sc_ic.ic_if; + IWI_LOCK_INFO; + + IWI_LOCK(sc); + + pci_write_config(dev, 0x41, 0, 1); + + if (ifp->if_flags & IFF_UP) { + ifp->if_init(ifp->if_softc); + if (ifp->if_flags & IFF_RUNNING) + ifp->if_start(ifp); + } + + IWI_UNLOCK(sc); + + return 0; +} + +static int +iwi_media_change(struct ifnet *ifp) +{ + struct iwi_softc *sc = ifp->if_softc; + int error = 0; + IWI_LOCK_INFO; + + IWI_LOCK(sc); + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) { + IWI_UNLOCK(sc); + return error; + } + error = 0; /* clear ENETRESET */ + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)){ + iwi_init(sc); + error = tsleep( IWI_FW_CMD_ACKED(sc), 0, "iwirun", hz ); + } + + + IWI_UNLOCK(sc); + + return error; +} + +static void +iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct iwi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; +#define N(a) (sizeof (a) / sizeof (a[0])) + static const struct { + u_int32_t val; + int rate; + } rates[] = { + { IWI_RATE_DS1, 2 }, + { IWI_RATE_DS2, 4 }, + { IWI_RATE_DS5, 11 }, + { IWI_RATE_DS11, 22 }, + { IWI_RATE_OFDM6, 12 }, + { IWI_RATE_OFDM9, 18 }, + { IWI_RATE_OFDM12, 24 }, + { IWI_RATE_OFDM18, 36 }, + { IWI_RATE_OFDM24, 48 }, + { IWI_RATE_OFDM36, 72 }, + { IWI_RATE_OFDM48, 96 }, + { IWI_RATE_OFDM54, 108 }, + }; + u_int32_t val, i; + int rate; + + imr->ifm_status = IFM_AVALID; + imr->ifm_active = IFM_IEEE80211; + if (ic->ic_state == IEEE80211_S_RUN) + imr->ifm_status |= IFM_ACTIVE; + + /* read current transmission rate from adapter */ + val = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); + + /* convert rate to 802.11 rate */ + for (i = 0; i < N(rates) && rates[i].val != val ; i++); + rate = (i < N(rates)) ? rates[i].rate : 0; + + imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + break; + + case IEEE80211_M_IBSS: + imr->ifm_active |= IFM_IEEE80211_ADHOC; + break; + + case IEEE80211_M_MONITOR: + imr->ifm_active |= IFM_IEEE80211_MONITOR; + break; + + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + /* should not get there */ + break; + } +#undef N +} + +static int +iwi_disassociate( struct iwi_softc *sc ) +{ + sc->assoc.type = 2; /* DISASSOCIATE */ + return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &sc->assoc, sizeof sc->assoc, 0); +} + + +static int +iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg __unused) +{ + struct iwi_softc *sc = ic->ic_softc; + + switch (nstate) { + case IEEE80211_S_SCAN: + if ( sc->flags & IWI_FLAG_ASSOCIATED ) { + sc->flags &= ~( IWI_FLAG_ASSOCIATED ); + iwi_disassociate(sc); + (void) tsleep( IWI_FW_DEASSOCIATED(sc), + 0, "iwisca", hz ); + + } + if ( !(sc->flags & IWI_FLAG_SCANNING) && + !(sc->flags & IWI_FLAG_RF_DISABLED) ) { + iwi_scan(sc); + } + break; + + case IEEE80211_S_AUTH: + if ( sc->flags & IWI_FLAG_ASSOCIATED ) { + sc->flags &= ~( IWI_FLAG_ASSOCIATED ); + iwi_disassociate(sc); + (void) tsleep( IWI_FW_DEASSOCIATED(sc), 0, + "iwiaut", hz ); + + } + if ( iwi_auth_and_assoc(sc) != 0 ) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + + case IEEE80211_S_RUN: + if (sc->flags & IWI_FLAG_SCAN_COMPLETE) { + sc->flags &= ~(IWI_FLAG_SCAN_COMPLETE); + if (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_MONITOR ) { + /* + * In IBSS mode, following an end_scan + * the ieee80211 stack state machine transitions + * straight to 'run' state. This is out of + * step with the firmware which requires + * an association first. Flip our state from + * RUN back to AUTH to allow us to tell the + * firmware to associate. + */ + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + } + } + break; + + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_INIT: + sc->flags &= ~( IWI_FLAG_SCANNING | IWI_FLAG_ASSOCIATED ); + break; + } + + ic->ic_state = nstate; + return 0; +} + +/* + * Read 16 bits at address 'addr' from the serial EEPROM. + * DON'T PLAY WITH THIS CODE UNLESS YOU KNOW *EXACTLY* WHAT YOU'RE DOING! + */ +static u_int16_t +iwi_read_prom_word(struct iwi_softc *sc, u_int8_t addr) +{ + u_int32_t tmp; + u_int16_t val; + int n; + + /* Clock C once before the first command */ + IWI_EEPROM_CTL(sc, 0); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + + /* Write start bit (1) */ + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); + + /* Write READ opcode (10) */ + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); + + /* Write address A7-A0 */ + for (n = 7; n >= 0; n--) { + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | + (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | + (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); + } + + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + + /* Read data Q15-Q0 */ + val = 0; + for (n = 15; n >= 0; n--) { + IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); + val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; + } + + IWI_EEPROM_CTL(sc, 0); + + /* Clear Chip Select and clock C */ + IWI_EEPROM_CTL(sc, IWI_EEPROM_S); + IWI_EEPROM_CTL(sc, 0); + IWI_EEPROM_CTL(sc, IWI_EEPROM_C); + + return be16toh(val); +} + +/* + * XXX: Hack to set the current channel to the value advertised in beacons or + * probe responses. Only used during AP detection. + */ +static void +iwi_fix_channel(struct iwi_softc *sc, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + u_int8_t subtype; + u_int8_t *frm, *efrm; + + wh = mtod(m, struct ieee80211_frame *); + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) + return; + + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && + subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) + return; + + /* + * Cache station entries from beacons and probes. + */ + if ( iwi_find_station(sc, wh->i_addr2) == 0xff ) + iwi_cache_station(sc, wh->i_addr2); + + frm = (u_int8_t *)(wh + 1); + efrm = mtod(m, u_int8_t *) + m->m_len; + + frm += 12; /* skip tstamp, bintval and capinfo fields */ +#if 0 + { /* XXX - debugging code */ + u_int8_t *ptr; + u_int32_t cnt; + printf("Frame -->"); + for ( ptr = frm, cnt = 0 ; ptr < efrm ; ptr++, cnt++ ) { + if ( cnt % 8 == 0 ) + printf("\n"); + printf("0x%-2.2x ", *ptr); + } + printf("<-- End Frame\n"); + } +#endif + + while (frm < efrm) { + if (*frm == IEEE80211_ELEMID_DSPARMS) +#if IEEE80211_CHAN_MAX < 255 + if (frm[2] <= IEEE80211_CHAN_MAX) +#endif + ic->ic_bss->ni_chan = &ic->ic_channels[frm[2]]; + + frm += frm[1] + 2; /* advance to the next tag */ + } +} + +static void +iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_buf *buf, int i, + struct iwi_frame *frame) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct mbuf *m; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + int error; + + DPRINTFN(5, ("RX!DATA!%u!%u!%u\n", le16toh(frame->len), frame->chan, + frame->rssi_dbm)); + + if (le16toh(frame->len) < sizeof (struct ieee80211_frame_min) || + le16toh(frame->len) > MCLBYTES) { + device_printf(sc->sc_dev, "bad frame length\n"); + return; + } + + bus_dmamap_unload(sc->rx_buf_dmat, buf->map); + + /* Finalize mbuf */ + m = buf->m; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + + sizeof (struct iwi_frame) + le16toh(frame->len); + + m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* + * Hardware decrypts the frame itself but leaves the WEP bit + * set in the 802.11 header and don't remove the iv and crc + * fields + */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + bcopy(wh, (char *)wh + IEEE80211_WEP_IVLEN + + IEEE80211_WEP_KIDLEN, sizeof (struct ieee80211_frame)); + m_adj(m, IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN); + m_adj(m, -IEEE80211_WEP_CRCLEN); + wh = mtod(m, struct ieee80211_frame *); + } + + if (sc->sc_drvbpf != NULL) { + struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + tap->wr_rate = frame->rate; + tap->wr_chan_freq = + htole16(ic->ic_channels[frame->chan].ic_freq); + tap->wr_chan_flags = + htole16(ic->ic_channels[frame->chan].ic_flags); + tap->wr_antsignal = frame->signal; + tap->wr_antnoise = frame->noise; + tap->wr_antenna = frame->antenna; + + bpf_ptap(sc->sc_drvbpf, m, tap, sc->sc_rxtap_len); + } + + if (ic->ic_state == IEEE80211_S_SCAN) + iwi_fix_channel(sc, m); + + if (ic->ic_opmode != IEEE80211_M_STA) { + ni = ieee80211_find_node(ic, wh->i_addr2); + if (ni == NULL) + ni = ieee80211_ref_node(ic->ic_bss); + } else + ni = ieee80211_ref_node(ic->ic_bss); + + /* Send the frame to the upper layer */ + ieee80211_input(ifp, m, ni, IWI_RSSIDBM2RAW(frame->rssi_dbm), 0); + + if (ni == ic->ic_bss) + ieee80211_unref_node(&ni); + else + ieee80211_free_node(ic, ni); + + buf->m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (buf->m == NULL) { + device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); + return; + } + + error = bus_dmamap_load(sc->rx_buf_dmat, buf->map, mtod(buf->m, void *), + MCLBYTES, iwi_dma_map_addr, &buf->physaddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load rx buf DMA map\n"); + m_freem(buf->m); + buf->m = NULL; + return; + } + + CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4, buf->physaddr); +} + +static void +iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct iwi_notif_scan_channel *chan; + struct iwi_notif_scan_complete *scan; + struct iwi_notif_authentication *auth; + struct iwi_notif_association *assoc; + + switch (notif->type) { + case IWI_NOTIF_TYPE_SCAN_CHANNEL: + chan = (struct iwi_notif_scan_channel *)(notif + 1); + + DPRINTFN(2, ("Scan channel (%u)\n", chan->nchan)); + break; + + case IWI_NOTIF_TYPE_SCAN_COMPLETE: + scan = (struct iwi_notif_scan_complete *)(notif + 1); + + DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, + scan->status)); + + sc->flags &= ~(IWI_FLAG_SCANNING); + sc->flags |= IWI_FLAG_SCAN_COMPLETE; + + if ( sc->flags & IWI_FLAG_SCAN_ABORT ) { + sc->flags &= ~(IWI_FLAG_SCAN_ABORT); + wakeup(IWI_FW_SCAN_COMPLETED(sc)); + } else { + ieee80211_end_scan(ifp); + wakeup(IWI_FW_SCAN_COMPLETED(sc)); + } + break; + + case IWI_NOTIF_TYPE_AUTHENTICATION: + auth = (struct iwi_notif_authentication *)(notif + 1); + + DPRINTFN(2, ("Authentication (%u)\n", auth->state)); + + switch (auth->state) { + case IWI_AUTHENTICATED: + ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); + break; + + case IWI_DEAUTHENTICATED: + ieee80211_begin_scan(ifp);/* not necessary */ + break; + + default: + device_printf(sc->sc_dev, + "unknown authentication state %u\n", auth->state); + } + break; + + case IWI_NOTIF_TYPE_ASSOCIATION: + assoc = (struct iwi_notif_association *)(notif + 1); + + DPRINTFN(2, ("Association (%u, %u)\n", assoc->state, + assoc->status)); + + switch (assoc->state) { + case IWI_ASSOCIATED: + sc->flags |= IWI_FLAG_ASSOCIATED; + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + break; + + case IWI_DEASSOCIATED: + sc->flags &= ~(IWI_FLAG_ASSOCIATED); + wakeup(IWI_FW_DEASSOCIATED(sc)); + ieee80211_begin_scan(ifp);/* probably not necessary */ + break; + + default: + device_printf(sc->sc_dev, + "unknown association state %u\n", assoc->state); + } + break; + + case IWI_NOTIF_TYPE_CALIBRATION: + DPRINTFN(5, ("Notification calib (%u)\n", notif->type)); + break; + case IWI_NOTIF_TYPE_BEACON: + DPRINTFN(5, ("Notification beacon (%u)\n", notif->type)); + break; + case IWI_NOTIF_TYPE_NOISE: + DPRINTFN(5, ("Notification noise (%u)\n", notif->type)); + break; + + default: + device_printf(sc->sc_dev, "unknown notification type %u\n", + notif->type); + } +} + +static void +iwi_rx_intr(struct iwi_softc *sc) +{ + struct iwi_rx_buf *buf; + struct iwi_hdr *hdr; + u_int32_t r, i; + + r = CSR_READ_4(sc, IWI_CSR_RX_READ_INDEX); + + for (i = (sc->rx_cur + 1) % IWI_RX_RING_SIZE; i != r; + i = (i + 1) % IWI_RX_RING_SIZE) { + + buf = &sc->rx_buf[i]; + + bus_dmamap_sync(sc->rx_buf_dmat, buf->map, + BUS_DMASYNC_POSTREAD); + + hdr = mtod(buf->m, struct iwi_hdr *); + + switch (hdr->type) { + case IWI_HDR_TYPE_FRAME: + iwi_frame_intr(sc, buf, i, + (struct iwi_frame *)(hdr + 1)); + break; + + case IWI_HDR_TYPE_NOTIF: + iwi_notification_intr(sc, + (struct iwi_notif *)(hdr + 1)); + break; + + default: + device_printf(sc->sc_dev, "unknown hdr type %u\n", + hdr->type); + } + } + + /* Tell the firmware what we have processed */ + sc->rx_cur = (r == 0) ? IWI_RX_RING_SIZE - 1 : r - 1; + CSR_WRITE_4(sc, IWI_CSR_RX_WRITE_INDEX, sc->rx_cur); +} + +static void +iwi_tx_intr(struct iwi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct iwi_tx_buf *buf; + u_int32_t r, i; + + r = CSR_READ_4(sc, IWI_CSR_TX1_READ_INDEX); +#if notyet + bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, BUS_DMASYNC_POSTWRITE); +#endif + + for (i = (sc->tx_old + 1) % IWI_TX_RING_SIZE; i != r; + i = (i + 1) % IWI_TX_RING_SIZE) { + + buf = &sc->tx_buf[i]; + + bus_dmamap_sync(sc->tx_buf_dmat, buf->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx_buf_dmat, buf->map); + m_freem(buf->m); + buf->m = NULL; + if (buf->ni != ic->ic_bss) + ieee80211_free_node(ic, buf->ni); + buf->ni = NULL; + + sc->tx_queued--; + + /* kill watchdog timer */ + sc->sc_tx_timer = 0; + } + + /* Remember what the firmware has processed */ + sc->tx_old = (r == 0) ? IWI_TX_RING_SIZE - 1 : r - 1; + + /* Call start() since some buffer descriptors have been released */ + ifp->if_flags &= ~IFF_OACTIVE; + (*ifp->if_start)(ifp); +} + +static void +iwi_intr(void *arg) +{ + struct iwi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + u_int32_t r; + IWI_LOCK_INFO; + IWI_IPLLOCK_INFO; + + IWI_IPLLOCK(sc); + + if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { + IWI_IPLUNLOCK(sc); + return; + } + + /* Disable interrupts */ + CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); + + DPRINTFN(8, ("INTR!0x%08x\n", r)); + + sc->flags &= ~(IWI_FLAG_RF_DISABLED); + + if ( r & IWI_INTR_FATAL_ERROR ) { + if ( !(sc->flags & (IWI_FLAG_RESET | IWI_FLAG_EXIT))) { + sc->flags |= IWI_FLAG_RESET; + wakeup(IWI_FW_WAKE_MONITOR(sc)); + } + } + + if (r & IWI_INTR_PARITY_ERROR) { + device_printf(sc->sc_dev, "fatal error\n"); + sc->sc_ic.ic_if.if_flags &= ~IFF_UP; + IWI_LOCK(sc); + iwi_stop(sc); + IWI_UNLOCK(sc); + } + + if (r & IWI_INTR_FW_INITED) { + if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) + wakeup(IWI_FW_INITIALIZED(sc)); + } + + if (r & IWI_INTR_RADIO_OFF) { + DPRINTF(("radio transmitter off\n")); + sc->sc_ic.ic_if.if_flags &= ~IFF_UP; + IWI_LOCK(sc); + iwi_stop(sc); + IWI_UNLOCK(sc); + sc->flags |= IWI_FLAG_RF_DISABLED; + } + + if (r & IWI_INTR_RX_TRANSFER) + iwi_rx_intr(sc); + + if (r & IWI_INTR_CMD_TRANSFER) + wakeup(IWI_FW_CMD_ACKED(sc)); + + if (r & IWI_INTR_TX1_TRANSFER) + iwi_tx_intr(sc); + + if (r & ~(IWI_HANDLED_INTR_MASK)) + device_printf(sc->sc_dev, + "unhandled interrupt(s) INTR!0x%08x\n", + r & ~(IWI_HANDLED_INTR_MASK)); + + /* Acknowledge interrupts */ + CSR_WRITE_4(sc, IWI_CSR_INTR, r); + + /* Re-enable interrupts */ + CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); + + IWI_IPLUNLOCK(sc); + + if ((ifp->if_flags & IFF_RUNNING) && !ifq_is_empty(&ifp->if_snd)) + iwi_start(ifp); +} + +struct iwi_dma_mapping { + bus_dma_segment_t segs[IWI_MAX_NSEG]; + int nseg; + bus_size_t mapsize; +}; + +static void +iwi_dma_map_buf(void *arg, bus_dma_segment_t *segs, int nseg, + bus_size_t mapsize, int error) +{ + struct iwi_dma_mapping *map = arg; + + if (error != 0) + return; + + KASSERT(nseg <= IWI_MAX_NSEG, ("too many DMA segments %d", nseg)); + + bcopy(segs, map->segs, nseg * sizeof (bus_dma_segment_t)); + map->nseg = nseg; + map->mapsize = mapsize; +} + +static void +iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg __unused, int error) +{ + if (error != 0) { + printf("iwi: fatal DMA mapping error !!!\n"); + return; + } + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +static int +iwi_cmd(struct iwi_softc *sc, u_int8_t type, void *data, u_int8_t len, + int async) +{ + struct iwi_cmd_desc *desc; + + DPRINTFN(2, ("TX!CMD!%u!%u\n", type, len)); + + desc = &sc->cmd_desc[sc->cmd_cur]; + desc->hdr.type = IWI_HDR_TYPE_COMMAND; + desc->hdr.flags = IWI_HDR_FLAG_IRQ; + desc->type = type; + desc->len = len; + bcopy(data, desc->data, len); + + bus_dmamap_sync(sc->cmd_ring_dmat, sc->cmd_ring_map, + BUS_DMASYNC_PREWRITE); + + sc->cmd_cur = (sc->cmd_cur + 1) % IWI_CMD_RING_SIZE; + CSR_WRITE_4(sc, IWI_CSR_CMD_WRITE_INDEX, sc->cmd_cur); + + return async ? 0 : tsleep( IWI_FW_CMD_ACKED(sc), 0, "iwicmd", hz); +} + +static int +iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct iwi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct iwi_tx_buf *buf; + struct iwi_tx_desc *desc; + struct iwi_dma_mapping map; + struct mbuf *mnew; + u_int32_t id = 0; + int error, i; + IWI_IPLLOCK_INFO; /* XXX still need old ipl locking mech. here */ + IWI_IPLLOCK(sc); + + if (sc->sc_drvbpf != NULL) { + struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + + bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len); + } + + buf = &sc->tx_buf[sc->tx_cur]; + desc = &sc->tx_desc[sc->tx_cur]; + + wh = mtod(m0, struct ieee80211_frame *); + + if ( (id = iwi_find_station( sc, wh->i_addr1 ) ) == 0xff ) + id = iwi_cache_station( sc, wh->i_addr1 ); + + bzero( desc, sizeof (struct iwi_tx_desc) ); + desc->station_number = id; + + /* trim IEEE802.11 header */ + m_adj(m0, sizeof (struct ieee80211_frame)); + + error = bus_dmamap_load_mbuf(sc->tx_buf_dmat, buf->map, m0, + iwi_dma_map_buf, &map, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", + error); + m_freem(m0); + IWI_IPLUNLOCK(sc); + return error; + } + if (error != 0) { + mnew = m_defrag(m0, MB_DONTWAIT); + if (mnew == NULL) { + device_printf(sc->sc_dev, + "could not defragment mbuf\n"); + m_freem(m0); + IWI_IPLUNLOCK(sc); + return ENOBUFS; + } + m0 = mnew; + + error = bus_dmamap_load_mbuf(sc->tx_buf_dmat, buf->map, m0, + iwi_dma_map_buf, &map, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "could not map mbuf (error %d)\n", error); + m_freem(m0); + IWI_IPLUNLOCK(sc); + return error; + } + } + + buf->m = m0; + buf->ni = ni; + + desc->hdr.type = IWI_HDR_TYPE_DATA; + desc->hdr.flags = IWI_HDR_FLAG_IRQ; + desc->cmd = IWI_DATA_CMD_TX; + desc->len = htole16(m0->m_pkthdr.len); + desc->flags = 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + desc->flags |= IWI_DATA_FLAG_NEED_ACK; + } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) + desc->flags |= IWI_DATA_FLAG_NEED_ACK; + + if (ic->ic_flags & IEEE80211_F_WEPON) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + desc->wep_txkey = ic->ic_wep_txkey; + } else + desc->flags |= IWI_DATA_FLAG_NO_WEP; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + desc->flags |= IWI_DATA_FLAG_SHPREAMBLE; + + bcopy(wh, &desc->wh, sizeof (struct ieee80211_frame)); + desc->nseg = htole32(map.nseg); + for (i = 0; i < map.nseg; i++) { + desc->seg_addr[i] = htole32(map.segs[i].ds_addr); + desc->seg_len[i] = htole32(map.segs[i].ds_len); + } + + bus_dmamap_sync(sc->tx_buf_dmat, buf->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->tx_ring_dmat, sc->tx_ring_map, + BUS_DMASYNC_PREWRITE); + + DPRINTFN(5, ("TX!DATA!%u!%u\n", desc->len, desc->nseg)); + + /* Inform firmware about this new packet */ + sc->tx_queued++; + sc->tx_cur = (sc->tx_cur + 1) % IWI_TX_RING_SIZE; + CSR_WRITE_4(sc, IWI_CSR_TX1_WRITE_INDEX, sc->tx_cur); + + IWI_IPLUNLOCK(sc); + return 0; +} + +static void +iwi_start(struct ifnet *ifp) +{ + struct iwi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m0; + struct ieee80211_node *ni; + + if (ic->ic_state != IEEE80211_S_RUN) { + return; + } + + for (;;) { + m0 = ifq_poll(&ifp->if_snd); + if (m0 == NULL) + break; + + m0 = ifq_dequeue(&ifp->if_snd); + + if (sc->tx_queued >= IWI_TX_RING_SIZE - 4) { + IF_PREPEND(&ifp->if_snd, m0); + ifp->if_flags |= IFF_OACTIVE; + break; + } + +#if NBPFILTER > 0 + BPF_MTAP(ifp, m0); +#endif + + m0 = ieee80211_encap(ifp, m0, &ni); + if (m0 == NULL) + continue; + + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0); + + if (iwi_tx_start(ifp, m0, ni) != 0) { + if (ni != NULL && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + break; + } + + /* start watchdog timer */ + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } + +} + +static void +iwi_watchdog(struct ifnet *ifp) +{ + struct iwi_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + if_printf(ifp, "device timeout\n"); + wakeup(IWI_FW_WAKE_MONITOR(sc)); + return; + } + ifp->if_timer = 1; + } + + ieee80211_watchdog(ifp); +} + + +static int +iwi_wi_ioctl_get(struct ifnet *ifp, caddr_t data) +{ + struct wi_req wreq; + struct ifreq *ifr; + struct iwi_softc *sc; + int error; + + sc = ifp->if_softc; + ifr = (struct ifreq *)data; + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + return (error); + + switch (wreq.wi_type) { + case WI_RID_READ_APS: + ieee80211_begin_scan(ifp); + (void) tsleep(IWI_FW_SCAN_COMPLETED(sc), + PPAUSE|PCATCH, "ssidscan", hz * 2); + ieee80211_end_scan(ifp); + break; + default: + error = ENOTTY; + break; + } + return (error); +} + + + + +static int +iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) +{ + struct iwi_softc *sc = ifp->if_softc; + struct ifreq *ifr; + struct ieee80211req *ireq; + struct ifaddr *ifa; + int error = 0; + IWI_LOCK_INFO; + + IWI_LOCK(sc); + + switch (cmd) { + case SIOCSIFADDR: + /* + * Handle this here instead of in net80211_ioctl.c + * so that we can lock (IWI_LOCK) the call to + * iwi_init(). + */ + ifa = (struct ifaddr *) data; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + if ((ifp->if_flags & IFF_UP) == 0) { + ifp->if_flags |= IFF_UP; + ifp->if_init(ifp->if_softc); + } + arp_ifinit(ifp, ifa); + break; +#endif +#ifdef IPX +#warning "IPX support has not been tested" + /* + * XXX - This code is probably wrong, + * but has been copied many times. + */ + case AF_IPX: { + struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); + struct arpcom *ac = (struct arpcom *)ifp; + + if (ipx_nullhost(*ina)) + ina->x_host = *(union ipx_host *) ac->ac_enaddr; + else + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) ac->ac_enaddr, + sizeof(ac->ac_enaddr)); + /* fall thru... */ + } +#endif + default: + if ((ifp->if_flags & IFF_UP) == 0) { + ifp->if_flags |= IFF_UP; + ifp->if_init(ifp->if_softc); + } + break; + } + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) { + iwi_init(sc); + error = tsleep(IWI_FW_CMD_ACKED(sc), 0, + "iwirun", hz); + } + } else { + if (ifp->if_flags & IFF_RUNNING) { + iwi_stop(sc); + } + } + break; + + case SIOCSLOADFW: + case SIOCSLOADIBSSFW: + /* only super-user can do that! */ + if ((error = suser(curthread)) != 0) + break; + + ifr = (struct ifreq *)data; + error = iwi_cache_firmware(sc, ifr->ifr_data, + (cmd == SIOCSLOADIBSSFW) ? 1 : 0); + break; + + case SIOCSKILLFW: + /* only super-user can do that! */ + if ((error = suser(curthread)) != 0) + break; + + ifp->if_flags &= ~IFF_UP; + iwi_stop(sc); + iwi_free_firmware(sc); + break; + + case SIOCG80211: + ireq = (struct ieee80211req *)data; + switch (ireq->i_type) { + case IEEE80211_IOC_AUTHMODE: + ireq->i_val = sc->authmode; + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data, cr); + } + break; + + case SIOCS80211: + /* only super-user can do that! */ + if ((error = suser(curthread)) != 0) + break; + + ireq = (struct ieee80211req *)data; + switch (ireq->i_type) { + case IEEE80211_IOC_AUTHMODE: + sc->authmode = ireq->i_val; + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data, cr); + } + break; + case SIOCGIFGENERIC: + if (sc->flags & IWI_FLAG_FW_INITED) { + error = iwi_wi_ioctl_get(ifp, data); + if (! error) + error = ieee80211_ioctl(ifp, cmd, data, cr); + } else + error = ENOTTY; + if (error != ENOTTY) + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data, cr); + } + + if (error == ENETRESET) { + error = 0; + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + iwi_init(sc); + error = tsleep(IWI_FW_CMD_ACKED(sc), 0, "iwirun", hz); + } + } + + IWI_UNLOCK(sc); + + return error; +} + +static int +iwi_abort_scan( struct iwi_softc *sc ) +{ + sc->flags |= IWI_FLAG_SCAN_ABORT; + return iwi_cmd(sc, IWI_CMD_SCAN_ABORT, NULL, 0, 1); +} + +static void +iwi_stop_master(struct iwi_softc *sc) +{ + int ntries; + + /* + * If the master is busy scanning, we will occasionally + * timeout waiting for it (the master) to stop. Make the + * 'stopping' process more robust by ceasing all scans + * prior to asking for the stop. + */ + if ( ( sc->flags & IWI_FLAG_SCANNING ) && + !( sc->flags & IWI_FLAG_RF_DISABLED ) ) { + iwi_abort_scan(sc); + if (( sc->flags & IWI_FLAG_SCAN_ABORT ) && + !( sc->flags & IWI_FLAG_RF_DISABLED )) { + (void) tsleep(IWI_FW_SCAN_COMPLETED(sc), 0, + "iwiabr", hz); + } + } + /* Disable interrupts */ + + CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); + + CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); + for (ntries = 0; ntries < 5; ntries++) { + if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) + break; + DELAY(10); + } + if (ntries == 5 && sc->debug_level > 0) + device_printf(sc->sc_dev, "timeout waiting for master\n"); + + CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | + IWI_RST_PRINCETON_RESET); + + sc->flags &= ~IWI_FLAG_FW_INITED; +} + +static int +iwi_reset(struct iwi_softc *sc) +{ + int i, ntries; + + iwi_stop_master(sc); + + /* Move adapter to D0 state */ + CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) | + IWI_CTL_INIT); + + /* Initialize Phase-Locked Level (PLL) */ + CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); + + /* Wait for clock stabilization */ + for (ntries = 0; ntries < 1000; ntries++) { + if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) + break; + DELAY(200); + } + if (ntries == 1000) { + return EIO; + } + + CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | + IWI_RST_SW_RESET); + + DELAY(10); + + CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) | + IWI_CTL_INIT); + + + /* Clear NIC memory */ + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); + + for (i = 0; i < 0xc000; i++) { + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); + } + + sc->num_stations = 0; + return 0; +} + +static int +iwi_load_ucode(struct iwi_softc *sc, void *uc, int size) +{ + u_int16_t *w; + int ntries, i; + + CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | + IWI_RST_STOP_MASTER); + for (ntries = 0; ntries < 5; ntries++) { + if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) + break; + DELAY(10); + } + if (ntries == 5) { + device_printf(sc->sc_dev, "timeout waiting for master\n"); + return EIO; + } + + MEM_WRITE_4(sc, 0x3000e0, 0x80000000); + DELAY(5000); + CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) & + ~IWI_RST_PRINCETON_RESET); + DELAY(5000); + MEM_WRITE_4(sc, 0x3000e0, 0); + DELAY(1000); + MEM_WRITE_4(sc, 0x300004, 1); + DELAY(1000); + MEM_WRITE_4(sc, 0x300004, 0); + DELAY(1000); + MEM_WRITE_1(sc, 0x200000, 0x00); + MEM_WRITE_1(sc, 0x200000, 0x40); + DELAY(1000); + + /* Adapter is buggy, we must set the address for each word */ + for (w = uc; size > 0; w++, size -= 2) + MEM_WRITE_2(sc, 0x200010, *w); + + MEM_WRITE_1(sc, 0x200000, 0x00); + MEM_WRITE_1(sc, 0x200000, 0x80); + + /* Wait until we get a response in the uc queue */ + for (ntries = 0; ntries < 100; ntries++) { + if (MEM_READ_1(sc, 0x200000) & 1) + break; + DELAY(100); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for ucode to initialize\n"); + return EIO; + } + + /* Empty the uc queue or the firmware will not initialize properly */ + for (i = 0; i < 7; i++) + MEM_READ_4(sc, 0x200004); + + MEM_WRITE_1(sc, 0x200000, 0x00); + + return 0; +} + +/* macro to handle unaligned little endian data in firmware image */ +#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) +static int +iwi_load_firmware(struct iwi_softc *sc, void *fw, int size) +{ + bus_dma_tag_t dmat; + bus_dmamap_t map; + bus_addr_t physaddr; + void *virtaddr; + u_char *p, *end; + u_int32_t sentinel, ctl, src, dst, sum, len, mlen; + int ntries, error = 0; + + sc->flags &= ~(IWI_FLAG_FW_INITED); + + /* Allocate DMA memory for storing firmware image */ + error = bus_dma_tag_create(sc->iwi_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, BUS_DMA_ALLOCNOW, &dmat); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create firmware DMA tag\n"); + goto fail1; + } + + /* + * We cannot map fw directly because of some hardware constraints on + * the mapping address. + */ + error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_WAITOK, &map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not allocate firmware DMA memory\n"); + goto fail2; + } + + error = bus_dmamap_load(dmat, map, virtaddr, size, iwi_dma_map_addr, + &physaddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load firmware DMA map\n"); + goto fail3; + } + + /* Copy firmware image to DMA memory */ + bcopy(fw, virtaddr, size); + + /* Make sure the adapter will get up-to-date values */ + bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE); + + /* Tell the adapter where the command blocks are stored */ + MEM_WRITE_4(sc, 0x3000a0, 0x27000); + + /* + * Store command blocks into adapter's internal memory using register + * indirections. The adapter will read the firmware image through DMA + * using information stored in command blocks. + */ + src = physaddr; + p = virtaddr; + end = p + size; + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); + + while (p < end) { + dst = GETLE32(p); p += 4; src += 4; + len = GETLE32(p); p += 4; src += 4; + p += len; + + while (len > 0) { + mlen = min(len, IWI_CB_MAXDATALEN); + + ctl = IWI_CB_DEFAULT_CTL | mlen; + sum = ctl ^ src ^ dst; + + /* Write a command block */ + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); + + src += mlen; + dst += mlen; + len -= mlen; + } + } + + /* Write a fictive final command block (sentinel) */ + sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); + CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); + + CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) & + ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER)); + + /* Tell the adapter to start processing command blocks */ + MEM_WRITE_4(sc, 0x3000a4, 0x540100); + + /* Wait until the adapter has processed all command blocks */ + for (ntries = 0; ntries < 400; ntries++) { + if (MEM_READ_4(sc, 0x3000d0) >= sentinel) + break; + DELAY(100); + } + if (ntries == 400) { + device_printf(sc->sc_dev, + "timeout processing command blocks\n"); + error = EIO; + goto fail4; + } + + + /* We're done with command blocks processing */ + MEM_WRITE_4(sc, 0x3000a4, 0x540c00); + + /* Allow interrupts so we know when the firmware is inited */ + CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); + + /* Tell the adapter to initialize the firmware */ + CSR_WRITE_4(sc, IWI_CSR_RST, 0); + CSR_WRITE_4(sc, IWI_CSR_CTL, CSR_READ_4(sc, IWI_CSR_CTL) | + IWI_CTL_ALLOW_STANDBY); + + /* Wait at most one second for firmware initialization to complete */ + if ((error = tsleep(IWI_FW_INITIALIZED(sc), 0, "iwiini", hz)) != 0) { + device_printf(sc->sc_dev, "timeout waiting for firmware " + "initialization to complete\n"); + goto fail4; + } + +fail4: bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dmat, map); +fail3: bus_dmamem_free(dmat, virtaddr, map); +fail2: bus_dma_tag_destroy(dmat); +fail1: + return error; +} + +/* + * Store firmware into kernel memory so we can download it when we need to, + * e.g when the adapter wakes up from suspend mode. + */ +static int +iwi_cache_firmware(struct iwi_softc *sc, void *data, int is_ibss) +{ + struct iwi_firmware *kfw = &sc->fw; + struct iwi_firmware ufw; + int error; + + iwi_free_firmware(sc); + + /* + * mutex(9): no mutexes should be held across functions which access + * memory in userspace, such as copyin(9) [...] + */ + + if ((error = copyin(data, &ufw, sizeof ufw)) != 0) + goto fail1; + + kfw->boot_size = ufw.boot_size; + kfw->ucode_size = ufw.ucode_size; + kfw->main_size = ufw.main_size; + + kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_WAITOK); + if (kfw->boot == NULL) { + error = ENOMEM; + goto fail1; + } + + kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_WAITOK); + if (kfw->ucode == NULL) { + error = ENOMEM; + goto fail2; + } + + kfw->main = malloc(kfw->main_size, M_DEVBUF, M_WAITOK); + if (kfw->main == NULL) { + error = ENOMEM; + goto fail3; + } + + if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0) + goto fail4; + + if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0) + goto fail4; + + if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0) + goto fail4; + + DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n", + kfw->boot_size, kfw->ucode_size, kfw->main_size)); + + + sc->flags |= IWI_FLAG_FW_CACHED; + sc->flags |= is_ibss ? IWI_FLAG_FW_IBSS : 0; + return 0; + +fail4: free(kfw->boot, M_DEVBUF); +fail3: free(kfw->ucode, M_DEVBUF); +fail2: free(kfw->main, M_DEVBUF); +fail1: + + return error; +} + +static void +iwi_free_firmware(struct iwi_softc *sc) +{ + if (!(sc->flags & IWI_FLAG_FW_CACHED)) + return; + + free(sc->fw.boot, M_DEVBUF); + free(sc->fw.ucode, M_DEVBUF); + free(sc->fw.main, M_DEVBUF); + + sc->flags &= ~( IWI_FLAG_FW_CACHED | IWI_FLAG_FW_IBSS ); +} + +static int +iwi_adapter_config(struct iwi_softc *sc, int is_a, int cmd_wait) +{ + struct iwi_configuration config; + + bzero(&config, sizeof config); + config.enable_multicast = 1; + config.noise_reported = 1; + + config.bg_autodetect = + ( !(is_a) && + ( sc->enable_bg_autodetect != 0 ) ) ? 1 : 0; /* default: on */ + + config.bluetooth_coexistence = + ( sc->enable_bt_coexist != 0 ) ? 1 : 0; /* default: on */ + + config.enable_cts_to_self = + ( sc->enable_cts_to_self > 0 ) ? 1 : 0; /* default: off */ + + if (sc->antenna_diversity > 0 ) { /* default: BOTH */ + switch( sc->antenna_diversity ) { + case 1: case 3: + config.antenna_diversity = sc->antenna_diversity; + } + } + + config.disable_unicast_decryption = + ( sc->disable_unicast_decryption != 0 ) ? 1 : 0; /* default: on */ + + config.disable_multicast_decryption = + ( sc->disable_multicast_decryption != 0 ) ? 1 : 0;/* default: on */ + + + if ( sc->debug_level > 0 ) { + printf("config.bluetooth_coexistence = %d\n", + config.bluetooth_coexistence ); + printf("config.bg_autodetect = %d\n", + config.bg_autodetect ); + printf("config.enable_cts_to_self = %d\n", + config.enable_cts_to_self ); + printf("config.antenna_diversity = %d\n", + config.antenna_diversity ); + printf("config.disable_unicast_decryption = %d\n", + config.disable_unicast_decryption ); + printf("config.disable_multicast_decryption = %d\n", + config.disable_multicast_decryption ); + printf("config.neg_best_rates_first = %d\n", + sc->enable_neg_best_first ); + } + + return iwi_cmd(sc, IWI_CMD_SET_CONFIGURATION, &config, + sizeof config, cmd_wait ); +} + +static int +iwi_config(struct iwi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct iwi_rateset rs; + struct iwi_txpower power; + struct ieee80211_wepkey *k; + struct iwi_wep_key wepkey; + u_int32_t data; + int error, i; + + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":")); + error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr, + IEEE80211_ADDR_LEN, 0); + if (error != 0) + return error; + + DPRINTF(("Configuring adapter\n")); + if ((error = iwi_adapter_config(sc, 1, 0)) != 0) + return error; + + data = htole32(IWI_POWER_MODE_CAM); + DPRINTF(("Setting power mode to %u\n", le32toh(data))); + error = iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data, 0); + if (error != 0) + return error; + + data = htole32(ic->ic_rtsthreshold); + DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); + error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data, 0); + if (error != 0) + return error; + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + power.mode = IWI_MODE_11B; + power.nchan = 11; + for (i = 0; i < 11; i++) { + power.chan[i].chan = i + 1; + power.chan[i].power = IWI_TXPOWER_MAX; + } + DPRINTF(("Setting .11b channels tx power\n")); + error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power, + 0); + if (error != 0) + return error; + + power.mode = IWI_MODE_11G; + DPRINTF(("Setting .11g channels tx power\n")); + error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power, + 0); + if (error != 0) + return error; + } + + rs.mode = IWI_MODE_11G; + rs.type = IWI_RATESET_TYPE_SUPPORTED; + rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; + bcopy(ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, rs.rates, + rs.nrates); + DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); + error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0); + if (error != 0) + return error; + + rs.mode = IWI_MODE_11A; + rs.type = IWI_RATESET_TYPE_SUPPORTED; + rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; + bcopy(ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, rs.rates, + rs.nrates); + DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); + error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0); + if (error != 0) + return error; + + data = htole32(arc4random()); + DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); + error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0); + if (error != 0) + return error; + + if (ic->ic_flags & IEEE80211_F_WEPON) { + k = ic->ic_nw_keys; + for (i = 0; i < IEEE80211_WEP_NKID; i++, k++) { + wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; + wepkey.idx = i; + wepkey.len = k->wk_len; + bzero(wepkey.key, sizeof wepkey.key); + bcopy(k->wk_key, wepkey.key, k->wk_len); + DPRINTF(("Setting wep key index %u len %u\n", + wepkey.idx, wepkey.len)); + error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, + sizeof wepkey, 0); + if (error != 0) + return error; + } + } + + /* Enable adapter */ + DPRINTF(("Enabling adapter\n")); + return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0); +} + +static int +iwi_scan(struct iwi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwi_scan scan; + u_int8_t *p; + int i, count; + int do_5ghz_scan = 0; + + sc->scan_counter++; /* track the number of scans started */ + + sc->flags |= IWI_FLAG_SCANNING; + + bzero(&scan, sizeof scan); + + /* + * Alternate two broadcast scans with + * two broadcast/direct scans. + */ + if ( sc->scan_counter & 2 ) { + scan.type = IWI_SCAN_TYPE_BROADCAST_AND_DIRECT; + scan.intval = htole16(100); + } else { + scan.type = IWI_SCAN_TYPE_BROADCAST; + scan.intval = htole16(40); + } + + p = scan.channels; + + /* + * If we have .11a capable adapter, and + * - we are in .11a mode, or + * - we are in auto mode and this is an odd numbered scan + * then do a 5GHz scan, otherwise do a 2GHz scan. + */ + if ( ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates > 0 ) { + if (( ic->ic_curmode == IEEE80211_MODE_11A ) || + (( ic->ic_curmode == IEEE80211_MODE_AUTO ) && + ( sc->scan_counter & 1))) + do_5ghz_scan = 1; + } + count = 0; + if ( do_5ghz_scan ) { + DPRINTF(("Scanning 5GHz band\n")); + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) && + isset(ic->ic_chan_active, i)) { + *++p = i; + count++; + } + } + *(p - count) = IWI_CHAN_5GHZ | count; + } else { + DPRINTF(("Scanning 2GHz band\n")); + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) && + isset(ic->ic_chan_active, i)) { + *++p = i; + count++; + } + } + *(p - count) = IWI_CHAN_2GHZ | count; + } + return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1); +} + +static int +iwi_auth_and_assoc(struct iwi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_node *ni = ic->ic_bss; + struct iwi_rateset rs; + u_int32_t data; + int error, x; + + if ( ( sc->flags & IWI_FLAG_FW_IBSS ) && + !( ni->ni_capinfo & IEEE80211_CAPINFO_IBSS ) ) { + return -1; /* IBSS F/W requires network ibss capability */ + } + + DPRINTF(("Configuring adapter\n")); + if ((error = iwi_adapter_config(sc, + IEEE80211_IS_CHAN_5GHZ(ni->ni_chan), 1)) != 0) + return error; + +#ifdef IWI_DEBUG + if (sc->debug_level > 0) { + printf("Setting ESSID to "); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf("\n"); + } +#endif + error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen, 1); + if (error != 0) + return error; + + /* the rate set has already been "negotiated" */ + rs.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A : + IWI_MODE_11G; + rs.type = IWI_RATESET_TYPE_NEGOTIATED; + rs.nrates = ni->ni_rates.rs_nrates; + if ( sc->enable_neg_best_first != 1 ) { + bcopy(ni->ni_rates.rs_rates, rs.rates, rs.nrates); + } else { + for ( x = 0 ; x < rs.nrates; x++ ) { + /* + * Present the firmware with the most favourable + * of the negotiated rates first. + */ + rs.rates[rs.nrates-x-1] = ni->ni_rates.rs_rates[x]; + } + } + + if ( sc->debug_level > 0 ) { + printf("Setting negotiated rates (%u) : ", rs.nrates); + for ( x = 0 ; x < rs.nrates; x++ ) { + printf("%d ", rs.rates[x]); + } + printf("\n"); + } + + error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1); + if (error != 0) + return error; + + data = htole32(ni->ni_rssi); + DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi)); + error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1); + if (error != 0) + return error; + + bzero(&sc->assoc, sizeof sc->assoc); + sc->assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A : + IWI_MODE_11G; + sc->assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + if (sc->authmode == IEEE80211_AUTH_SHARED) + sc->assoc.auth = (ic->ic_wep_txkey << 4) | IWI_AUTH_SHARED; + bcopy(ni->ni_tstamp, sc->assoc.tstamp, 8); + sc->assoc.capinfo = htole16(ni->ni_capinfo); + sc->assoc.lintval = htole16(ic->ic_lintval); + sc->assoc.intval = htole16(ni->ni_intval); + IEEE80211_ADDR_COPY(sc->assoc.bssid, ni->ni_bssid); + if ( ic->ic_opmode == IEEE80211_M_IBSS ) + IEEE80211_ADDR_COPY(sc->assoc.dst, ifp->if_broadcastaddr); + else + IEEE80211_ADDR_COPY(sc->assoc.dst, ni->ni_bssid); + + DPRINTF(("Trying to associate to %6D channel %u auth %u\n", + sc->assoc.bssid, ":", sc->assoc.chan, sc->assoc.auth)); + return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &sc->assoc, sizeof sc->assoc, 1); +} + +static void +iwi_init(void *priv) +{ + struct iwi_softc *sc = priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct iwi_firmware *fw = &sc->fw; + int i; + + /* exit immediately if firmware has not been ioctl'd */ + if (!(sc->flags & IWI_FLAG_FW_CACHED)) { + ifp->if_flags &= ~IFF_UP; + return; + } + + iwi_stop(sc); + + if (iwi_reset(sc) != 0) { + device_printf(sc->sc_dev, "could not reset adapter\n"); + goto fail; + } + + if (iwi_load_firmware(sc, fw->boot, fw->boot_size) != 0) { + device_printf(sc->sc_dev, "could not load boot firmware\n"); + goto fail; + } + + if (iwi_load_ucode(sc, fw->ucode, fw->ucode_size) != 0) { + device_printf(sc->sc_dev, "could not load microcode\n"); + goto fail; + } + + iwi_stop_master(sc); + + sc->tx_cur = 0; + sc->tx_queued = 0; + sc->tx_old = IWI_TX_RING_SIZE - 1; + sc->cmd_cur = 0; + sc->rx_cur = IWI_RX_RING_SIZE - 1; + + CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmd_ring_pa); + CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, IWI_CMD_RING_SIZE); + CSR_WRITE_4(sc, IWI_CSR_CMD_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_CMD_WRITE_INDEX, sc->cmd_cur); + + CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->tx_ring_pa); + CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, IWI_TX_RING_SIZE); + CSR_WRITE_4(sc, IWI_CSR_TX1_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_TX1_WRITE_INDEX, sc->tx_cur); + + CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->tx_ring_pa); + CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, IWI_TX_RING_SIZE); + CSR_WRITE_4(sc, IWI_CSR_TX2_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_TX2_WRITE_INDEX, 0); + + CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->tx_ring_pa); + CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, IWI_TX_RING_SIZE); + CSR_WRITE_4(sc, IWI_CSR_TX3_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_TX3_WRITE_INDEX, 0); + + CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->tx_ring_pa); + CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, IWI_TX_RING_SIZE); + CSR_WRITE_4(sc, IWI_CSR_TX4_READ_INDEX, 0); + CSR_WRITE_4(sc, IWI_CSR_TX4_WRITE_INDEX, 0); + + for (i = 0; i < IWI_RX_RING_SIZE; i++) + CSR_WRITE_4(sc, IWI_CSR_RX_BASE + i * 4, + sc->rx_buf[i].physaddr); + + /* + * Kick Rx + */ + CSR_WRITE_4(sc, IWI_CSR_RX_WRITE_INDEX, sc->rx_cur); + CSR_WRITE_4(sc, IWI_CSR_RX_READ_INDEX, 0); + + if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) { + device_printf(sc->sc_dev, "could not load main firmware\n"); + goto fail; + } + + /* + * Force the opmode based on what firmware is loaded. This + * stops folks from killing the firmware by asking it to + * do something it doesn't support. + */ + if ( ic->ic_opmode != IEEE80211_M_MONITOR ) { + ic->ic_opmode = ( sc->flags & IWI_FLAG_FW_IBSS ) + ? IEEE80211_M_IBSS : IEEE80211_M_STA; + } + + sc->flags |= IWI_FLAG_FW_INITED; + + sc->flags &= ~( IWI_FLAG_SCANNING | + IWI_FLAG_SCAN_COMPLETE | + IWI_FLAG_SCAN_ABORT | + IWI_FLAG_ASSOCIATED ); + + if (iwi_config(sc) != 0) { + device_printf(sc->sc_dev, "device configuration failed\n"); + goto fail; + } + + if ( ic->ic_opmode != IEEE80211_M_MONITOR ) { + ieee80211_begin_scan(ifp); + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; + } else { + ieee80211_begin_scan(ifp); + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; + } + + return; + +fail: + if ( !(sc->flags & IWI_FLAG_RESET) ) + ifp->if_flags &= ~IFF_UP; + iwi_stop(sc); +} + +static void +iwi_init_locked(void *priv) +{ + struct iwi_softc *sc = priv; + IWI_LOCK_INFO; + IWI_LOCK(sc); + iwi_init(sc); + IWI_UNLOCK(sc); +} + +static void +iwi_stop(void *priv) +{ + struct iwi_softc *sc = priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct iwi_tx_buf *buf; + int i; + + iwi_stop_master(sc); + CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SW_RESET); + + /* + * Release Tx buffers + */ + for (i = 0; i < IWI_TX_RING_SIZE; i++) { + buf = &sc->tx_buf[i]; + + if (buf->m != NULL) { + bus_dmamap_sync(sc->tx_buf_dmat, buf->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->tx_buf_dmat, buf->map); + m_freem(buf->m); + buf->m = NULL; + + if (buf->ni != NULL) { + if (buf->ni != ic->ic_bss) + ieee80211_free_node(ic, buf->ni); + buf->ni = NULL; + } + } + } + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); +} + +static int8_t +iwi_cache_station(struct iwi_softc *sc, u_int8_t *mac) +{ + int i, x, base, elemsize = sizeof(struct iwi_fw_station); + for (i = 0; i < sc->num_stations; i++) + if (!memcmp(sc->stations[i], mac, IEEE80211_ADDR_LEN)) + break; + if (i == IWI_FW_MAX_STATIONS) + return 0xff; + memcpy(sc->stations[i], mac, IEEE80211_ADDR_LEN); + for (x = 0, base = IWI_STATION_TABLE + (i * elemsize) ; + x < IEEE80211_ADDR_LEN ; x++ ) { + CSR_WRITE_1(sc, base + x, mac[x]); + } + if ( (i + 1) > sc->num_stations ) + sc->num_stations++; + return i; +} + +static u_int8_t +iwi_find_station(struct iwi_softc *sc, u_int8_t *mac) +{ + u_int8_t i; + for (i = 0; i < sc->num_stations; i++) + if (!memcmp(sc->stations[i], mac, IEEE80211_ADDR_LEN)) + return i; + return 0xff; +} + +static const char * +iwi_error_desc(u_int32_t val) +{ + switch (val) { + case IWI_FW_ERROR_OK: + return "OK"; + case IWI_FW_ERROR_FAIL: + return "FAIL"; + case IWI_FW_ERROR_MEMORY_UNDERFLOW: + return "MEMORY_UNDERFLOW"; + case IWI_FW_ERROR_MEMORY_OVERFLOW: + return "MEMORY_OVERFLOW"; + case IWI_FW_ERROR_BAD_PARAM: + return "BAD_PARAMETER"; + case IWI_FW_ERROR_BAD_CHECKSUM: + return "BAD_CHECKSUM"; + case IWI_FW_ERROR_NMI_INTERRUPT: + return "NMI_INTERRUPT"; + case IWI_FW_ERROR_BAD_DATABASE: + return "BAD_DATABASE"; + case IWI_FW_ERROR_ALLOC_FAIL: + return "ALLOC_FAIL"; + case IWI_FW_ERROR_DMA_UNDERRUN: + return "DMA_UNDERRUN"; + case IWI_FW_ERROR_DMA_STATUS: + return "DMA_STATUS"; + case IWI_FW_ERROR_DINOSTATUS_ERROR: + return "DINOSTATUS_ERROR"; + case IWI_FW_ERROR_EEPROMSTATUS_ERROR: + return "EEPROMSTATUS_ERROR"; + case IWI_FW_ERROR_SYSASSERT: + return "SYSASSERT"; + case IWI_FW_ERROR_FATAL_ERROR: + return "FATAL"; + default: + return "UNKNOWN_ERROR"; + } +} + +static void +iwi_dump_fw_event_log(struct iwi_softc *sc) +{ + u_int32_t ev, time, data, i, count, base; + base = CSR_READ_4(sc, IWI_FW_EVENT_LOG); + count = MEM_READ_4(sc, base); + if ( count > 0 && (sc->flags & IWI_FLAG_FW_INITED) ) { + printf("Reading %d event log entries from base address 0x%x.\n", + count, base); + if (IWI_FW_EVENT_START_OFFSET <= count * IWI_FW_EVENT_ELEM_SIZE) + device_printf(sc->sc_dev,"Start IWI Event Log Dump:\n"); + for (i = IWI_FW_EVENT_START_OFFSET; + i <= count * IWI_FW_EVENT_ELEM_SIZE; + i += IWI_FW_EVENT_ELEM_SIZE) { + ev = MEM_READ_4(sc, base + i); + time = MEM_READ_4(sc, base + i + 1 * sizeof(u_int32_t)); + data = MEM_READ_4(sc, base + i + 2 * sizeof(u_int32_t)); + printf("%d %8p %8.8d\n", time, (void *) data, ev); + } + } else { + printf("There are no entries in the firmware event log.\n"); + } +} + +static void +iwi_dump_fw_error_log(struct iwi_softc *sc) +{ + u_int32_t i = 0; + int32_t count, base; + base = CSR_READ_4(sc, IWI_FW_ERROR_LOG); + count = MEM_READ_4(sc, base); + if ( count > 0 && (sc->flags & IWI_FLAG_FW_INITED) ) { + printf("Reading %d error log entries " + "from base address 0x%p.\n", count, (void *)base); + for ( i = IWI_FW_ERROR_START_OFFSET; + i <= count * IWI_FW_EVENT_ELEM_SIZE; + i += IWI_FW_ERROR_ELEM_SIZE ) { + u_int32_t elems; + printf("%15.15s", + iwi_error_desc(MEM_READ_4(sc, base + i))); + printf(" time(%8.8d)", MEM_READ_4(sc, base + i + 4)); + for ( elems = 2 ; elems < 7 ; elems++ ) { + printf(" %8p", (void *) + MEM_READ_4(sc, base + i + (4 * elems))); + } + printf("\n"); + } + } +} + +static int +iwi_sysctl_cts_to_self(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = (void *)arg1; + int cts_to_self = sc->enable_cts_to_self; + int error = sysctl_handle_int(oidp, &cts_to_self, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + + if ( !error && req->newptr && cts_to_self != sc->enable_cts_to_self ) { + switch ( cts_to_self ) { + case -1: case 0: case 1: + sc->enable_cts_to_self = cts_to_self; + error = iwi_adapter_config(sc, 0, 0); + break; + } + } + return error; +} + + +static int +iwi_sysctl_antenna_diversity(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = (void *)arg1; + int antenna_diversity = sc->antenna_diversity; + int error = sysctl_handle_int(oidp, &antenna_diversity, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + + if ( !error && req->newptr && antenna_diversity != sc->antenna_diversity ) { + switch ( antenna_diversity ) { + case 1: case 3: case 0: case -1: + sc->antenna_diversity = antenna_diversity; + error = iwi_adapter_config(sc, 0, 0); + break; + } + } + return error; +} + +static int +iwi_sysctl_bg_autodetect(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = (void *)arg1; + int bg_autodetect = sc->enable_bg_autodetect; + int error = sysctl_handle_int(oidp, &bg_autodetect, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + + if ( !error && req->newptr && bg_autodetect != sc->enable_bg_autodetect ) { + switch ( bg_autodetect ) { + case 1: case 0: case -1: + sc->enable_bg_autodetect = bg_autodetect; + error = iwi_adapter_config(sc, 0, 0); + break; + } + } + return error; +} + +static int +iwi_sysctl_bt_coexist(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = (void *)arg1; + int bt_coexist = sc->enable_bt_coexist; + int error = sysctl_handle_int(oidp, &bt_coexist, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + + if ( !error && req->newptr && bt_coexist != sc->enable_bt_coexist ) { + switch ( bt_coexist ) { + case 1: case 0: case -1: + sc->enable_bt_coexist = bt_coexist; + error = iwi_adapter_config(sc, 0, 0); + break; + } + } + return error; +} + +static int +iwi_sysctl_dump_logs(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + int result = -1; + int error = sysctl_handle_int(oidp, &result, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + + if (!error && req->newptr && result == 1) { + iwi_dump_fw_event_log(sc); + iwi_dump_fw_error_log(sc); + } + return error; +} + +static int +iwi_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + u_int32_t size; + struct iwi_dump_buffer dump; + + (void)arg2; /* silence WARNS == 6 */ + (void)oidp; /* silence WARNS == 6 */ + + if (!(sc->flags & IWI_FLAG_FW_INITED)) { + bzero(dump.buf, sizeof dump.buf); + return SYSCTL_OUT(req, &dump, sizeof dump); + } + + size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); + CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &dump.buf[1], size); + + return SYSCTL_OUT(req, &dump, sizeof dump); +} + +static int +iwi_sysctl_radio(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + int val; + + (void)arg2; /* silence WARNS == 6 */ + (void)oidp; /* silence WARNS == 6 */ + + val = (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) ? 1 : 0; + return SYSCTL_OUT(req, &val, sizeof val); +} + +static int +iwi_sysctl_neg_best_rates_first(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + int best_first = sc->enable_neg_best_first; + int error = sysctl_handle_int(oidp, &best_first, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + (void)oidp; /* silence WARNS == 6 */ + + if ( !error && req->newptr && best_first != sc->enable_neg_best_first ) { + switch ( best_first ) { + case 1: case 0: case -1: + sc->enable_neg_best_first = best_first; + break; + } + } + return error; +} + +static int +iwi_sysctl_disable_unicast_decryption(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + int disable_uni = sc->disable_unicast_decryption; + int error = sysctl_handle_int(oidp, &disable_uni, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + (void)oidp; /* silence WARNS == 6 */ + + if (!error && req->newptr && disable_uni != sc->disable_unicast_decryption) { + switch ( disable_uni ) { + case 1: case 0: case -1: + sc->disable_unicast_decryption = disable_uni; + break; + } + } + return error; +} + +static int +iwi_sysctl_disable_multicast_decryption(SYSCTL_HANDLER_ARGS) +{ + struct iwi_softc *sc = arg1; + int disable_mul = sc->disable_multicast_decryption; + int error = sysctl_handle_int(oidp, &disable_mul, 0, req); + + (void)arg2; /* silence WARNS == 6 */ + (void)oidp; /* silence WARNS == 6 */ + + if (!error && req->newptr && disable_mul!=sc->disable_multicast_decryption){ + switch ( disable_mul ) { + case 1: case 0: case -1: + sc->disable_multicast_decryption = disable_mul; + break; + } + } + return error; +} + + diff --git a/sys/dev/netif/iwi/if_iwireg.h b/sys/dev/netif/iwi/if_iwireg.h new file mode 100644 index 0000000000..b5ba82567a --- /dev/null +++ b/sys/dev/netif/iwi/if_iwireg.h @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2004, 2005 + * Damien Bergamini . + * Copyright (c) 2004, 2005 + * Andrew Atrens . + * + * 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 unmodified, 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 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 THE AUTHOR OR 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. + * + * $DragonFly: src/sys/dev/netif/iwi/if_iwireg.h,v 1.1 2005/03/06 05:02:02 dillon Exp $ + */ + +#define IWI_TX_RING_SIZE 64 +#define IWI_CMD_RING_SIZE 16 +#define IWI_RX_RING_SIZE 32 + +#define IWI_CSR_INTR 0x0008 +#define IWI_CSR_INTR_MASK 0x000c +#define IWI_CSR_INDIRECT_ADDR 0x0010 +#define IWI_CSR_INDIRECT_DATA 0x0014 +#define IWI_CSR_AUTOINC_ADDR 0x0018 +#define IWI_CSR_AUTOINC_DATA 0x001c +#define IWI_CSR_RST 0x0020 +#define IWI_CSR_CTL 0x0024 +#define IWI_CSR_IO 0x0030 +#define IWI_CSR_CMD_BASE 0x0200 +#define IWI_CSR_CMD_SIZE 0x0204 +#define IWI_CSR_TX1_BASE 0x0208 +#define IWI_CSR_TX1_SIZE 0x020c +#define IWI_CSR_TX2_BASE 0x0210 +#define IWI_CSR_TX2_SIZE 0x0214 +#define IWI_CSR_TX3_BASE 0x0218 +#define IWI_CSR_TX3_SIZE 0x021c +#define IWI_CSR_TX4_BASE 0x0220 +#define IWI_CSR_TX4_SIZE 0x0224 +#define IWI_CSR_CMD_READ_INDEX 0x0280 +#define IWI_CSR_TX1_READ_INDEX 0x0284 +#define IWI_CSR_TX2_READ_INDEX 0x0288 +#define IWI_CSR_TX3_READ_INDEX 0x028c +#define IWI_CSR_TX4_READ_INDEX 0x0290 +#define IWI_CSR_RX_READ_INDEX 0x02a0 +#define IWI_CSR_RX_BASE 0x0500 +#define IWI_CSR_TABLE0_SIZE 0x0700 +#define IWI_CSR_TABLE0_BASE 0x0704 +#define IWI_CSR_CURRENT_TX_RATE IWI_CSR_TABLE0_BASE + +#define IWI_CSR_GET_TABLE0_BASE 0x0380 +#define IWI_CSR_GET_TABLE1_BASE 0x0384 +#define IWI_CSR_GET_TABLE2_BASE 0x0388 +#define IWI_CSR_GET_TABLE3_BASE 0x038C + +#define IWI_CSR_CMD_WRITE_INDEX 0x0f80 +#define IWI_CSR_TX1_WRITE_INDEX 0x0f84 +#define IWI_CSR_TX2_WRITE_INDEX 0x0f88 +#define IWI_CSR_TX3_WRITE_INDEX 0x0f8c +#define IWI_CSR_TX4_WRITE_INDEX 0x0f90 +#define IWI_CSR_RX_WRITE_INDEX 0x0fa0 +#define IWI_CSR_READ_INT 0x0ff4 + +#define IWI_STATION_TABLE 0x0c0c + +#define IWI_FW_ERROR_LOG 0x0610 +#define IWI_FW_EVENT_LOG 0x0614 + + +/* possible flags for IWI_CSR_INTR */ +#define IWI_INTR_RX_TRANSFER 0x00000002 +#define IWI_INTR_STATUS_CHG 0x00000010 +#define IWI_INTR_BEACON_EXP 0x00000020 +#define IWI_INTR_CMD_TRANSFER 0x00000800 +#define IWI_INTR_TX1_TRANSFER 0x00001000 +#define IWI_INTR_TX2_TRANSFER 0x00002000 +#define IWI_INTR_TX3_TRANSFER 0x00004000 +#define IWI_INTR_TX4_TRANSFER 0x00008000 +#define IWI_INTR_SLMODE_CDONE 0x00010000 +#define IWI_INTR_PREP_PWDOWN 0x00100000 +#define IWI_INTR_PWDOWN 0x00200000 +#define IWI_INTR_FW_INITED 0x01000000 +#define IWI_INTR_DIS_PHY_DONE 0x02000000 +#define IWI_INTR_RADIO_OFF 0x04000000 +#define IWI_INTR_FATAL_ERROR 0x40000000 +#define IWI_INTR_PARITY_ERROR 0x80000000 + +#define IWI_HANDLED_INTR_MASK \ + (IWI_INTR_RX_TRANSFER | IWI_INTR_CMD_TRANSFER | \ + IWI_INTR_TX1_TRANSFER | IWI_INTR_TX2_TRANSFER | \ + IWI_INTR_TX3_TRANSFER | IWI_INTR_TX4_TRANSFER | \ + IWI_INTR_FW_INITED | IWI_INTR_FATAL_ERROR | \ + IWI_INTR_RADIO_OFF | IWI_INTR_PARITY_ERROR) + +#define IWI_INTR_MASK \ + (IWI_INTR_RX_TRANSFER | IWI_INTR_CMD_TRANSFER | \ + IWI_INTR_TX1_TRANSFER | IWI_INTR_TX2_TRANSFER | \ + IWI_INTR_TX3_TRANSFER | IWI_INTR_TX4_TRANSFER | \ + IWI_INTR_FW_INITED | IWI_INTR_FATAL_ERROR | \ + IWI_INTR_PARITY_ERROR | IWI_INTR_STATUS_CHG | \ + IWI_INTR_BEACON_EXP | IWI_INTR_SLMODE_CDONE | \ + IWI_INTR_PREP_PWDOWN | IWI_INTR_PWDOWN | \ + IWI_INTR_DIS_PHY_DONE | IWI_INTR_RADIO_OFF) + +/* possible flags for register IWI_CSR_RST */ +#define IWI_RST_PRINCETON_RESET 0x00000001 +#define IWI_RST_SW_RESET 0x00000080 +#define IWI_RST_MASTER_DISABLED 0x00000100 +#define IWI_RST_STOP_MASTER 0x00000200 + +/* possible flags for register IWI_CSR_CTL */ +#define IWI_CTL_CLOCK_READY 0x00000001 +#define IWI_CTL_ALLOW_STANDBY 0x00000002 +#define IWI_CTL_INIT 0x00000004 + +/* possible flags for register IWI_CSR_IO */ +#define IWI_IO_RADIO_ENABLED 0x00010000 + +/* possible flags for IWI_CSR_READ_INT */ +#define IWI_READ_INT_INIT_HOST 0x20000000 + +/* table2 offsets */ +#define IWI_INFO_ADAPTER_MAC 40 + +/* constants for command blocks */ +#define IWI_CB_DEFAULT_CTL 0x8cea0000 +#define IWI_CB_MAXDATALEN 8191 + +/* supported rates */ +#define IWI_RATE_DS1 10 +#define IWI_RATE_DS2 20 +#define IWI_RATE_DS5 55 +#define IWI_RATE_DS11 110 +#define IWI_RATE_OFDM6 13 +#define IWI_RATE_OFDM9 15 +#define IWI_RATE_OFDM12 5 +#define IWI_RATE_OFDM18 7 +#define IWI_RATE_OFDM24 9 +#define IWI_RATE_OFDM36 11 +#define IWI_RATE_OFDM48 1 +#define IWI_RATE_OFDM54 3 + +struct iwi_hdr { + u_int8_t type; +#define IWI_HDR_TYPE_DATA 0 +#define IWI_HDR_TYPE_COMMAND 1 +#define IWI_HDR_TYPE_NOTIF 3 +#define IWI_HDR_TYPE_FRAME 9 + u_int8_t seq; + u_int8_t flags; +#define IWI_HDR_FLAG_IRQ 0x04 + u_int8_t reserved; +} __packed; + +struct iwi_notif { + u_int32_t reserved[2]; + u_int8_t type; +#define IWI_NOTIF_TYPE_ASSOCIATION 10 +#define IWI_NOTIF_TYPE_AUTHENTICATION 11 +#define IWI_NOTIF_TYPE_SCAN_CHANNEL 12 +#define IWI_NOTIF_TYPE_SCAN_COMPLETE 13 +#define IWI_NOTIF_TYPE_BEACON 17 +#define IWI_NOTIF_TYPE_CALIBRATION 20 +#define IWI_NOTIF_TYPE_NOISE 25 + u_int8_t flags; + u_int16_t len; +} __packed; + +/* structure for notification IWI_NOTIF_TYPE_NOISE */ +struct iwi_notif_noise { + u_int32_t value; +} __packed; + +/* structure for notification IWI_NOTIF_TYPE_AUTHENTICATION */ +struct iwi_notif_authentication { + u_int8_t state; +#define IWI_DEAUTHENTICATED 0 +#define IWI_AUTHENTICATED 9 +} __packed; + +/* structure for notification IWI_NOTIF_TYPE_ASSOCIATION */ +struct iwi_notif_association { + u_int8_t state; +#define IWI_DEASSOCIATED 0 +#define IWI_ASSOCIATED 12 + struct ieee80211_frame frame; + u_int16_t capinfo; + u_int16_t status; + u_int16_t associd; +} __packed; + +/* structure for notification IWI_NOTIF_TYPE_SCAN_CHANNEL */ +struct iwi_notif_scan_channel { + u_int8_t nchan; + u_int8_t reserved[47]; +} __packed; + +/* structure for notification IWI_NOTIF_TYPE_SCAN_COMPLETE */ +struct iwi_notif_scan_complete { + u_int8_t type; + u_int8_t nchan; + u_int8_t status; + u_int8_t reserved; +} __packed; + +/* received frame header */ +struct iwi_frame { + u_int32_t reserved1; + u_int8_t parent_tsf[4]; + u_int8_t chan; + u_int8_t status; + u_int8_t rate; + u_int8_t rssi; /* receiver signal strength indicator */ + u_int8_t agc; /* automatic gain control */ + u_int8_t rssi_dbm; + u_int16_t signal; + u_int16_t noise; + u_int8_t antenna; + u_int8_t control; + u_int8_t rtscts_rate; + u_int8_t rtscts_seen; + u_int16_t len; +} __packed; + +/* header for transmission */ +struct iwi_tx_desc { + struct iwi_hdr hdr; + u_int32_t work_area_ptr; + u_int8_t station_number; /* 0 for BSS */ + u_int8_t reserved1[3]; + u_int8_t cmd; +#define IWI_DATA_CMD_TX 0x0b + u_int8_t seq; + u_int16_t len; + u_int8_t priority; + u_int8_t flags; +#define IWI_DATA_FLAG_SHPREAMBLE 0x04 +#define IWI_DATA_FLAG_NO_WEP 0x20 +#define IWI_DATA_FLAG_NEED_ACK 0x80 + u_int8_t xflags; + u_int8_t wep_txkey; + u_int8_t wepkey[IEEE80211_KEYBUF_SIZE]; + u_int8_t rate; + u_int8_t antenna; + u_int8_t reserved2[10]; + + struct ieee80211_qosframe_addr4 wh; + u_int32_t iv[2]; + + u_int32_t nseg; +#define IWI_MAX_NSEG 6 + u_int32_t seg_addr[IWI_MAX_NSEG]; + u_int16_t seg_len[IWI_MAX_NSEG]; +} __packed; + +/* command */ +struct iwi_cmd_desc { + struct iwi_hdr hdr; + u_int8_t type; +#define IWI_CMD_ENABLE 2 +#define IWI_CMD_SET_CONFIGURATION 6 +#define IWI_CMD_SET_ESSID 8 +#define IWI_CMD_SET_MAC_ADDRESS 11 +#define IWI_CMD_SET_RTS_THRESHOLD 15 +#define IWI_CMD_SET_POWER_MODE 17 +#define IWI_CMD_SET_WEP_KEY 18 +#define IWI_CMD_SCAN 20 +#define IWI_CMD_ASSOCIATE 21 +#define IWI_CMD_SET_RATES 22 +#define IWI_CMD_SCAN_ABORT 23 +#define IWI_CMD_DISABLE 33 +#define IWI_CMD_SET_IV 34 +#define IWI_CMD_SET_TX_POWER 35 +#define IWI_CMD_SET_SENSITIVITY 42 + u_int8_t len; + u_int16_t reserved; + u_int8_t data[120]; +} __packed; + +/* constants for 'mode' fields */ +#define IWI_MODE_11A 0 +#define IWI_MODE_11B 1 +#define IWI_MODE_11G 2 + +/* macro for command IWI_CMD_SET_SENSITIVITY */ +#define IWI_RSSIDBM2RAW(rssi) ((rssi) - 112) + +/* possible values for command IWI_CMD_SET_POWER_MODE */ +#define IWI_POWER_MODE_CAM 0 + +/* structure for command IWI_CMD_SET_RATES */ +struct iwi_rateset { + u_int8_t mode; + u_int8_t nrates; + u_int8_t type; +#define IWI_RATESET_TYPE_NEGOTIATED 0 +#define IWI_RATESET_TYPE_SUPPORTED 1 + u_int8_t reserved; + u_int8_t rates[12]; +} __packed; + +/* structure for command IWI_CMD_SET_TX_POWER */ +struct iwi_txpower { + u_int8_t nchan; + u_int8_t mode; + struct { + u_int8_t chan; + u_int8_t power; +#define IWI_TXPOWER_MAX 20 +#define IWI_TXPOWER_RATIO (IEEE80211_TXPOWER_MAX / IWI_TXPOWER_MAX) + } __packed chan[37]; +} __packed; + +/* structure for command IWI_CMD_ASSOCIATE */ +struct iwi_associate { + u_int8_t chan; + u_int8_t auth; +#define IWI_AUTH_OPEN 0 +#define IWI_AUTH_SHARED 1 +#define IWI_AUTH_NONE 3 + u_int8_t type; + u_int8_t reserved; + u_int16_t policy_support; + u_int8_t plen; + u_int8_t mode; + u_int8_t bssid[IEEE80211_ADDR_LEN]; + u_int8_t tstamp[8]; + u_int16_t capinfo; + u_int16_t lintval; + u_int16_t intval; + u_int8_t dst[IEEE80211_ADDR_LEN]; + u_int16_t atim_window; + u_int8_t smr; + u_int8_t reserved1; + u_int16_t reserved2; +} __packed; + +/* structure for command IWI_CMD_SCAN */ +struct iwi_scan { + u_int8_t type; +#define IWI_SCAN_PASSIVE_TILL_FIRST_BEACON 0 +#define IWI_SCAN_PASSIVE_FULL_DWELL 1 +#define IWI_SCAN_TYPE_DIRECT 2 +#define IWI_SCAN_TYPE_BROADCAST 3 +#define IWI_SCAN_TYPE_BROADCAST_AND_DIRECT 4 + u_int16_t intval; + u_int8_t channels[54]; +#define IWI_CHAN_5GHZ (0 << 6) +#define IWI_CHAN_2GHZ (1 << 6) + u_int8_t reserved[3]; +} __packed; + +/* structure for command IWI_CMD_SET_CONFIGURATION */ +struct iwi_configuration { + u_int8_t bluetooth_coexistence; + u_int8_t reserved1; + u_int8_t answer_broadcast_probe_req; + u_int8_t allow_invalid_frames; + u_int8_t enable_multicast; + u_int8_t exclude_unicast_unencrypted; + u_int8_t disable_unicast_decryption; + u_int8_t exclude_multicast_unencrypted; + u_int8_t disable_multicast_decryption; + u_int8_t antenna_diversity; + u_int8_t pass_crc_to_host; + u_int8_t bg_autodetect; + u_int8_t enable_cts_to_self; + u_int8_t enable_multicast_filtering; + u_int8_t bluetooth_threshold; + u_int8_t reserved2; + u_int8_t allow_beacon_and_probe_resp; + u_int8_t allow_mgt; + u_int8_t noise_reported; + u_int8_t reserved3; +} __packed; + +/* structure for command IWI_CMD_SET_WEP_KEY */ +struct iwi_wep_key { + u_int8_t cmd; +#define IWI_WEP_KEY_CMD_SETKEY 0x08 + u_int8_t seq; + u_int8_t idx; + u_int8_t len; + u_int8_t key[IEEE80211_KEYBUF_SIZE]; +} __packed; + +/* EEPROM = Electrically Erasable Programmable Read-Only Memory */ + +#define IWI_MEM_EEPROM_CTL 0x00300040 + +#define IWI_EEPROM_MAC 0x21 + +#define IWI_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ + +#define IWI_EEPROM_C (1 << 0) /* Serial Clock */ +#define IWI_EEPROM_S (1 << 1) /* Chip Select */ +#define IWI_EEPROM_D (1 << 2) /* Serial data input */ +#define IWI_EEPROM_Q (1 << 4) /* Serial data output */ + +#define IWI_EEPROM_SHIFT_D 2 +#define IWI_EEPROM_SHIFT_Q 4 + +/* + * control and status registers access macros + */ +#define CSR_READ_1(sc, reg) \ + bus_space_read_1((sc)->sc_st, (sc)->sc_sh, (reg)) + +#define CSR_READ_2(sc, reg) \ + bus_space_read_2((sc)->sc_st, (sc)->sc_sh, (reg)) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + +#define CSR_READ_REGION_4(sc, offset, datap, count) \ + bus_space_read_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +/* + * indirect memory space access macros + */ +#define MEM_WRITE_1(sc, addr, val) do { \ + CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)); \ + CSR_WRITE_1((sc), IWI_CSR_INDIRECT_DATA, (val)); \ +} while (/* CONSTCOND */0) + +#define MEM_WRITE_2(sc, addr, val) do { \ + CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)); \ + CSR_WRITE_2((sc), IWI_CSR_INDIRECT_DATA, (val)); \ +} while (/* CONSTCOND */0) + +#define MEM_WRITE_4(sc, addr, val) do { \ + CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)); \ + CSR_WRITE_4((sc), IWI_CSR_INDIRECT_DATA, (val)); \ +} while (/* CONSTCOND */0) + +#define MEM_WRITE_MULTI_1(sc, addr, buf, len) do { \ + CSR_WRITE_4((sc), IWI_CSR_INDIRECT_ADDR, (addr)); \ + CSR_WRITE_MULTI_1((sc), IWI_CSR_INDIRECT_DATA, (buf), (len)); \ +} while (/* CONSTCOND */0) + + +#define IWI_FW_ERROR_OK 0 +#define IWI_FW_ERROR_FAIL 1 +#define IWI_FW_ERROR_MEMORY_UNDERFLOW 2 +#define IWI_FW_ERROR_MEMORY_OVERFLOW 3 +#define IWI_FW_ERROR_BAD_PARAM 4 +#define IWI_FW_ERROR_BAD_CHECKSUM 5 +#define IWI_FW_ERROR_NMI_INTERRUPT 6 +#define IWI_FW_ERROR_BAD_DATABASE 7 +#define IWI_FW_ERROR_ALLOC_FAIL 8 +#define IWI_FW_ERROR_DMA_UNDERRUN 9 +#define IWI_FW_ERROR_DMA_STATUS 10 +#define IWI_FW_ERROR_DINOSTATUS_ERROR 11 +#define IWI_FW_ERROR_EEPROMSTATUS_ERROR 12 +#define IWI_FW_ERROR_SYSASSERT 13 +#define IWI_FW_ERROR_FATAL_ERROR 14 + + +#define IWI_FW_EVENT_ELEM_SIZE (3 * sizeof(u_int32_t)) +#define IWI_FW_EVENT_START_OFFSET (sizeof(u_int32_t) + 2 * sizeof(u_int16_t)) +#define IWI_FW_ERROR_ELEM_SIZE (7 * sizeof(u_int32_t)) +#define IWI_FW_ERROR_START_OFFSET (1 * sizeof(u_int32_t)) + + +#define IWI_FW_MAX_STATIONS 32 + +struct iwi_fw_station { + u_int8_t mac[ETHER_ADDR_LEN]; + u_int8_t reserved; + u_int8_t support_mode; +} __packed; + + + +/* + * EEPROM access macro + */ +#define IWI_EEPROM_CTL(sc, val) do { \ + MEM_WRITE_4((sc), IWI_MEM_EEPROM_CTL, (val)); \ + DELAY(IWI_EEPROM_DELAY); \ +} while (/* CONSTCOND */0) diff --git a/sys/dev/netif/iwi/if_iwivar.h b/sys/dev/netif/iwi/if_iwivar.h new file mode 100644 index 0000000000..18fc4fd080 --- /dev/null +++ b/sys/dev/netif/iwi/if_iwivar.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2004, 2005 + * Damien Bergamini . + * Copyright (c) 2004, 2005 + * Andrew Atrens . + * + * 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 unmodified, 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 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 THE AUTHOR OR 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. + * + * $DragonFly: src/sys/dev/netif/iwi/if_iwivar.h,v 1.1 2005/03/06 05:02:02 dillon Exp $ + */ + +struct iwi_firmware { + void *boot; + int boot_size; + void *ucode; + int ucode_size; + void *main; + int main_size; +}; + +struct iwi_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + u_int8_t wr_flags; + u_int8_t wr_rate; + u_int16_t wr_chan_freq; + u_int16_t wr_chan_flags; + u_int8_t wr_antsignal; + u_int8_t wr_antnoise; + u_int8_t wr_antenna; +}; + +#define IWI_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct iwi_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + u_int8_t wt_flags; + u_int16_t wt_chan_freq; + u_int16_t wt_chan_flags; +}; + +#define IWI_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct iwi_softc { + struct ieee80211com sc_ic; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + device_t sc_dev; + + + struct iwi_firmware fw; + u_int32_t flags; +#define IWI_FLAG_FW_CACHED (1 << 0) +#define IWI_FLAG_FW_IBSS (1 << 1) +#define IWI_FLAG_FW_INITED (1 << 2) +#define IWI_FLAG_SCANNING (1 << 3) +#define IWI_FLAG_SCAN_COMPLETE (1 << 4) +#define IWI_FLAG_SCAN_ABORT (1 << 5) +#define IWI_FLAG_ASSOCIATED (1 << 6) +#define IWI_FLAG_RF_DISABLED (1 << 7) +#define IWI_FLAG_RESET (1 << 8) +#define IWI_FLAG_EXIT (1 << 9) + + struct iwi_tx_desc *tx_desc; + bus_dma_tag_t iwi_parent_tag; + bus_dma_tag_t tx_ring_dmat; + bus_dmamap_t tx_ring_map; + bus_addr_t tx_ring_pa; + bus_dma_tag_t tx_buf_dmat; + + struct iwi_tx_buf { + bus_dmamap_t map; + struct mbuf *m; + struct ieee80211_node *ni; + } tx_buf[IWI_TX_RING_SIZE]; + + int tx_cur; + int tx_old; + int tx_queued; + + struct iwi_cmd_desc *cmd_desc; + bus_dma_tag_t cmd_ring_dmat; + bus_dmamap_t cmd_ring_map; + bus_addr_t cmd_ring_pa; + int cmd_cur; + + bus_dma_tag_t rx_buf_dmat; + + struct iwi_rx_buf { + bus_dmamap_t map; + bus_addr_t physaddr; + struct mbuf *m; + } rx_buf[IWI_RX_RING_SIZE]; + + int rx_cur; + + struct resource *irq; + struct resource *mem; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + void *sc_ih; + + int authmode; + + int sc_tx_timer; + +#if NBPFILTER > 0 + struct bpf_if *sc_drvbpf; + + union { + struct iwi_rx_radiotap_header th; + u_int8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct iwi_tx_radiotap_header th; + u_int8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th + int sc_txtap_len; +#endif + int num_stations; + u_int8_t stations[IWI_FW_MAX_STATIONS][ETHER_ADDR_LEN]; + + struct lwkt_token sc_lock; + struct lwkt_token sc_intrlock; + + struct sysctl_ctx_list sysctl_ctx; + struct sysctl_oid *sysctl_tree; + + int debug_level; + + int enable_bg_autodetect; + int enable_bt_coexist; + int enable_cts_to_self; + int antenna_diversity; /* 1 = A, 3 = B, 0 = A + B */ + int enable_neg_best_first; + int disable_unicast_decryption; + int disable_multicast_decryption; + + struct thread *event_thread; + + struct iwi_associate assoc; + + int scan_counter; + +}; + +#define SIOCSLOADFW _IOW('i', 137, struct ifreq) +#define SIOCSLOADIBSSFW _IOW('i', 138, struct ifreq) +#define SIOCSKILLFW _IOW('i', 139, struct ifreq) + +#define IWI_LOCK_INIT(tok) lwkt_token_init(tok) +#define IWI_LOCK_DESTROY(tok) lwkt_token_uninit(tok) + +#define IWI_LOCK_INFO struct lwkt_tokref tokinfo +#define IWI_INTRLOCK_INFO struct lwkt_tokref intrtokinfo +#define IWI_INTRLOCK(_sc) lwkt_gettoken(&intrtokinfo,(&(_sc)->sc_intrlock)) +#define IWI_INTRUNLOCK(SC) lwkt_reltoken(&intrtokinfo) +#define IWI_LOCK(_sc) lwkt_gettoken(&tokinfo,&((_sc)->sc_lock)) +#define IWI_UNLOCK(SC) lwkt_reltoken(&tokinfo) + +/* + * Holding a token is not enough for iwi_tx_start() the DMA send + * routine. Revert back to the old ipl mechanism for now. + */ + +#define IWI_IPLLOCK_INFO int saved_ipl_level +#define IWI_IPLLOCK(_sc) saved_ipl_level = splimp() +#define IWI_IPLUNLOCK(_sc) splx(saved_ipl_level) + +/* tsleepable events */ +#define IWI_FW_WAKE_MONITOR(sc) (sc + 1) +#define IWI_FW_INITIALIZED(sc) (sc + 2) +#define IWI_FW_CMD_ACKED(sc) (sc + 3) +#define IWI_FW_SCAN_COMPLETED(sc) (sc + 4) +#define IWI_FW_DEASSOCIATED(sc) (sc + 5) +#define IWI_FW_MON_EXIT(sc) (sc + 6) + diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index b90fed73e8..2b8a8fedb2 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,6 +1,6 @@ # From: @(#)Makefile 5.20 (Berkeley) 6/12/93 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $ -# $DragonFly: src/usr.sbin/Makefile,v 1.21 2005/02/19 01:46:12 swildner Exp $ +# $DragonFly: src/usr.sbin/Makefile,v 1.22 2005/03/06 05:01:53 dillon Exp $ # XXX MISSING: mkproto SUBDIR= IPXrouted \ @@ -40,6 +40,7 @@ SUBDIR= IPXrouted \ ifmcstat \ inetd \ iostat \ + iwicontrol \ jail \ jexec \ jls \ diff --git a/usr.sbin/iwicontrol/Makefile b/usr.sbin/iwicontrol/Makefile new file mode 100644 index 0000000000..6091c6f032 --- /dev/null +++ b/usr.sbin/iwicontrol/Makefile @@ -0,0 +1,8 @@ +# $DragonFly: src/usr.sbin/iwicontrol/Makefile,v 1.1 2005/03/06 05:02:03 dillon Exp $ +# +PROG = iwicontrol +MAN = iwicontrol.8 + +WARNS?= 6 + +.include diff --git a/usr.sbin/iwicontrol/iwicontrol.8 b/usr.sbin/iwicontrol/iwicontrol.8 new file mode 100644 index 0000000000..ca5e52dc4a --- /dev/null +++ b/usr.sbin/iwicontrol/iwicontrol.8 @@ -0,0 +1,102 @@ +.\" +.\" Copyright (c) 2004, 2005 +.\" Damien Bergamini . +.\" Andrew Atrens . +.\" +.\" 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 unmodified, 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 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 THE AUTHOR OR 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. +.\" +.\" $DragonFly: src/usr.sbin/iwicontrol/iwicontrol.8,v 1.1 2005/03/06 05:02:03 dillon Exp $ +.\" +.Dd September 26, 2004 +.Os +.Dt IWICONTROL 8 +.Sh NAME +.Nm iwicontrol +.Nd configure Intel(R) PRO/Wireless 2200BG/2915ABG network adapters +.Sh SYNOPSIS +.Nm +.Op Fl i +.Ar iface +.Nm +.Op Fl i +.Ar iface Fl d Ar directory +.Op Fl m Ar bss|ibss +.Nm +.Op Fl i +.Ar iface Fl k +(kill firmware) +.Nm +.Op Fl i +.Ar iface Fl r +(display radio transmitter state) +.Nm +.Op Fl i +.Ar iface Fl D +(dump firmware event and error logs) +.Sh DESCRIPTION +The +.Nm +utility controls the operation of Intel(R) PRO/Wireless 2200BG/2915ABG +networking devices via +.Xr iwi 4 +driver. +.Pp +You should not use this program to configure IEEE 802.11 parameters. Use +.Xr ifconfig 8 +instead. +.Sh OPTIONS +The options are as follows: +.Bl -tag -width indent +.It Oo Fl i Oc Ar iface +Displays adapter's internal statistics. +.It Oo Fl i Oc Ar iface Fl d Ar directory Oo Fl m Ar bss|ibss Oc +Download firmware binary image to the adapter. The image is read from the +.Ar directory +directory. By default, the firmware binary image for BSS (aka infrastructure +mode) mode is downloaded unless the +.Fl m +flag is given. +.It Oo Fl i Oc Ar iface Fl k +Kill the firmware and reset the adapter. +.It Oo Fl i Oc Ar iface Fl r +Displays the radio transmitter state (on or off). +.It Oo Fl i Oc Ar iface Fl D +Dumps the contents of the firmware event and error logs. Information is +mostly in hexadecimal form and as such isn't overly useful (yet). +.El +.Sh SEE ALSO +.Xr iwi 4 , +.Xr ifconfig 8 +.Sh AUTHORS +The +.Nm +utility and this man page were written by +.An Damien Bergamini Aq damien.bergamini@free.fr . +.Pp +The not-so-useful-yet +.Fl D +option and its write up were added by +.An Andrew Atrens Aq atrens@nortelnetworks.com . + diff --git a/usr.sbin/iwicontrol/iwicontrol.c b/usr.sbin/iwicontrol/iwicontrol.c new file mode 100644 index 0000000000..78fae47947 --- /dev/null +++ b/usr.sbin/iwicontrol/iwicontrol.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2004, 2005 + * Damien Bergamini . + * Copyright (c) 2004, 2005 + * Andrew Atrens . + * + * 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 unmodified, 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 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 THE AUTHOR OR 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. + * + * $DragonFly: src/usr.sbin/iwicontrol/iwicontrol.c,v 1.1 2005/03/06 05:02:03 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIOCSLOADFW _IOW('i', 137, struct ifreq) +#define SIOCSLOADIBSSFW _IOW('i', 138, struct ifreq) +#define SIOCSKILLFW _IOW('i', 139, struct ifreq) + +struct firmware { + char *boot; + int boot_size; + char *ucode; + int ucode_size; + char *main; + int main_size; +}; + +struct header { + u_int32_t version; + u_int32_t mode; +} __attribute__((__packed__)); + +static void usage(void); +static int do_req(const char *, unsigned long, void *); +static void mmap_file(const char *, char **, size_t *); +static void load_firmware(const char *, const char *, const char *); +static void kill_firmware(const char *); +static void get_radio_state(const char *); +static void get_statistics(const char *); +static void dump_debug(const char *); + +int +main(int argc, char **argv) +{ + int ch; + const char *iface = NULL, *mode = "bss", *path = NULL; + int noflag = 1, kflag = 0, rflag = 0, dflag = 0; + + if (argc > 1 && argv[1][0] != '-') { + iface = argv[1]; + optind++; + } + + while ((ch = getopt(argc, argv, "d:i:km:rD")) != -1) { + if (ch != 'i') + noflag = 0; + + switch (ch) { + case 'd': + path = optarg; + break; + + case 'i': + iface = optarg; + break; + + case 'k': + kflag = 1; + break; + + case 'm': + mode = optarg; + break; + + case 'r': + rflag = 1; + break; + + case 'D': + dflag = 1; + break; + + default: + usage(); + } + } + + if (iface == NULL) + usage(); + + if (kflag && (path != NULL || rflag)) + usage(); + + if (kflag) + kill_firmware(iface); + + if (path != NULL) + load_firmware(iface, path, mode); + + if (rflag) + get_radio_state(iface); + + if (dflag) + dump_debug(iface); + + if (noflag) + get_statistics(iface); + + return EX_OK; +} + +extern char *__progname; + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: %s iface\n" + "\t%s iface -d path [-m bss|ibss]\n" + "\t%s iface -k\n" + "\t%s iface -r\n", __progname, __progname, __progname, + __progname); + + exit(EX_USAGE); +} + +static int +do_req(const char *iface, unsigned long req, void *data) +{ + int s; + struct ifreq ifr; + int error; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + err(EX_OSERR, "Can't create socket"); + + (void)memset(&ifr, 0, sizeof ifr); + (void)strncpy(ifr.ifr_name, iface, sizeof ifr.ifr_name); + ifr.ifr_data = data; + error = ioctl(s, req, &ifr); + + (void)close(s); + + return error; +} + +static void +mmap_file(const char *filename, char **addr, size_t *len) +{ + int fd; + struct stat st; + + if ((fd = open(filename, O_RDONLY)) == -1) + err(EX_OSERR, "%s", filename); + + if (fstat(fd, &st) == -1) + err(EX_OSERR, "Unable to stat %s", filename); + + *len = st.st_size; + + if ((*addr = mmap(NULL, st.st_size, PROT_READ, 0, fd, 0)) == NULL) + err(EX_OSERR, "Can't map %s into memory", filename); + + *addr += sizeof (struct header); + *len -= sizeof (struct header); + + (void)close(fd); +} + +static void +load_firmware(const char *iface, const char *path, const char *mode) +{ + char filename[FILENAME_MAX]; + struct firmware fw; + + (void)snprintf(filename, sizeof filename, "%s/iwi-boot.fw", path); + mmap_file(filename, &fw.boot, &fw.boot_size); + + (void)snprintf(filename, sizeof filename, "%s/iwi-ucode-%s.fw", path, + mode); + mmap_file(filename, &fw.ucode, &fw.ucode_size); + + (void)snprintf(filename, sizeof filename, "%s/iwi-%s.fw", path, mode); + mmap_file(filename, &fw.main, &fw.main_size); + + if (do_req(iface, strstr(mode,"ibss") + ? SIOCSLOADIBSSFW : SIOCSLOADFW, &fw) == -1) + err(EX_OSERR, "Can't load firmware to driver"); +} + +static void +kill_firmware(const char *iface) +{ + if (do_req(iface, SIOCSKILLFW, NULL) == -1) + err(EX_OSERR, "Can't kill firmware"); +} + +static void +dump_debug(const char *iface) +{ + int dump = 1, len = sizeof dump; + char oid_name[128]; + snprintf(oid_name, sizeof oid_name, "hw.%s.firmware_logs", iface ); + if (sysctlbyname(oid_name, 0, 0, &dump, len) == -1) + err(EX_OSERR, "Can't dump firmware logs"); + (void)printf("All firmware logs dumped.\n"); +} + + +static void +get_radio_state(const char *iface) +{ + int radio, len; + char oid_name[128]; + + snprintf(oid_name, sizeof oid_name, "hw.%s.radio", iface ); + len = sizeof radio; + if (sysctlbyname(oid_name, &radio, &len, NULL, 0) == -1) + err(EX_OSERR, "Can't get radio transmitter state"); + + (void)printf("Radio is %s\n", radio ? "ON" : "OFF"); +} + +struct statistic { + int index; + const char *desc; +}; + +static const struct statistic tbl[] = { + { 1, "Current transmission rate" }, + { 2, "Fragmentation threshold" }, + { 3, "RTS threshold" }, + { 4, "Number of frames submitted for transfer" }, + { 5, "Number of frames transmitted" }, + { 6, "Number of unicast frames transmitted" }, + { 7, "Number of unicast 802.11b frames transmitted at 1Mb/s" }, + { 8, "Number of unicast 802.11b frames transmitted at 2Mb/s" }, + { 9, "Number of unicast 802.11b frames transmitted at 5.5Mb/s" }, + { 10, "Number of unicast 802.11b frames transmitted at 11Mb/s" }, + + { 19, "Number of unicast 802.11g frames transmitted at 1Mb/s" }, + { 20, "Number of unicast 802.11g frames transmitted at 2Mb/s" }, + { 21, "Number of unicast 802.11g frames transmitted at 5.5Mb/s" }, + { 22, "Number of unicast 802.11g frames transmitted at 6Mb/s" }, + { 23, "Number of unicast 802.11g frames transmitted at 9Mb/s" }, + { 24, "Number of unicast 802.11g frames transmitted at 11Mb/s" }, + { 25, "Number of unicast 802.11g frames transmitted at 12Mb/s" }, + { 26, "Number of unicast 802.11g frames transmitted at 18Mb/s" }, + { 27, "Number of unicast 802.11g frames transmitted at 24Mb/s" }, + { 28, "Number of unicast 802.11g frames transmitted at 36Mb/s" }, + { 29, "Number of unicast 802.11g frames transmitted at 48Mb/s" }, + { 30, "Number of unicast 802.11g frames transmitted at 54Mb/s" }, + { 31, "Number of multicast frames transmitted" }, + { 32, "Number of multicast 802.11b frames transmitted at 1Mb/s" }, + { 33, "Number of multicast 802.11b frames transmitted at 2Mb/s" }, + { 34, "Number of multicast 802.11b frames transmitted at 5.5Mb/s" }, + { 35, "Number of multicast 802.11b frames transmitted at 11Mb/s" }, + + { 44, "Number of multicast 802.11g frames transmitted at 1Mb/s" }, + { 45, "Number of multicast 802.11g frames transmitted at 2Mb/s" }, + { 46, "Number of multicast 802.11g frames transmitted at 5.5Mb/s" }, + { 47, "Number of multicast 802.11g frames transmitted at 6Mb/s" }, + { 48, "Number of multicast 802.11g frames transmitted at 9Mb/s" }, + { 49, "Number of multicast 802.11g frames transmitted at 11Mb/s" }, + { 50, "Number of multicast 802.11g frames transmitted at 12Mb/s" }, + { 51, "Number of multicast 802.11g frames transmitted at 18Mb/s" }, + { 52, "Number of multicast 802.11g frames transmitted at 24Mb/s" }, + { 53, "Number of multicast 802.11g frames transmitted at 36Mb/s" }, + { 54, "Number of multicast 802.11g frames transmitted at 48Mb/s" }, + { 55, "Number of multicast 802.11g frames transmitted at 54Mb/s" }, + { 56, "Number of transmission retries" }, + { 57, "Number of transmission failures" }, + { 58, "Number of frames with a bad CRC received" }, + + { 61, "Number of full scans" }, + { 62, "Number of partial scans" }, + + { 64, "Number of bytes transmitted" }, + { 65, "Current RSSI" }, + { 66, "Number of beacons received" }, + { 67, "Number of beacons missed" }, + + { 0, NULL } +}; + +static void +get_statistics(const char *iface) +{ + static u_int32_t stats[256]; + char oid_name[128]; + const struct statistic *stt; + int len; + + snprintf(oid_name, sizeof oid_name, "hw.%s.stats", iface ); + len = sizeof stats; + if (sysctlbyname(oid_name, stats, &len, NULL, 0) == -1) + err(EX_OSERR, "Can't retrieve statistics"); + + for (stt = tbl; stt->index != 0; stt++) + (void)printf("%-60s[%u]\n", stt->desc, stats[stt->index]); +} + -- 2.41.0