summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ifconfig/Makefile9
-rw-r--r--sbin/ifconfig/ifconfig.8715
-rw-r--r--sbin/ifconfig/ifieee80211.c1333
-rw-r--r--sbin/ifconfig/ifmedia.c16
-rw-r--r--sbin/ifconfig/regdomain.c636
-rw-r--r--sbin/ifconfig/regdomain.h112
-rw-r--r--share/man/man4/ath.4121
-rw-r--r--share/man/man4/ipw.471
-rw-r--r--share/man/man4/iwi.471
-rw-r--r--share/man/man4/malo.474
-rw-r--r--share/man/man4/ral.467
-rw-r--r--share/man/man4/rum.472
-rw-r--r--share/man/man4/ural.452
-rw-r--r--share/man/man4/wi.4178
-rw-r--r--share/man/man4/wlan.4130
-rw-r--r--share/man/man4/wlan_acl.411
-rw-r--r--share/man/man4/wlan_amrr.412
-rw-r--r--share/man/man4/wlan_xauth.415
-rw-r--r--share/man/man4/wpi.491
-rw-r--r--share/man/man4/zyd.463
-rw-r--r--share/man/man5/Makefile1
-rw-r--r--share/man/man5/regdomain.548
-rw-r--r--sys/amd64/conf/GENERIC2
-rw-r--r--sys/arm/conf/AVILA2
-rw-r--r--sys/arm/conf/HL2002
-rw-r--r--sys/arm/conf/KB920X2
-rw-r--r--sys/conf/NOTES2
-rw-r--r--sys/conf/files16
-rw-r--r--sys/conf/options10
-rw-r--r--sys/contrib/dev/ral/LICENSE16
-rw-r--r--sys/contrib/dev/ral/Makefile36
-rw-r--r--sys/contrib/dev/ral/rt2561.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2561s.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2661.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2661_ucode.h (renamed from sys/dev/ral/rt2661_ucode.h)758
-rw-r--r--sys/contrib/dev/ral/rt2860.fw.uu202
-rw-r--r--sys/dev/ath/ah_osdep.c2
-rw-r--r--sys/dev/ath/ah_osdep.h2
-rw-r--r--sys/dev/ath/ath_rate/amrr/amrr.c146
-rw-r--r--sys/dev/ath/ath_rate/amrr/amrr.h4
-rw-r--r--sys/dev/ath/ath_rate/onoe/onoe.c117
-rw-r--r--sys/dev/ath/ath_rate/onoe/onoe.h5
-rw-r--r--sys/dev/ath/ath_rate/sample/sample.c132
-rw-r--r--sys/dev/ath/ath_rate/sample/sample.h3
-rw-r--r--sys/dev/ath/if_ath.c2175
-rw-r--r--sys/dev/ath/if_ath_pci.c2
-rw-r--r--sys/dev/ath/if_athioctl.h2
-rw-r--r--sys/dev/ath/if_athrate.h4
-rw-r--r--sys/dev/ath/if_athvar.h118
-rw-r--r--sys/dev/if_ndis/if_ndis.c549
-rw-r--r--sys/dev/if_ndis/if_ndisvar.h9
-rw-r--r--sys/dev/ipw/if_ipw.c1029
-rw-r--r--sys/dev/ipw/if_ipwvar.h52
-rw-r--r--sys/dev/iwi/if_iwi.c1072
-rw-r--r--sys/dev/iwi/if_iwivar.h65
-rw-r--r--sys/dev/malo/if_malo.c597
-rw-r--r--sys/dev/malo/if_malo.h14
-rw-r--r--sys/dev/ral/if_ral_pci.c4
-rw-r--r--sys/dev/ral/if_ralrate.c192
-rw-r--r--sys/dev/ral/rt2560.c1221
-rw-r--r--sys/dev/ral/rt2560reg.h2
-rw-r--r--sys/dev/ral/rt2560var.h46
-rw-r--r--sys/dev/ral/rt2661.c1249
-rw-r--r--sys/dev/ral/rt2661var.h48
-rw-r--r--sys/dev/usb/if_rum.c978
-rw-r--r--sys/dev/usb/if_rumvar.h45
-rw-r--r--sys/dev/usb/if_ural.c949
-rw-r--r--sys/dev/usb/if_uralvar.h45
-rw-r--r--sys/dev/usb/if_zyd.c559
-rw-r--r--sys/dev/usb/if_zydreg.h21
-rw-r--r--sys/dev/wi/if_wavelan_ieee.h3
-rw-r--r--sys/dev/wi/if_wi.c2815
-rw-r--r--sys/dev/wi/if_wi_pccard.c63
-rw-r--r--sys/dev/wi/if_wi_pci.c7
-rw-r--r--sys/dev/wi/if_wivar.h102
-rw-r--r--sys/dev/wi/spectrum24t_cf.h4327
-rw-r--r--sys/dev/wpi/if_wpi.c747
-rw-r--r--sys/dev/wpi/if_wpivar.h46
-rw-r--r--sys/i386/conf/GENERIC2
-rw-r--r--sys/mips/conf/IDT2
-rw-r--r--sys/modules/Makefile3
-rw-r--r--sys/modules/ath_rate_amrr/Makefile8
-rw-r--r--sys/modules/ath_rate_onoe/Makefile8
-rw-r--r--sys/modules/ath_rate_sample/Makefile8
-rw-r--r--sys/modules/malo/Makefile6
-rw-r--r--sys/modules/ral/Makefile6
-rw-r--r--sys/modules/ralfw/Makefile5
-rw-r--r--sys/modules/ralfw/Makefile.inc15
-rw-r--r--sys/modules/ralfw/rt2561/Makefile5
-rw-r--r--sys/modules/ralfw/rt2561s/Makefile5
-rw-r--r--sys/modules/ralfw/rt2661/Makefile6
-rw-r--r--sys/modules/wlan/Makefile16
-rw-r--r--sys/modules/wlan_acl/Makefile6
-rw-r--r--sys/modules/wlan_amrr/Makefile6
-rw-r--r--sys/modules/wlan_ccmp/Makefile6
-rw-r--r--sys/modules/wlan_rssadapt/Makefile14
-rw-r--r--sys/modules/wlan_scan_ap/Makefile8
-rw-r--r--sys/modules/wlan_scan_sta/Makefile8
-rw-r--r--sys/modules/wlan_tkip/Makefile6
-rw-r--r--sys/modules/wlan_wep/Makefile6
-rw-r--r--sys/modules/wlan_xauth/Makefile6
-rw-r--r--sys/net80211/_ieee80211.h119
-rw-r--r--sys/net80211/ieee80211.c1002
-rw-r--r--sys/net80211/ieee80211.h19
-rw-r--r--sys/net80211/ieee80211_acl.c116
-rw-r--r--sys/net80211/ieee80211_adhoc.c877
-rw-r--r--sys/net80211/ieee80211_adhoc.h35
-rw-r--r--sys/net80211/ieee80211_amrr.c178
-rw-r--r--sys/net80211/ieee80211_amrr.h53
-rw-r--r--sys/net80211/ieee80211_crypto.c256
-rw-r--r--sys/net80211/ieee80211_crypto.h93
-rw-r--r--sys/net80211/ieee80211_crypto_ccmp.c53
-rw-r--r--sys/net80211/ieee80211_crypto_none.c37
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c98
-rw-r--r--sys/net80211/ieee80211_crypto_wep.c48
-rw-r--r--sys/net80211/ieee80211_ddb.c789
-rw-r--r--sys/net80211/ieee80211_dfs.c372
-rw-r--r--sys/net80211/ieee80211_dfs.h57
-rw-r--r--sys/net80211/ieee80211_freebsd.c397
-rw-r--r--sys/net80211/ieee80211_freebsd.h318
-rw-r--r--sys/net80211/ieee80211_hostap.c2236
-rw-r--r--sys/net80211/ieee80211_hostap.h35
-rw-r--r--sys/net80211/ieee80211_ht.c591
-rw-r--r--sys/net80211/ieee80211_ht.h84
-rw-r--r--sys/net80211/ieee80211_input.c3250
-rw-r--r--sys/net80211/ieee80211_input.h156
-rw-r--r--sys/net80211/ieee80211_ioctl.c2511
-rw-r--r--sys/net80211/ieee80211_ioctl.h253
-rw-r--r--sys/net80211/ieee80211_monitor.c136
-rw-r--r--sys/net80211/ieee80211_monitor.h35
-rw-r--r--sys/net80211/ieee80211_node.c1605
-rw-r--r--sys/net80211/ieee80211_node.h257
-rw-r--r--sys/net80211/ieee80211_output.c2026
-rw-r--r--sys/net80211/ieee80211_phy.c472
-rw-r--r--sys/net80211/ieee80211_phy.h149
-rw-r--r--sys/net80211/ieee80211_power.c239
-rw-r--r--sys/net80211/ieee80211_power.h8
-rw-r--r--sys/net80211/ieee80211_proto.c1251
-rw-r--r--sys/net80211/ieee80211_proto.h190
-rw-r--r--sys/net80211/ieee80211_regdomain.c429
-rw-r--r--sys/net80211/ieee80211_regdomain.h96
-rw-r--r--sys/net80211/ieee80211_rssadapt.c273
-rw-r--r--sys/net80211/ieee80211_rssadapt.h (renamed from sys/dev/ral/if_ralrate.h)101
-rw-r--r--sys/net80211/ieee80211_scan.c703
-rw-r--r--sys/net80211/ieee80211_scan.h147
-rw-r--r--sys/net80211/ieee80211_scan_ap.c408
-rw-r--r--sys/net80211/ieee80211_scan_sta.c837
-rw-r--r--sys/net80211/ieee80211_sta.c1647
-rw-r--r--sys/net80211/ieee80211_sta.h36
-rw-r--r--sys/net80211/ieee80211_var.h511
-rw-r--r--sys/net80211/ieee80211_wds.c865
-rw-r--r--sys/net80211/ieee80211_wds.h39
-rw-r--r--sys/net80211/ieee80211_xauth.c37
-rw-r--r--sys/pc98/conf/GENERIC2
-rw-r--r--sys/sparc64/conf/GENERIC2
155 files changed, 29398 insertions, 22417 deletions
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
index 09592ad..1f5cfed 100644
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -23,7 +23,10 @@ SRCS+= ifclone.c # clone device support
SRCS+= ifmac.c # MAC support
SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support
SRCS+= ifvlan.c # SIOC[GS]ETVLAN support
-SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support
+
+SRCS+= ifieee80211.c regdomain.c # SIOC[GS]IEEE80211 support
+DPADD+= ${LIBBSDXML} ${LIBSBUF}
+LDADD+= -lbsdxml -lsbuf
SRCS+= ifcarp.c # SIOC[GS]VH support
SRCS+= ifgroup.c # ...
@@ -34,8 +37,8 @@ SRCS+= iflagg.c # lagg support
.if ${MK_IPX_SUPPORT} != "no" && !defined(RELEASE_CRUNCH)
SRCS+= af_ipx.c # IPX support
-DPADD= ${LIBIPX}
-LDADD= -lipx
+DPADD+= ${LIBIPX}
+LDADD+= -lipx
.endif
MAN= ifconfig.8
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index ffa3ba1..22adfaa 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
.\" $FreeBSD$
.\"
-.Dd February 29, 2008
+.Dd April 11, 2008
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -584,8 +584,137 @@ If the interface was reset when previously marked down,
the hardware will be re-initialized.
.El
.Pp
-The following parameters are specific to IEEE 802.11 wireless interfaces:
+The following parameters are specific to cloning
+IEEE 802.11 wireless interfaces with the
+.Cm create
+request:
+.Bl -tag -width indent
+.It Cm wlandev Ar device
+Use
+.Ar device
+as the parent for the cloned device.
+.It Cm wlanmode Ar mode
+Specify the operating mode for this cloned device.
+.Ar mode
+is one of
+.Cm sta ,
+.Cm ahdemo
+(or
+.Cm adhoc-demo ),
+.Cm ibss ,
+(or
+.Cm adhoc ),
+.Cm ap ,
+(or
+.Cm hostap ),
+.Cm wds ,
+and
+.Cm monitor .
+The operating mode of a cloned interface cannot be changed.
+.It Cm wlanbssid Ar bssid
+The 802.11 mac address to use for the bssid.
+This must be specified at create time for a legacy
+.Cm wds
+device.
+.It Cm wlanaddr Ar address
+The local mac address.
+If this is not specified then a mac address will automatically be assigned
+to the cloned device.
+Typically this address is the same as the address of the parent device
+but if the
+.Cm bssid
+parameter is specified then the driver will craft a unique address for
+the device (if supported).
+.It Cm wdslegacy
+Mark a
+.Cm wds
+device as operating in ``legacy mode''.
+Legacy
+.Cm wds
+devices have a fixed peer relationship and do not, for example, roam
+if their peer stops communicating.
+For completeness a Dynamic WDS (DWDS) interface may marked as
+.Fl wdslegacy .
+.It Cm bssid
+Request a unique local mac address for the cloned device.
+This is only possible if the device supports multiple mac addresses.
+To force use of the parent's mac address use
+.Fl bssid .
+.It Cm beacons
+Mark the cloned interface as depending on hardware support to
+track received beacons.
+To have beacons tracked in software use
+.Fl beacons .
+For
+.Cm hostap
+mode
+.Fl beacons
+can also be used to indicate no beacons should
+be transmitted; this can be useful when creating a WDS configuration but
+.Cm wds
+interfaces can only be created as companions to an access point.
+.El
+.Pp
+The following parameters are specific to IEEE 802.11 wireless interfaces
+cloned with a
+.Cm create
+operation:
.Bl -tag -width indent
+.It Cm ampdu
+Enable sending and receiving AMPDU frames when using 802.11n (default).
+The 802.11n specification states a compliant station must be capable
+of receiving AMPDU frames but transmision is optional.
+Use
+.Fl ampdu
+to disable all use of AMPDU with 802.11n.
+For testing and/or to work around interoperability problems one can use
+.Cm ampdutx
+and
+.Cm ampdurx
+to control use of AMPDU in one direction.
+.It Cm ampdudensity Ar density
+Set the AMPDU density parameter used when operating with 802.11n.
+This parameter controls the inter-packet gap for AMPDU frames.
+The sending device normally controls this setting but a receiving station
+may request wider gaps.
+Legal values for
+.Ar density
+are 0, .25, .5, 1, 2, 4, 8, and 16 (microseconds).
+A value of
+.Cm -
+is treated the same as 0.
+.It Cm ampdulimit Ar limit
+Set the limit on packet size for receiving AMPDU frames when operating
+with 802.11n.
+Legal values for
+.Ar limit
+are 8192, 16384, 32768, and 65536 but one can also specify
+just the unique prefix: 8, 16, 32, 64.
+Note the sender may limit the size of AMPDU frames to be less
+than the maximum specified by the receiving station.
+.It Cm amsdu
+Enable sending and receiving AMSDU frames when using 802.11n.
+By default AMSDU is received but not transmitted.
+Use
+.Fl amsdu
+to disable all use of AMSDU with 802.11n.
+For testing and/or to work around interoperability problems one can use
+.Cm amsdutx
+and
+.Cm amsdurx
+to control use of AMSDU in one direction.
+.It Cm amsdulimit Ar limit
+Set the limit on packet size for sending and receiving AMSDU frames
+when operating with 802.11n.
+Legal values for
+.Ar limit
+are 7935 and 3839 (bytes).
+Note the sender may limit the size of AMSDU frames to be less
+than the maximum specified by the receiving station.
+Note also that devices are not required to support the 7935 limit,
+only 3839 is required by the specification and the larger value
+may require more memory to be dedicated to support functionality
+that is rarely used.
.It Cm apbridge
When operating as an access point, pass packets between
wireless clients directly (default).
@@ -597,7 +726,7 @@ is useful when traffic is to be processed with
packet filtering.
.It Cm authmode Ar mode
Set the desired authentication mode in infrastructure mode.
-Not all adaptors support all modes.
+Not all adapters support all modes.
The set of
valid modes is
.Cm none , open , shared
@@ -725,7 +854,7 @@ or
.Cm -
will give you the default for your adaptor.
Some
-adaptors ignore this setting unless you are in ad-hoc mode.
+adapters ignore this setting unless you are in ad-hoc mode.
Alternatively the frequency, in megahertz, may be specified
instead of the channel number.
.Pp
@@ -770,9 +899,61 @@ a 40MHz HT channel specification may include the location
of the extension channel by appending ``+'' or ``-'' for above and below,
respectively; e.g. ``2437:ht/40+'' specifies 40MHz wide HT operation
with the center channel at frequency 2437 and the extension channel above.
+.It Cm country Ar name
+Set the country code to use in calculating the regulatory constraints
+for operation.
+In particular the set of available channels, how the wireless device
+will operation on the channels, and the maximum transmit power that
+can be used on a channel are defined by this setting.
+Country/Region codes are specified as a 2-character abbreviation
+defined by ISO 3166 or using a longer, but possibly ambiguous, spelling;
+e.g. "ES" and "Spain".
+The set of country codes are taken from /etc/regdomain.xml and can also
+be viewed with the ``list countries'' request.
+Note that not all devices support changing the country code from a default
+setting; typically stored in EEPROM.
+See also
+.Cm regdomain ,
+.Cm indoor ,
+.Cm outdoor ,
+and
+.Cm anywhere .
+.It Cm dfs
+Enable Dynamic Frequency Selection (DFS) as specified in 802.11h.
+DFS embodies several facilities including detection of overlapping
+radar signals, dynamic transmit power control, and channel selection
+according to a least-congested criteria.
+DFS support is mandatory for some 5Ghz frequencies in certain
+locales (e.g. ETSI).
+By default DFS is enabled according to the regulatory definitions
+specified in /etc/regdomain.xml and the curent country code, regdomain,
+and channel.
+Note the underlying device (and driver) must support radar detection
+for full DFS support to work.
+To be fully compliant with the local regulatory agency frequencies that
+require DFS should not be used unless it is fully supported.
+Use
+.Fl dfs
+to disable this functionality for testing.
+.It Cm dotd
+Enable support for the 802.11d specification (default).
+When this support is enabled in station mode, beacon frames that advertise
+a country code different than the currently configured country code will
+cause an event to be dispatched to user applications.
+This event can be used by the station to adopt that country code and
+operate according to the associated regulatory constraints.
+When operating as an access point with 802.11d enabled the beacon and
+probe response frames transmitted will advertise the current regulatory
+domain settings.
+To disable 802.11d use
+.Fl dotd .
.It Cm doth
-Enable inclusion of an 802.11h country information element in beacon
-frames transmitted when operating as an access point.
+Enable 802.11h support including spectrum management.
+When 802.11h is enabled beacon and probe response frames will have
+the SpectrumMgt bit set in the capabilities field and
+country and power constraint information elements will be present.
+802.11h support also includes handling Channel Switch Announcements (CSA)
+which are a mechanism to coordinate channel changes by an access point.
By default 802.11h is enabled if the device is capable.
To disable 802.11h use
.Fl doth .
@@ -811,6 +992,48 @@ channels depending on the regulatory constraints; use the
command to identify the channels where turbo mode may be used.
To disable Dynamic Turbo mode use
.Fl dturbo .
+.It Cm dwds
+Enable Dynamic WDS (DWDS) support.
+DWDS is a facility by which 4-address traffic can be carried between
+stations operating in infrastructure mode.
+A station first associates to an access point and authenticates using
+normal procedures (e.g. WPA).
+Then 4-address frames are passed to carry traffic for stations
+operating on either side of the wireless link.
+DWDS extends the normal WDS mechanism by leveraging existing security
+protocols and eliminating static binding.
+.Pp
+When DWDS is enabled on an access point 4-address frames received from
+an authorized station will generate a ``DWDS discovery'' event to user
+applications.
+This event should be used to create a WDS interface that is bound
+to the remote station (and usually plumbed into a bridge).
+Once the WDS interface is up and running 4-address traffic then logically
+flows through that interface.
+.Pp
+When DWDS is enabled on a station, traffic with a destination address
+different from the peer station are encapsulated in a 4-address frame
+and transmitted to the peer.
+All 4-address traffic uses the security information of the stations
+(e.g. cryptographic keys).
+A station is associated using 802.11n facilities may transport
+4-address traffic using these same mechanisms; this depends on available
+resources and capabilities of the device.
+The DWDS implementation guards against layer 2 routing loops of
+multicast traffic.
+.It Cm ff
+Enable the use of Atheros Fast Frames when communicating with
+another Fast Frames-capable station.
+Fast Frames are an encapsulation technique by which two 802.3
+frames are transmitted in a single 802.11 frame.
+This can noticeably improve throughput but requires that the
+receiving station understand how to decapsulate the frame.
+Fast frame use is negotiated using the Atheros 802.11 vendor-specific
+protocol extension so enabling use is safe when communicating with
+non-Atheros devices.
+By default, use of fast frames is enabled if the device is capable.
+To explicitly disable fast frames, use
+.Fl ff .
.It Cm fragthreshold Ar length
Set the threshold for which transmitted frames are broken into fragments.
The
@@ -824,7 +1047,7 @@ to
or
.Cm -
disables transmit fragmentation.
-Not all adaptors honor the fragmentation threshold.
+Not all adapters honor the fragmentation threshold.
.It Cm hidessid
When operating as an access point, do not broadcast the SSID
in beacon frames or respond to probe request frames unless
@@ -833,19 +1056,76 @@ By default, the SSID is included in beacon frames and
undirected probe request frames are answered.
To re-enable the broadcast of the SSID etc., use
.Fl hidessid .
-.It Cm ff
-Enable the use of Atheros Fast Frames when communicating with
-another Fast Frames-capable station.
-Fast Frames are an encapsulation technique by which two 802.3
-frames are transmitted in a single 802.11 frame.
-This can noticeably improve throughput but requires that the
-receiving station understand how to decapsulate the frame.
-Fast frame use is negotiated using the Atheros 802.11 vendor-specific
-protocol extension so enabling use is safe when communicating with
-non-Atheros devices.
-By default, use of fast frames is enabled if the device is capable.
-To explicitly disable fast frames, use
-.Fl ff .
+.It Cm ht
+Enable use of High Throughput (HT) when using 802.11n (default).
+The 802.11n specification includes mechanisms for operation
+on 20MHz and 40MHz wide channels using different signalling mechanisms
+than specified in 802.11b, 802.11g, and 802.11a.
+Stations negotiate use of these facilities, termed HT20 and HT40,
+when they associate.
+To disable all use of 802.11n use
+.Fl ht .
+To disable use of HT20 (e.g. to force only HT40 use) use
+.Fl ht20 .
+To disable use of HT40 use
+.Fl ht40 .
+.Pp
+HT configuration is used to ``auto promote'' operation
+when several choices are available.
+For example, if a station associates to an 11n-capable access point
+it controls whether the station uses legacy operation, HT20, or HT40.
+When an 11n-capable device is setup as an access point and
+Auto Channel Selection is used to locate a channel to operate on,
+HT configuration controls whether legacy, HT20, or HT40 operation is setup
+on the selected channel.
+If a fixed channel is specified for a station then HT configuration can
+be given as part of the channel specification; e.g. 6:ht/20 to setup
+HT20 operation on channel 6.
+.It Cm htcompat
+Enable use of compatibility support for pre-802.11n devices (default).
+The 802.11n protocol specification went through several incompatible iterations.
+Some vendors implemented 11n support to older specifications that
+will not interoperate with a purely 11n-compliant station.
+In particular the information elements included in management frames
+for old devices are different.
+When compatibility support is enabled both standard and compatible data
+will be provided.
+Stations that associate using the compatiblity mechanisms are flagged
+in ``list sta''.
+To disable compatiblity support use
+.Fl htcompat .
+.It Cm htprotmode Ar technique
+For interfaces operating in 802.11n, use the specified
+.Ar technique
+for protecting HT frames in a mixed legacy/HT network.
+The set of valid techniques is
+.Cm off ,
+and
+.Cm rts
+(RTS/CTS, default).
+Technique names are case insensitive.
+.It Cm inact
+Enable inactivity processing for stations associated to an
+access point (default).
+When operating as an access point the 802.11 layer monitors
+the activity of each associated station.
+When a station is inactive for 5 minutes it will send several
+``probe frames'' to see if the station is still present.
+If no response is received then the station is deauthenticated.
+Applications that prefer to handle this work can disable this
+facility by using
+.Fl inact .
+.It Cm indoor
+Set the location to use in calculating regulatory constraints.
+The location is also advertised in beacon and probe response frames
+when 802.11d is enabled with
+.Cm dotd .
+See also
+.Cm outdoor ,
+.Cm anywhere ,
+.Cm country ,
+and
+.Cm regdomain .
.It Cm list active
Display the list of channels available for use taking into account
any restrictions set with the
@@ -883,6 +1163,9 @@ is another way of requesting this information.
By default a compacted list of channels is displayed; if the
.Fl v
option is specified then all channels are shown.
+.It Cm list countries
+Display the set of country codes and regulatory domains that can be
+used in regulatory configuration.
.It Cm list mac
Display the current MAC Access Control List state.
Each address is prefixed with a character that indicates the
@@ -894,18 +1177,79 @@ indicates the address is denied access,
.Ql *
indicates the address is present but the current policy open
(so the ACL is not consulted).
+.It Cm list regdomain
+Display the current regulatory settings including the available channels
+and transmit power caps.
+.It Cm list roam
+Display the parameters that govern roaming operation.
+.It Cm list txparam
+Display the parameters that govern transmit operation.
+.It Cm list txpower
+Display the transmit power caps for each channel.
.It Cm list scan
Display the access points and/or ad-hoc neighbors
located in the vicinity.
-The
-.Fl v
-flag may be used to display long SSIDs.
-.Fl v
-also causes received information elements to be displayed symbolicaly.
-This information may be updated automatically by the adaptor
-and/or with a
+This information may be updated automatically by the adapter
+with a
.Cm scan
request or through background scanning.
+Depending on the capabilities of the stations the following
+flags can be included in the output:
+.Bl -tag -width 3n
+.It Li A
+Authorized.
+Indicates that the station is permitted to send/receive data frames.
+.It Li E
+Extended Rate Phy (ERP).
+Indicates that the station is operating in an 802.11g network
+using extended transmit rates.
+.It Li H
+High Throughput (HT).
+Indicates that the station is using HT transmit rates.
+If a `+' follows immediately after then the station associated
+using deprecated mechanisms supported only when
+.Cm htcompat
+is enabled.
+.It Li P
+Power Save.
+Indicates that the station is operating in power save mode.
+.It Li Q
+Quality of Service (QoS).
+Indicates that the station is using QoS encapsulation for
+data frame.
+QoS encapsulation is enabled only when WME mode is enabled.
+.It Li T
+Transitional Security Network (TSN).
+Indicates that the station associated using TSN; see also
+.Cm tsn
+below.
+.It Li W
+Wi-Fi Protected Setup (WPS).
+Indicates that the station associated using WPS.
+.El
+.Pp
+By default interesting information elements captured from the neighboring
+stations are displayed at the end of each row.
+Possible elements include:
+.Cm WME
+(station supports WME),
+.Cm WPA
+(station supports WPA),
+.Cm RSN
+(station supports 802.11i/RSN),
+.Cm HTCAP
+(station supports 802.11n/HT communication),
+.Cm ATH
+(station supoprts Atheros protocol extensions),
+.Cm VEN
+(station supports unknown vendor-specific extensions).
+If the
+.Fl v
+flag is used all the information elements and their
+contents will be shown.
+Specifying The
+.Fl v
+flag also enables display of long SSIDs.
.Cm list ap
is another way of requesting this information.
.It Cm list sta
@@ -930,7 +1274,11 @@ Indicates that the station is operating in an 802.11g network
using extended transmit rates.
.It Li H
High Throughput (HT).
-Indicates that the station is using MCS to send/receive frames.
+Indicates that the station is using HT transmit rates.
+If a `+' follows immediately after then the station associated
+using deprecated mechanisms supported only when
+.Cm htcompat
+is enabled.
.It Li P
Power Save.
Indicates that the station is operating in power save mode.
@@ -939,6 +1287,14 @@ Quality of Service (QoS).
Indicates that the station is using QoS encapsulation for
data frame.
QoS encapsulation is enabled only when WME mode is enabled.
+.It Li T
+Transitional Security Network (TSN).
+Indicates that the station associated using TSN; see also
+.Cm tsn
+below.
+.It Li W
+Wi-Fi Protected Setup (WPS).
+Indicates that the station associated using WPS.
.El
.Pp
By default information elements received from associated stations
@@ -953,12 +1309,30 @@ for examining parameters when WME mode is disabled.
See the description of the
.Cm wme
directive for information on the various parameters.
+.It Cm maxretry Ar count
+Set the maximum number of tries to use in sending unicast frames.
+The default setting is 6 but drivers may override this with a value
+they choose.
.It Cm mcastrate Ar rate
Set the rate for transmitting multicast/broadcast frames.
Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s.
This rate should be valid for the current operating conditions;
if an invalid rate is specified drivers are free to chose an
appropriate rate.
+.It Cm mgtrate Ar rate
+Set the rate for transmitting management and/or control frames.
+Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s.
+.It Cm outdoor
+Set the location to use in calculating regulatory constraints.
+The location is also advertised in beacon and probe response frames
+when 802.11d is enabled with
+.Cm dotd .
+See also
+.Cm anywhere ,
+.Cm country ,
+.Cm indoor ,
+and
+.Cm regdomain .
.It Cm powersave
Enable powersave operation.
When operating as a client, the station will conserve power by
@@ -994,6 +1368,60 @@ When operating as an access point in 802.11g mode allow only
permitted to associate).
To allow both 11g and 11b-only stations to associate, use
.Fl pureg .
+.It Cm puren
+When operating as an access point in 802.11n mode allow only
+HT-capable stations to associate (legacy stations are not
+permitted to associate).
+To allow both HT and legacy stations to associate, use
+.Fl puren .
+.It Cm regdomain Ar sku
+Set the regulatory domain to use in calculating the regulatory constraints
+for operation.
+In particular the set of available channels, how the wireless device
+will operation on the channels, and the maximum transmit power that
+can be used on a channel are defined by this setting.
+Regdomain codes (SKU's) are taken from /etc/regdomain.xml and can also
+be viewed with the ``list countries'' request.
+Note that not all devices support changing the regdomain from a default
+setting; typically stored in EEPROM.
+See also
+.Cm country ,
+.Cm indoor ,
+.Cm outdoor ,
+and
+.Cm anywhere .
+.It Cm roam:rate Ar rate
+Set the threshold for controlling roaming when operating in a BSS.
+The
+.Ar rate
+parameter specifies the transmit rate in megabits
+at which roaming should be considered.
+If the current transmit rate drops below this setting and background scanning
+is enabled, then the system will check if a more desirable access point is
+available and switch over to it.
+The current scan cache contents are used if they are considered
+valid according to the
+.Cm scanvalid
+parameter; otherwise a background scan operation is triggered before
+any selection occurs.
+Each channel type has a separate rate threshold; the default values are:
+12 Mb/s (11a), 2 Mb/s (11b), 2 Mb/s (11g), MCS 1 (11na, 11ng).
+.It Cm roam:rssi Ar rssi
+Set the threshold for controlling roaming when operating in a BSS.
+The
+.Ar rssi
+parameter specifies the receive signal strength in dBm units
+at which roaming should be considered.
+If the current rssi drops below this setting and background scanning
+is enabled, then the system will check if a more desirable access point is
+available and switch over to it.
+The current scan cache contents are used if they are considered
+valid according to the
+.Cm scanvalid
+parameter; otherwise a background scan operation is triggered before
+any selection occurs.
+Rach channel type has a separate rssi threshold; the default values are
+all 7 dBm.
.It Cm roaming Ar mode
When operating as a station, control how the system will
behave when communication with the current access point
@@ -1013,78 +1441,6 @@ attempt to reestablish communication.
Manual mode is used by applications such as
.Xr wpa_supplicant 8
that want to control the selection of an access point.
-.It Cm roam:rssi11a Ar rssi
-Set the threshold for controlling roaming when operating in an
-802.11a BSS.
-The
-.Ar rssi
-parameter specifies the receive signal strength in dBm units
-at which roaming should be considered.
-If the current rssi drops below this setting and background scanning
-is enabled, then the system will check if a more desirable access point is
-available and switch over to it.
-The current scan cache contents are used if they are considered
-valid according to the
-.Cm scanvalid
-parameter; otherwise a background scan operation is triggered before
-any selection occurs.
-By default
-.Ar rssi
-is set to 7 dBm.
-.It Cm roam:rssi11b Ar rssi
-Set the threshold for controlling roaming when operating in an
-802.11b-only BSS.
-See
-.Cm roam:rssi11a
-for a description of this parameter.
-By default
-.Ar rssi
-is set to 7 dBm.
-.It Cm roam:rssi11g Ar rssi
-Set the threshold for controlling roaming when operating in a
-(mixed) 802.11g BSS.
-See
-.Cm roam:rssi11a
-for a description of this parameter.
-By default
-.Ar rssi
-is set to 7 dBm.
-.It Cm roam:rate11a Ar rate
-Set the threshold for controlling roaming when operating in an
-802.11a BSS.
-The
-.Ar rate
-parameter specifies the transmit rate in megabits
-at which roaming should be considered.
-If the current transmit rate drops below this setting and background scanning
-is enabled, then the system will check if a more desirable access point is
-available and switch over to it.
-The current scan cache contents are used if they are considered
-valid according to the
-.Cm scanvalid
-parameter; otherwise a background scan operation is triggered before
-any selection occurs.
-By default
-.Ar rate
-is set to 12 Mb/s.
-.It Cm roam:rate11b Ar rate
-Set the threshold for controlling roaming when operating in an
-802.11b-only BSS.
-See
-.Cm roam:rate11a
-for a description of this parameter.
-By default
-.Ar rate
-is set to 1 Mb/s.
-.It Cm roam:rate11g Ar rate
-Set the threshold for controlling roaming when operating in a
-(mixed) 802.11g BSS.
-See
-.Cm roam:rate11a
-for a description of this parameter.
-By default
-.Ar rate
-is set to 5 Mb/s.
.It Cm rtsthreshold Ar length
Set the threshold for which
transmitted frames are preceded by transmission of an
@@ -1102,93 +1458,20 @@ to
or
.Cm -
disables transmission of RTS frames.
-Not all adaptors support setting the RTS threshold.
-.It Cm ssid Ar ssid
-Set the desired Service Set Identifier (aka network name).
-The SSID is a string up to 32 characters
-in length and may be specified as either a normal string or in
-hexadecimal when preceded by
-.Ql 0x .
-Additionally, the SSID may be cleared by setting it to
-.Ql - .
+Not all adapters support setting the RTS threshold.
.It Cm scan
Initiate a scan of neighboring stations, wait for it to complete, and
display all stations found.
Only the super-user can initiate a scan.
-Depending on the capabilities of the APs, the following
-flags can be included in the output:
-.Bl -tag -width 3n
-.It Li A
-Channel Agility.
-Indicates that the station support channel hopping as described by the
-IEEE 802.11b specification.
-.It Li B
-Packet Binary Convolution Code (PBCC).
-A modulation alternative to the standard OFDM method.
-.It Dv C
-Pollreq
-.It Dv c
-Pollable
-.It Dv D
-Direct Sequence Spread Spectrum (DSSSOFDM).
-Indicates the the station supports DSSS modulation.
-.It Li E
-Extended Service Set (ESS).
-Indicates that the station is part of an infrastructure network
-(in contrast to an IBSS/ad-hoc network).
-.It Li I
-IBSS/ad-hoc network.
-Indicates that the station is part of an ad-hoc network
-(in contrast to an ESS network).
-.It Li P
-Privacy.
-Data confidentiality is required for all data frames
-exchanged within the BSS.
-This means that this BSS requires the station to
-use cryptographic means such as WEP, TKIP or AES-CCMP to
-encrypt/decrypt data frames being exchanged with others.
-.It Dv R
-Robust Security Network (RSN).
-Indicates that the station supports the IEEE 802.11i authentication
-and key management protocol.
-.It Li S
-Short Preamble.
-Indicates that the network is using short preambles (defined
-in 802.11b High Rate/DSSS PHY, short preamble utilizes a
-56 bit sync field in contrast to a 128 bit field used in long
-preamble mode).
-.It Li s
-Short slot time.
-Indicates that the network is using a short slot time.
-.El
-.Pp
-Interesting information elements captured from the neighboring
-stations are displayed at the end of each row.
-Possible elements are:
-.Cm WME
-(station supports WME),
-.Cm WPA
-(station supports WPA),
-.Cm RSN
-(station supports 802.11i/RSN),
-.Cm HT
-(station supports 802.11n/HT communication),
-.Cm ATH
-(station supoprts Atheros protocol extensions),
-.Cm VEN
-(station supports unknown vendor-specific extensions).
-If the
-.Fl v
-flag is used the information element contents will be shown.
-.Pp
+See
+.Cm list scan
+for information on the display.
+By default a background scan is done; otherwise a foreground
+scan is done and the station may roam to a different access point.
The
.Cm list scan
request can be used to show recent scan results without
initiating a new scan.
-.Pp
-The
-.Fl v
-flag may be used to prevent the shortening of long SSIDs.
.It Cm scanvalid Ar threshold
Set the maximum time the scan cache contents are considered valid;
i.e. will be used without first triggering a scan operation to
@@ -1202,13 +1485,25 @@ is 10 seconds.
One should take care setting this threshold; if it is set too low
then attempts to roam to another access point may trigger unnecessary
background scan operations.
-.It Cm stationname Ar name
-Set the name of this station.
-The station name is not part of the IEEE 802.11
-protocol though some interfaces support it.
-As such it only
-seems to be meaningful to identical or virtually identical equipment.
-Setting the station name is identical in syntax to setting the SSID.
+.It Cm shortgi
+Enable use of Short Guard Interval when operating in 802.11n
+on an HT channel.
+NB: this currently enables Short GI on both HT40 and HT20 channels.
+To disable Short GI use
+.Fl shortgi .
+.It Cm ssid Ar ssid
+Set the desired Service Set Identifier (aka network name).
+The SSID is a string up to 32 characters
+in length and may be specified as either a normal string or in
+hexadecimal when preceded by
+.Ql 0x .
+Additionally, the SSID may be cleared by setting it to
+.Ql - .
+.It Cm tsn
+When operating as an access point with WPA/802.11i allow legacy
+stations to associate using static key WEP and open authentication.
+To disallow legacy station use of WEP, use
+.Fl tsn .
.It Cm txpower Ar power
Set the power used to transmit frames.
The
@@ -1217,10 +1512,16 @@ argument is specified in .5 dBm units.
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.
+Not all adapters support changing the transmit power.
+.It Cm ucastrate Ar rate
+Set a fixed rate for transmitting unicast frames.
+Rates are specified as megabits/second in decimal; e.g.\& 5.5 for 5.5 Mb/s.
+This rate should be valid for the current operating conditions;
+if an invalid rate is specified drivers are free to chose an
+appropriate rate.
.It Cm wepmode Ar mode
Set the desired WEP mode.
-Not all adaptors support all modes.
+Not all adapters support all modes.
The set of valid modes is
.Cm off , on ,
and
@@ -1229,10 +1530,10 @@ The
.Cm mixed
mode explicitly tells the adaptor to allow association with access
points which allow both encrypted and unencrypted traffic.
-On these adaptors,
+On these adapters,
.Cm on
means that the access point must only allow encrypted connections.
-On other adaptors,
+On other adapters,
.Cm on
is generally another name for
.Cm mixed .
@@ -1261,7 +1562,7 @@ drivers do this mapping differently to
A key may be cleared by setting it to
.Ql - .
If WEP is supported then there are at least four keys.
-Some adaptors support more than four keys.
+Some adapters support more than four keys.
If that is the case, then the first four keys
(1-4) will be the standard temporary keys and any others will be adaptor
specific keys such as permanent keys stored in NVRAM.
@@ -1276,6 +1577,8 @@ WME is a subset of the IEEE 802.11e standard to support the
efficient communication of realtime and multimedia data.
To disable WME support, use
.Fl wme .
+Another name for this parameter is
+.Cm wmm .
.Pp
The following parameters are meaningful only when WME support is in use.
Parameters are specified per-AC (Access Category) and
@@ -1362,10 +1665,15 @@ This parameter is meaningful only when operating in ap mode.
Set the TxOpLimit channel access parameter to send to stations in a BSS.
This parameter is meaningful only when operating in ap mode.
.El
+.It Cm wps
+Enable Wireless Privacy Subscriber support.
+Note that WPS support requires a WPS-capable supplicant.
+To disable this function use
+.Fl wps .
.El
.Pp
The following parameters support an optional access control list
-feature available with some adaptors when operating in ap mode; see
+feature available with some adapters when operating in ap mode; see
.Xr wlan_acl 4 .
This facility allows an access point to accept/deny association
requests based on the MAC address of the station.
@@ -1392,6 +1700,14 @@ address database.
Set the ACL policy to allow all stations to associate.
.It Cm mac:flush
Delete all entries in the database.
+.It Cm mac:radius
+Set the ACL policy to permit association only by
+stations approved by a RADIUS server.
+Note that this feature requires the
+.Xr hostapd 8
+program be configured to do the right thing
+as it handles the RADIUS processing
+(and marks stations as authorized).
.El
.Pp
The following parameters are for compatibility with other systems:
@@ -1403,11 +1719,16 @@ parameter.
Included for
.Nx
compatibility.
-.It Cm station Ar name
-Another name for the
-.Cm stationname
-parameter.
-Included for
+.It Cm stationname Ar name
+Set the name of this station.
+The station name is not part of the IEEE 802.11
+protocol though some interfaces support it.
+As such it only
+seems to be meaningful to identical or virtually identical equipment.
+Setting the station name is identical in syntax to setting the SSID.
+One can also use
+.Cm station
+for
.Bsx
compatibility.
.It Cm wep
diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c
index 97eed75..e81b6a3 100644
--- a/sbin/ifconfig/ifieee80211.c
+++ b/sbin/ifconfig/ifieee80211.c
@@ -77,8 +77,6 @@
#include <net/if_media.h>
#include <net/route.h>
-#include <net80211/ieee80211.h>
-#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_ioctl.h>
#include <ctype.h>
@@ -94,6 +92,27 @@
#include <stddef.h> /* NB: for offsetof */
#include "ifconfig.h"
+#include "regdomain.h"
+
+#ifndef IEEE80211_FIXED_RATE_NONE
+#define IEEE80211_FIXED_RATE_NONE 0xff
+#endif
+
+#define REQ_ECM 0x01000000 /* enable if ECM set */
+#define REQ_OUTDOOR 0x02000000 /* enable for outdoor operation */
+#define REQ_FLAGS 0xff000000 /* private flags, don't pass to os */
+
+/* XXX need these publicly defined or similar */
+#ifndef IEEE80211_NODE_AUTH
+#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
+#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
+#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
+#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
+#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
+#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
+#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
+#endif
#define MAXCOL 78
static int col;
@@ -103,38 +122,34 @@ static void LINE_INIT(char c);
static void LINE_BREAK(void);
static void LINE_CHECK(const char *fmt, ...);
-/* XXX need max array size */
-static const int htrates[16] = {
- 13, /* IFM_IEEE80211_MCS0 */
- 26, /* IFM_IEEE80211_MCS1 */
- 39, /* IFM_IEEE80211_MCS2 */
- 52, /* IFM_IEEE80211_MCS3 */
- 78, /* IFM_IEEE80211_MCS4 */
- 104, /* IFM_IEEE80211_MCS5 */
- 117, /* IFM_IEEE80211_MCS6 */
- 130, /* IFM_IEEE80211_MCS7 */
- 26, /* IFM_IEEE80211_MCS8 */
- 52, /* IFM_IEEE80211_MCS9 */
- 78, /* IFM_IEEE80211_MCS10 */
- 104, /* IFM_IEEE80211_MCS11 */
- 156, /* IFM_IEEE80211_MCS12 */
- 208, /* IFM_IEEE80211_MCS13 */
- 234, /* IFM_IEEE80211_MCS14 */
- 260, /* IFM_IEEE80211_MCS15 */
+static const char *modename[] = {
+ "auto", "11a", "11b", "11g", "fh", "turboA", "turboG",
+ "sturbo", "11na", "11ng"
};
+static void set80211(int s, int type, int val, int len, void *data);
static int get80211(int s, int type, void *data, int len);
static int get80211len(int s, int type, void *data, int len, int *plen);
static int get80211val(int s, int type, int *val);
-static void set80211(int s, int type, int val, int len, void *data);
static const char *get_string(const char *val, const char *sep,
u_int8_t *buf, int *lenp);
static void print_string(const u_int8_t *buf, int len);
+static void print_regdomain(const struct ieee80211_regdomain *, int);
+static void print_channels(int, const struct ieee80211req_chaninfo *,
+ int allchans, int verbose);
+static void regdomain_makechannels(struct ieee80211_regdomain_req *,
+ const struct ieee80211_devcaps_req *);
static struct ieee80211req_chaninfo chaninfo;
-static struct ifmediareq *ifmr;
+static struct ieee80211_regdomain regdomain;
+static int gotregdomain = 0;
+static struct ieee80211_roamparams_req roamparams;
+static int gotroam = 0;
+static struct ieee80211_txparams_req txparams;
+static int gottxparams = 0;
static struct ieee80211_channel curchan;
static int gotcurchan = 0;
+static struct ifmediareq *ifmr;
static int htconf = 0;
static int gothtconf = 0;
@@ -159,11 +174,22 @@ getchaninfo(int s)
return;
if (get80211(s, IEEE80211_IOC_CHANINFO, &chaninfo, sizeof(chaninfo)) < 0)
errx(1, "unable to get channel information");
-
ifmr = ifmedia_getstate(s);
gethtconf(s);
}
+static struct regdata *
+getregdata(void)
+{
+ static struct regdata *rdp = NULL;
+ if (rdp == NULL) {
+ rdp = lib80211_alloc_regdata();
+ if (rdp == NULL)
+ exit(-1);
+ }
+ return rdp;
+}
+
/*
* Given the channel at index i with attributes from,
* check if there is a channel with attributes to in
@@ -308,6 +334,159 @@ getcurchan(int s)
return &curchan;
}
+static enum ieee80211_phymode
+chan2mode(const struct ieee80211_channel *c)
+{
+ if (IEEE80211_IS_CHAN_HTA(c))
+ return IEEE80211_MODE_11NA;
+ if (IEEE80211_IS_CHAN_HTG(c))
+ return IEEE80211_MODE_11NG;
+ if (IEEE80211_IS_CHAN_108A(c))
+ return IEEE80211_MODE_TURBO_A;
+ if (IEEE80211_IS_CHAN_108G(c))
+ return IEEE80211_MODE_TURBO_G;
+ if (IEEE80211_IS_CHAN_ST(c))
+ return IEEE80211_MODE_STURBO_A;
+ if (IEEE80211_IS_CHAN_FHSS(c))
+ return IEEE80211_MODE_FH;
+ if (IEEE80211_IS_CHAN_A(c))
+ return IEEE80211_MODE_11A;
+ if (IEEE80211_IS_CHAN_ANYG(c))
+ return IEEE80211_MODE_11G;
+ if (IEEE80211_IS_CHAN_B(c))
+ return IEEE80211_MODE_11B;
+ return IEEE80211_MODE_AUTO;
+}
+
+static void
+getroam(int s)
+{
+ if (gotroam)
+ return;
+ if (get80211(s, IEEE80211_IOC_ROAM,
+ &roamparams, sizeof(roamparams)) < 0)
+ errx(1, "unable to get roaming parameters");
+ gotroam = 1;
+}
+
+static void
+setroam_cb(int s, void *arg)
+{
+ struct ieee80211_roamparams_req *roam = arg;
+ set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
+}
+
+static void
+gettxparams(int s)
+{
+ if (gottxparams)
+ return;
+ if (get80211(s, IEEE80211_IOC_TXPARAMS,
+ &txparams, sizeof(txparams)) < 0)
+ errx(1, "unable to get transmit parameters");
+ gottxparams = 1;
+}
+
+static void
+settxparams_cb(int s, void *arg)
+{
+ struct ieee80211_txparams_req *txp = arg;
+ set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
+}
+
+static void
+getregdomain(int s)
+{
+ if (gotregdomain)
+ return;
+ if (get80211(s, IEEE80211_IOC_REGDOMAIN,
+ &regdomain, sizeof(regdomain)) < 0)
+ errx(1, "unable to get regulatory domain info");
+ gotregdomain = 1;
+}
+
+static void
+getdevcaps(int s, struct ieee80211_devcaps_req *dc)
+{
+ if (get80211(s, IEEE80211_IOC_DEVCAPS, dc, sizeof(*dc)) < 0)
+ errx(1, "unable to get device capabilities");
+}
+
+static void
+setregdomain_cb(int s, void *arg)
+{
+ struct ieee80211_regdomain_req req;
+ struct ieee80211_regdomain *rd = arg;
+ struct ieee80211_devcaps_req dc;
+ struct regdata *rdp = getregdata();
+
+ if (rd->country != 0) {
+ const struct country *cc;
+ /*
+ * Check current country seting to make sure it's
+ * compatible with the new regdomain. If not, then
+ * override it with any default country for this
+ * SKU. If we cannot arrange a match, then abort.
+ */
+ cc = lib80211_country_findbycc(rdp, rd->country);
+ if (cc == NULL)
+ errx(1, "unknown ISO country code %d", rd->country);
+ if (cc->rd->sku != rd->regdomain) {
+ const struct regdomain *rp;
+ /*
+ * Check if country is incompatible with regdomain.
+ * To enable multiple regdomains for a country code
+ * we permit a mismatch between the regdomain and
+ * the country's associated regdomain when the
+ * regdomain is setup w/o a default country. For
+ * example, US is bound to the FCC regdomain but
+ * we allow US to be combined with FCC3 because FCC3
+ * has not default country. This allows bogus
+ * combinations like FCC3+DK which are resolved when
+ * constructing the channel list by deferring to the
+ * regdomain to construct the channel list.
+ */
+ rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
+ if (rp == NULL)
+ errx(1, "country %s (%s) is not usable with "
+ "regdomain %d", cc->isoname, cc->name,
+ rd->regdomain);
+ else if (rp->cc != 0 && rp->cc != cc)
+ errx(1, "country %s (%s) is not usable with "
+ "regdomain %s", cc->isoname, cc->name,
+ rp->name);
+ }
+ }
+ req.rd = *rd;
+ /*
+ * Fetch the device capabilities and calculate the
+ * full set of netbands for which we request a new
+ * channel list be constructed. Once that's done we
+ * push the regdomain info + channel list to the kernel.
+ */
+ getdevcaps(s, &dc);
+#if 0
+ if (verbose) {
+ printf("drivercaps: 0x%x\n", dc.dc_drivercaps);
+ printf("cryptocaps: 0x%x\n", dc.dc_cryptocaps);
+ printf("htcaps : 0x%x\n", dc.dc_htcaps);
+ memcpy(&chaninfo, &dc.dc_chaninfo, sizeof(chaninfo));
+ print_channels(s, &dc.dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
+ }
+#endif
+ regdomain_makechannels(&req, &dc);
+ if (verbose) {
+ LINE_INIT(':');
+ print_regdomain(rd, 1/*verbose*/);
+ LINE_BREAK();
+ memcpy(&chaninfo, &req.chaninfo, sizeof(chaninfo));
+ print_channels(s, &req.chaninfo, 1/*allchans*/, 1/*verbose*/);
+ }
+ if (req.chaninfo.ic_nchans == 0)
+ errx(1, "no channels calculated");
+ set80211(s, IEEE80211_IOC_REGDOMAIN, 0, sizeof(req), &req);
+}
+
static int
ieee80211_mhz2ieee(int freq, int flags)
{
@@ -332,7 +511,7 @@ set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
ssid = 0;
len = strlen(val);
- if (len > 2 && isdigit(val[0]) && val[1] == ':') {
+ if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
ssid = atoi(val)-1;
val += 2;
}
@@ -503,6 +682,26 @@ set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
}
static void
+set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ struct ieee80211_chanswitch_req csr;
+ int v, flags;
+
+ memset(&csr, 0, sizeof(csr));
+ getchaninfo(s);
+ v = atoi(val);
+ flags = getchannelflags(val, v);
+ if (v > 255) { /* treat as frequency */
+ mapfreq(&csr.csa_chan, v, flags);
+ } else {
+ mapchan(&csr.csa_chan, v, flags);
+ }
+ csr.csa_mode = 1;
+ csr.csa_count = 5;
+ set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
+}
+
+static void
set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
{
int mode;
@@ -609,7 +808,7 @@ set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
int len;
u_int8_t data[IEEE80211_KEYBUF_SIZE];
- if (isdigit(val[0]) && val[1] == ':') {
+ if (isdigit((int)val[0]) && val[1] == ':') {
key = atoi(val)-1;
val += 2;
}
@@ -635,7 +834,7 @@ set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
- if (isdigit(val[0]) && val[1] == ':') {
+ if (isdigit((int)val[0]) && val[1] == ':') {
txkey = val[0]-'0'-1;
val += 2;
@@ -1040,47 +1239,271 @@ DECL_CMD_FUNC(set80211scanvalid, val, d)
set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
}
-static
-DECL_CMD_FUNC(set80211roamrssi11a, val, d)
+/*
+ * Parse an optional trailing specification of which netbands
+ * to apply a parameter to. This is basically the same syntax
+ * as used for channels but you can concatenate to specify
+ * multiple. For example:
+ * 14:abg apply to 11a, 11b, and 11g
+ * 6:ht apply to 11na and 11ng
+ * We don't make a big effort to catch silly things; this is
+ * really a convenience mechanism.
+ */
+static int
+getmodeflags(const char *val)
{
- set80211(s, IEEE80211_IOC_ROAM_RSSI_11A, atoi(val), 0, NULL);
+ const char *cp;
+ int flags;
+
+ flags = 0;
+
+ cp = strchr(val, ':');
+ if (cp != NULL) {
+ for (cp++; isalpha((int) *cp); cp++) {
+ /* accept mixed case */
+ int c = *cp;
+ if (isupper(c))
+ c = tolower(c);
+ switch (c) {
+ case 'a': /* 802.11a */
+ flags |= IEEE80211_CHAN_A;
+ break;
+ case 'b': /* 802.11b */
+ flags |= IEEE80211_CHAN_B;
+ break;
+ case 'g': /* 802.11g */
+ flags |= IEEE80211_CHAN_G;
+ break;
+ case 'h': /* ht = 802.11n */
+ case 'n': /* 802.11n */
+ flags |= IEEE80211_CHAN_HT;
+ break;
+ case 'd': /* dt = Atheros Dynamic Turbo */
+ flags |= IEEE80211_CHAN_TURBO;
+ break;
+ case 't': /* ht, dt, st, t */
+ /* dt and unadorned t specify Dynamic Turbo */
+ if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
+ flags |= IEEE80211_CHAN_TURBO;
+ break;
+ case 's': /* st = Atheros Static Turbo */
+ flags |= IEEE80211_CHAN_STURBO;
+ break;
+ default:
+ errx(-1, "%s: Invalid mode attribute %c\n",
+ val, *cp);
+ }
+ }
+ }
+ return flags;
}
+#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
+#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
+
+#define _APPLY(_flags, _base, _param, _v) do { \
+ if (_flags & IEEE80211_CHAN_HT) { \
+ if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
+ _base.params[IEEE80211_MODE_11NA]._param = _v; \
+ _base.params[IEEE80211_MODE_11NG]._param = _v; \
+ } else if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_11NA]._param = _v; \
+ else \
+ _base.params[IEEE80211_MODE_11NG]._param = _v; \
+ } \
+ if (_flags & IEEE80211_CHAN_TURBO) { \
+ if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
+ _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
+ } else if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
+ else \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
+ } \
+ if (_flags & IEEE80211_CHAN_STURBO) \
+ _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
+ if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
+ _base.params[IEEE80211_MODE_11A]._param = _v; \
+ if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
+ _base.params[IEEE80211_MODE_11G]._param = _v; \
+ if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
+ _base.params[IEEE80211_MODE_11B]._param = _v; \
+} while (0)
+#define _APPLY1(_flags, _base, _param, _v) do { \
+ if (_flags & IEEE80211_CHAN_HT) { \
+ if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_11NA]._param = _v; \
+ else \
+ _base.params[IEEE80211_MODE_11NG]._param = _v; \
+ } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
+ _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
+ else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
+ else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
+ _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
+ else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
+ _base.params[IEEE80211_MODE_11A]._param = _v; \
+ else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
+ _base.params[IEEE80211_MODE_11G]._param = _v; \
+ else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
+ _base.params[IEEE80211_MODE_11B]._param = _v; \
+} while (0)
+#define _APPLY_RATE(_flags, _base, _param, _v) do { \
+ if (_flags & IEEE80211_CHAN_HT) { \
+ if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
+ _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
+ _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
+ } else if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
+ else \
+ _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
+ } \
+ if (_flags & IEEE80211_CHAN_TURBO) { \
+ if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
+ _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
+ } else if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
+ else \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
+ } \
+ if (_flags & IEEE80211_CHAN_STURBO) \
+ _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
+ if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
+ _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
+ if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
+ _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
+ if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
+ _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
+} while (0)
+#define _APPLY_RATE1(_flags, _base, _param, _v) do { \
+ if (_flags & IEEE80211_CHAN_HT) { \
+ if (_flags & IEEE80211_CHAN_5GHZ) \
+ _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
+ else \
+ _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
+ } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
+ _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
+ else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
+ _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
+ else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
+ _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
+ else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
+ _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
+ else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
+ _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
+ else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
+ _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
+} while (0)
+
static
-DECL_CMD_FUNC(set80211roamrssi11b, val, d)
+DECL_CMD_FUNC(set80211roamrssi, val, d)
{
- set80211(s, IEEE80211_IOC_ROAM_RSSI_11B, atoi(val), 0, NULL);
+ double v = atof(val);
+ int rssi, flags;
+
+ rssi = (int) (2*v);
+ if (rssi != 2*v)
+ errx(-1, "invalid rssi (must be .5 dBm units)");
+ flags = getmodeflags(val);
+ getroam(s);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY1(flags, roamparams, rssi, rssi);
+ } else
+ _APPLY(flags, roamparams, rssi, rssi);
+ callback_register(setroam_cb, &roamparams);
}
static
-DECL_CMD_FUNC(set80211roamrssi11g, val, d)
+DECL_CMD_FUNC(set80211roamrate, val, d)
{
- set80211(s, IEEE80211_IOC_ROAM_RSSI_11G, atoi(val), 0, NULL);
+ int v = atoi(val), flags;
+
+ flags = getmodeflags(val);
+ getroam(s);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY_RATE1(flags, roamparams, rate, v);
+ } else
+ _APPLY_RATE(flags, roamparams, rate, v);
+ callback_register(setroam_cb, &roamparams);
}
static
-DECL_CMD_FUNC(set80211roamrate11a, val, d)
+DECL_CMD_FUNC(set80211mcastrate, val, d)
{
- set80211(s, IEEE80211_IOC_ROAM_RATE_11A, 2*atoi(val), 0, NULL);
+ int v = atoi(val), flags;
+
+ flags = getmodeflags(val);
+ gettxparams(s);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY_RATE1(flags, txparams, mcastrate, v);
+ } else
+ _APPLY_RATE(flags, txparams, mcastrate, v);
+ callback_register(settxparams_cb, &txparams);
}
static
-DECL_CMD_FUNC(set80211roamrate11b, val, d)
+DECL_CMD_FUNC(set80211mgtrate, val, d)
{
- set80211(s, IEEE80211_IOC_ROAM_RATE_11B, 2*atoi(val), 0, NULL);
+ int v = atoi(val), flags;
+
+ flags = getmodeflags(val);
+ gettxparams(s);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY_RATE1(flags, txparams, mgmtrate, v);
+ } else
+ _APPLY_RATE(flags, txparams, mgmtrate, v);
+ callback_register(settxparams_cb, &txparams);
}
static
-DECL_CMD_FUNC(set80211roamrate11g, val, d)
+DECL_CMD_FUNC(set80211ucastrate, val, d)
{
- set80211(s, IEEE80211_IOC_ROAM_RATE_11G, 2*atoi(val), 0, NULL);
+ int v, flags;
+
+ gettxparams(s);
+ flags = getmodeflags(val);
+ if (isanyarg(val)) {
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY1(flags, txparams, ucastrate,
+ IEEE80211_FIXED_RATE_NONE);
+ } else
+ _APPLY(flags, txparams, ucastrate,
+ IEEE80211_FIXED_RATE_NONE);
+ } else {
+ v = atoi(val);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY_RATE1(flags, txparams, ucastrate, v);
+ } else
+ _APPLY_RATE(flags, txparams, ucastrate, v);
+ }
+ callback_register(settxparams_cb, &txparams);
}
static
-DECL_CMD_FUNC(set80211mcastrate, val, d)
+DECL_CMD_FUNC(set80211maxretry, val, d)
{
- set80211(s, IEEE80211_IOC_MCAST_RATE, 2*atoi(val), 0, NULL);
+ int v = atoi(val), flags;
+
+ flags = getmodeflags(val);
+ gettxparams(s);
+ if (flags == 0) { /* NB: no flags => current channel */
+ flags = getcurchan(s)->ic_flags;
+ _APPLY1(flags, txparams, maxretry, v);
+ } else
+ _APPLY(flags, txparams, maxretry, v);
+ callback_register(settxparams_cb, &txparams);
}
+#undef _APPLY_RATE
+#undef _APPLY
+#undef IEEE80211_CHAN_HTA
+#undef IEEE80211_CHAN_HTG
static
DECL_CMD_FUNC(set80211fragthreshold, val, d)
@@ -1109,6 +1532,12 @@ set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
}
static void
+set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
+}
+
+static void
set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
{
set80211(s, IEEE80211_IOC_SHORTGI,
@@ -1238,12 +1667,321 @@ set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
}
static void
+set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
+}
+
+static void
set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
{
set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
}
static void
+set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
+}
+
+static void
+set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
+}
+
+static int
+regdomain_sort(const void *a, const void *b)
+{
+#define CHAN_ALL \
+ (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
+ const struct ieee80211_channel *ca = a;
+ const struct ieee80211_channel *cb = b;
+
+ return ca->ic_freq == cb->ic_freq ?
+ (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
+ ca->ic_freq - cb->ic_freq;
+#undef CHAN_ALL
+}
+
+static const struct ieee80211_channel *
+chanlookup(const struct ieee80211_channel chans[], int nchans,
+ int freq, int flags)
+{
+ int i;
+
+ flags &= IEEE80211_CHAN_ALLTURBO;
+ for (i = 0; i < nchans; i++) {
+ const struct ieee80211_channel *c = &chans[i];
+ if (c->ic_freq == freq &&
+ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return c;
+ }
+ return NULL;
+}
+
+static void
+regdomain_addchans(struct ieee80211req_chaninfo *ci,
+ const netband_head *bands,
+ const struct ieee80211_regdomain *reg,
+ uint32_t chanFlags,
+ const struct ieee80211req_chaninfo *avail)
+{
+ const struct netband *nb;
+ const struct freqband *b;
+ struct ieee80211_channel *c, *prev;
+ int freq, channelSep;
+
+ channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
+ LIST_FOREACH(nb, bands, next) {
+ b = nb->band;
+ if (verbose)
+ printf("%s: chanFlags 0x%x b %p\n",
+ __func__, chanFlags, b);
+ prev = NULL;
+ for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) {
+ uint32_t flags = nb->flags | b->flags;
+
+ /* check if device can operate on this frequency */
+ if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL) {
+ if (verbose)
+ printf("%u: skip, flags 0x%x not available\n", freq, chanFlags);
+ continue;
+ }
+ if ((flags & IEEE80211_CHAN_HALF) &&
+ (chanFlags & IEEE80211_CHAN_HALF) == 0) {
+ if (verbose)
+ printf("%u: skip, device does not support half-rate channels\n", freq);
+ continue;
+ }
+ if ((flags & IEEE80211_CHAN_QUARTER) &&
+ (chanFlags & IEEE80211_CHAN_QUARTER) == 0) {
+ if (verbose)
+ printf("%u: skip, device does not support quarter-rate channels\n", freq);
+ continue;
+ }
+ if ((flags & IEEE80211_CHAN_HT20) &&
+ (chanFlags & IEEE80211_CHAN_HT20) == 0) {
+ if (verbose)
+ printf("%u: skip, device does not support HT20 operation\n", freq);
+ continue;
+ }
+ if ((flags & IEEE80211_CHAN_HT40) &&
+ (chanFlags & IEEE80211_CHAN_HT40) == 0) {
+ if (verbose)
+ printf("%u: skip, device does not support HT40 operation\n", freq);
+ continue;
+ }
+ if ((flags & REQ_ECM) && !reg->ecm) {
+ if (verbose)
+ printf("%u: skip, ECM channel\n", freq);
+ continue;
+ }
+ if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
+ if (verbose)
+ printf("%u: skip, outdoor channel\n", freq);
+ continue;
+ }
+ if ((flags & IEEE80211_CHAN_HT40) &&
+ prev != NULL && (freq - prev->ic_freq) < channelSep) {
+ if (verbose)
+ printf("%u: skip, only %u channel "
+ "separation, need %d\n", freq,
+ freq - prev->ic_freq, channelSep);
+ continue;
+ }
+ if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
+ if (verbose)
+ printf("%u: skip, channel table full\n", freq);
+ break;
+ }
+ c = &ci->ic_chans[ci->ic_nchans++];
+ c->ic_freq = freq;
+ c->ic_flags = chanFlags |
+ (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40));
+ if (c->ic_flags & IEEE80211_CHAN_DFS)
+ c->ic_maxregpower = nb->maxPowerDFS;
+ else
+ c->ic_maxregpower = nb->maxPower;
+ if (verbose)
+ printf("[%3d] add freq %u flags 0x%x power %u\n",
+ ci->ic_nchans-1, c->ic_freq, c->ic_flags,
+ c->ic_maxregpower);
+ /* NB: kernel fills in other fields */
+ prev = c;
+ }
+ }
+}
+
+static void
+regdomain_makechannels(
+ struct ieee80211_regdomain_req *req,
+ const struct ieee80211_devcaps_req *dc)
+{
+ struct regdata *rdp = getregdata();
+ const struct country *cc;
+ const struct ieee80211_regdomain *reg = &req->rd;
+ struct ieee80211req_chaninfo *ci = &req->chaninfo;
+ const struct regdomain *rd;
+
+ /*
+ * Locate construction table for new channel list. We treat
+ * the regdomain/SKU as definitive so a country can be in
+ * multiple with different properties (e.g. US in FCC+FCC3).
+ * If no regdomain is specified then we fallback on the country
+ * code to find the associated regdomain since countries always
+ * belong to at least one regdomain.
+ */
+ if (reg->regdomain == 0) {
+ cc = lib80211_country_findbycc(rdp, reg->country);
+ if (cc == NULL)
+ errx(1, "internal error, country %d not found",
+ reg->country);
+ rd = cc->rd;
+ } else
+ rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
+ if (rd == NULL)
+ errx(1, "internal error, regdomain %d not found",
+ reg->regdomain);
+ if (rd->sku != SKU_DEBUG) {
+ memset(ci, 0, sizeof(*ci));
+ if (!LIST_EMPTY(&rd->bands_11b))
+ regdomain_addchans(ci, &rd->bands_11b, reg,
+ IEEE80211_CHAN_B, &dc->dc_chaninfo);
+ if (!LIST_EMPTY(&rd->bands_11g))
+ regdomain_addchans(ci, &rd->bands_11g, reg,
+ IEEE80211_CHAN_G, &dc->dc_chaninfo);
+ if (!LIST_EMPTY(&rd->bands_11a))
+ regdomain_addchans(ci, &rd->bands_11a, reg,
+ IEEE80211_CHAN_A, &dc->dc_chaninfo);
+ if (!LIST_EMPTY(&rd->bands_11na)) {
+ regdomain_addchans(ci, &rd->bands_11na, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11na, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11na, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
+ &dc->dc_chaninfo);
+ }
+ if (!LIST_EMPTY(&rd->bands_11ng)) {
+ regdomain_addchans(ci, &rd->bands_11ng, reg,
+ IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11ng, reg,
+ IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11ng, reg,
+ IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
+ &dc->dc_chaninfo);
+ }
+ qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
+ regdomain_sort);
+ } else
+ *ci = dc->dc_chaninfo;
+}
+
+static void
+list_countries(void)
+{
+ struct regdata *rdp = getregdata();
+ const struct country *cp;
+ const struct regdomain *dp;
+ int i;
+
+ i = 0;
+ printf("\nCountry codes:\n");
+ LIST_FOREACH(cp, &rdp->countries, next) {
+ printf("%2s %-15.15s%s", cp->isoname,
+ cp->name, ((i+1)%4) == 0 ? "\n" : " ");
+ i++;
+ }
+ i = 0;
+ printf("\nRegulatory domains:\n");
+ LIST_FOREACH(dp, &rdp->domains, next) {
+ printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
+ i++;
+ }
+ printf("\n");
+}
+
+static void
+defaultcountry(const struct regdomain *rd)
+{
+ struct regdata *rdp = getregdata();
+ const struct country *cc;
+
+ cc = lib80211_country_findbycc(rdp, rd->cc->code);
+ if (cc == NULL)
+ errx(1, "internal error, ISO country code %d not "
+ "defined for regdomain %s", rd->cc->code, rd->name);
+ regdomain.country = cc->code;
+ regdomain.isocc[0] = cc->isoname[0];
+ regdomain.isocc[1] = cc->isoname[1];
+}
+
+static
+DECL_CMD_FUNC(set80211regdomain, val, d)
+{
+ struct regdata *rdp = getregdata();
+ const struct regdomain *rd;
+
+ rd = lib80211_regdomain_findbyname(rdp, val);
+ if (rd == NULL) {
+ rd = lib80211_regdomain_findbysku(rdp, atoi(val));
+ if (rd == NULL)
+ errx(1, "unknown regdomain %s", val);
+ }
+ getregdomain(s);
+ regdomain.regdomain = rd->sku;
+ if (regdomain.country == 0 && rd->cc != NULL) {
+ /*
+ * No country code setup and there's a default
+ * one for this regdomain fill it in.
+ */
+ defaultcountry(rd);
+ }
+ callback_register(setregdomain_cb, &regdomain);
+}
+
+static
+DECL_CMD_FUNC(set80211country, val, d)
+{
+ struct regdata *rdp = getregdata();
+ const struct country *cc;
+
+ cc = lib80211_country_findbyname(rdp, val);
+ if (cc == NULL) {
+ cc = lib80211_country_findbycc(rdp, atoi(val));
+ if (cc == NULL)
+ errx(1, "unknown ISO country code %s", val);
+ }
+ getregdomain(s);
+ regdomain.regdomain = cc->rd->sku;
+ regdomain.country = cc->code;
+ regdomain.isocc[0] = cc->isoname[0];
+ regdomain.isocc[1] = cc->isoname[1];
+ callback_register(setregdomain_cb, &regdomain);
+}
+
+static void
+set80211location(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ getregdomain(s);
+ regdomain.location = d;
+ callback_register(setregdomain_cb, &regdomain);
+}
+
+static void
+set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ getregdomain(s);
+ regdomain.ecm = d;
+ callback_register(setregdomain_cb, &regdomain);
+}
+
+static void
LINE_INIT(char c)
{
spacer = c;
@@ -1331,16 +2069,6 @@ getcaps(int capinfo)
static const char *
getflags(int flags)
{
-/* XXX need these publicly defined or similar */
-#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
-#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
-#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
-#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
-#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
-#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
-#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
-#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
-
static char flagstring[32];
char *cp = flagstring;
@@ -1363,14 +2091,6 @@ getflags(int flags)
*cp++ = 'T';
*cp = '\0';
return flagstring;
-#undef IEEE80211_NODE_TSN
-#undef IEEE80211_NODE_WPS
-#undef IEEE80211_NODE_HTCOMPAT
-#undef IEEE80211_NODE_HT
-#undef IEEE80211_NODE_AUTH
-#undef IEEE80211_NODE_QOS
-#undef IEEE80211_NODE_ERP
-#undef IEEE80211_NODE_PWR_MGT
}
static void
@@ -1814,27 +2534,27 @@ printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
(((const u_int8_t *)(p))[2] << 16) | \
(((const u_int8_t *)(p))[3] << 24)))
-static int __inline
+static __inline int
iswpaoui(const u_int8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
}
-static int __inline
+static __inline int
iswmeinfo(const u_int8_t *frm)
{
return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
frm[6] == WME_INFO_OUI_SUBTYPE;
}
-static int __inline
+static __inline int
iswmeparam(const u_int8_t *frm)
{
return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
frm[6] == WME_PARAM_OUI_SUBTYPE;
}
-static int __inline
+static __inline int
isatherosoui(const u_int8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
@@ -1925,6 +2645,18 @@ printies(const u_int8_t *vp, int ielen, int maxcols)
}
static void
+printmimo(const struct ieee80211_mimo_info *mi)
+{
+ /* NB: don't muddy display unless there's something to show */
+ if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
+ /* XXX ignore EVM for now */
+ printf(" (rssi %d:%d:%d nf %d:%d:%d)",
+ mi->rssi[0], mi->rssi[1], mi->rssi[2],
+ mi->noise[0], mi->noise[1], mi->noise[2]);
+ }
+}
+
+static void
list_scan(int s)
{
uint8_t buf[24*1024];
@@ -1973,11 +2705,17 @@ list_scan(int s)
} while (len >= sizeof(struct ieee80211req_scan_result));
}
+#ifdef __FreeBSD__
#include <net80211/ieee80211_freebsd.h>
+#endif
+#ifdef __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
static void
scan_and_wait(int s)
{
+ struct ieee80211_scan_req sr;
struct ieee80211req ireq;
int sroute;
@@ -1989,6 +2727,16 @@ scan_and_wait(int s)
(void) memset(&ireq, 0, sizeof(ireq));
(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
ireq.i_type = IEEE80211_IOC_SCAN_REQ;
+
+ memset(&sr, 0, sizeof(sr));
+ sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
+ | IEEE80211_IOC_SCAN_NOPICK
+ | IEEE80211_IOC_SCAN_ONCE;
+ sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+ sr.sr_nssid = 0;
+
+ ireq.i_data = &sr;
+ ireq.i_len = sizeof(sr);
/* NB: only root can trigger a scan so ignore errors */
if (ioctl(s, SIOCS80211, &ireq) >= 0) {
char buf[2048];
@@ -2055,20 +2803,6 @@ getrxseq(const struct ieee80211req_sta_info *si)
#undef IEEE80211_NODE_QOS
}
-static int
-gettxrate(const struct ieee80211req_sta_info *si)
-{
- int txrate = si->isi_txrate;
-
- if (txrate & 0x80) {
- txrate = htrates[txrate & 0xf];
- /* NB: could bump this more based on short gi */
- return si->isi_flags & IEEE80211_CHAN_HT40 ?
- txrate : txrate / 2;
- } else
- return (si->isi_rates[txrate] & IEEE80211_RATE_VAL) / 2;
-}
-
static void
list_stations(int s)
{
@@ -2119,7 +2853,7 @@ list_stations(int s)
, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
, IEEE80211_AID(si->isi_associd)
, ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
- , gettxrate(si)
+ , si->isi_txmbps/2
, si->isi_rssi/2.
, si->isi_inact
, gettxseq(si)
@@ -2128,6 +2862,7 @@ list_stations(int s)
, getflags(si->isi_state)
);
printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
+ printmimo(&si->isi_mimo);
printf("\n");
cp += si->isi_len, len -= si->isi_len;
} while (len >= sizeof(struct ieee80211req_sta_info));
@@ -2326,23 +3061,33 @@ list_keys(int s)
}
#define IEEE80211_C_BITS \
-"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
-"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
-"\31WPA2\32BURST\33WME\34WDS\36BGSCAN\37TXFRAG"
+ "\20\7FF\10TURBOP\11IBSS\12PMGT" \
+ "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
+ "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
+ "\37TXFRAG"
+
+#define IEEE80211_CRYPTO_BITS \
+ "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT"
+
+#define IEEE80211_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\21AMPDU\22AMSDU\23HT"
static void
list_capabilities(int s)
{
- struct ieee80211req ireq;
- u_int32_t caps;
+ struct ieee80211_devcaps_req dc;
- (void) memset(&ireq, 0, sizeof(ireq));
- (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
- ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
- if (ioctl(s, SIOCG80211, &ireq) < 0)
- errx(1, "unable to get driver capabilities");
- caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
- printb(name, caps, IEEE80211_C_BITS);
+ getdevcaps(s, &dc);
+ printb("drivercaps", dc.dc_drivercaps, IEEE80211_C_BITS);
+ if (dc.dc_cryptocaps != 0 || verbose) {
+ putchar('\n');
+ printb("cryptocaps", dc.dc_cryptocaps, IEEE80211_CRYPTO_BITS);
+ }
+ if (dc.dc_htcaps != 0 || verbose) {
+ putchar('\n');
+ printb("htcaps", dc.dc_htcaps, IEEE80211_HTCAP_BITS);
+ }
putchar('\n');
}
@@ -2412,6 +3157,77 @@ again:
}
static void
+list_roam(int s)
+{
+ const struct ieee80211_roamparam *rp;
+ int mode;
+
+ getroam(s);
+ for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
+ rp = &roamparams.params[mode];
+ if (rp->rssi == 0 && rp->rate == 0)
+ continue;
+ if (rp->rssi & 1)
+ LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s",
+ modename[mode], rp->rssi/2, rp->rate/2);
+ else
+ LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s",
+ modename[mode], rp->rssi/2, rp->rate/2);
+ }
+ for (; mode < IEEE80211_MODE_MAX; mode++) {
+ rp = &roamparams.params[mode];
+ if (rp->rssi == 0 && rp->rate == 0)
+ continue;
+ if (rp->rssi & 1)
+ LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ",
+ modename[mode], rp->rssi/2, rp->rate &~ 0x80);
+ else
+ LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ",
+ modename[mode], rp->rssi/2, rp->rate &~ 0x80);
+ }
+}
+
+static void
+list_txparams(int s)
+{
+ const struct ieee80211_txparam *tp;
+ int mode;
+
+ gettxparams(s);
+ for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
+ tp = &txparams.params[mode];
+ if (tp->mgmtrate == 0 && tp->mcastrate == 0)
+ continue;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s "
+ "mcast %2u Mb/s maxretry %u",
+ modename[mode], tp->mgmtrate/2,
+ tp->mcastrate/2, tp->maxretry);
+ else
+ LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s "
+ "mcast %2u Mb/s maxretry %u",
+ modename[mode], tp->ucastrate/2, tp->mgmtrate/2,
+ tp->mcastrate/2, tp->maxretry);
+ }
+ for (; mode < IEEE80211_MODE_MAX; mode++) {
+ tp = &txparams.params[mode];
+ if (tp->mgmtrate == 0 && tp->mcastrate == 0)
+ continue;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS "
+ "mcast %2u MCS maxretry %u",
+ modename[mode], tp->mgmtrate &~ 0x80,
+ tp->mcastrate &~ 0x80, tp->maxretry);
+ else
+ LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS "
+ "mcast %2u MCS maxretry %u",
+ modename[mode], tp->ucastrate &~ 0x80,
+ tp->mgmtrate &~ 0x80,
+ tp->mcastrate &~ 0x80, tp->maxretry);
+ }
+}
+
+static void
printpolicy(int policy)
{
switch (policy) {
@@ -2424,6 +3240,9 @@ printpolicy(int policy)
case IEEE80211_MACCMD_POLICY_DENY:
printf("policy: deny\n");
break;
+ case IEEE80211_MACCMD_POLICY_RADIUS:
+ printf("policy: radius\n");
+ break;
default:
printf("policy: unknown (%u)\n", policy);
break;
@@ -2457,6 +3276,8 @@ list_mac(int s)
c = '+';
} else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
c = '-';
+ } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
+ c = 'r'; /* NB: should never have entries */
} else {
printf("policy: unknown (%u)\n", policy);
c = '?';
@@ -2490,6 +3311,52 @@ list_mac(int s)
free(data);
}
+static void
+print_regdomain(const struct ieee80211_regdomain *reg, int verb)
+{
+ if ((reg->regdomain != 0 &&
+ reg->regdomain != reg->country) || verb) {
+ const struct regdomain *rd =
+ lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
+ if (rd == NULL)
+ LINE_CHECK("regdomain %d", reg->regdomain);
+ else
+ LINE_CHECK("regdomain %s", rd->name);
+ }
+ if (reg->country != 0 || verb) {
+ const struct country *cc =
+ lib80211_country_findbycc(getregdata(), reg->country);
+ if (cc == NULL)
+ LINE_CHECK("country %d", reg->country);
+ else
+ LINE_CHECK("country %s", cc->isoname);
+ }
+ if (reg->location == 'I')
+ LINE_CHECK("indoor");
+ else if (reg->location == 'O')
+ LINE_CHECK("outdoor");
+ else if (verb)
+ LINE_CHECK("anywhere");
+ if (reg->ecm)
+ LINE_CHECK("ecm");
+ else if (verb)
+ LINE_CHECK("-ecm");
+}
+
+static void
+list_regdomain(int s, int channelsalso)
+{
+ getregdomain(s);
+ if (channelsalso) {
+ getchaninfo(s);
+ spacer = ':';
+ print_regdomain(&regdomain, 1);
+ LINE_BREAK();
+ print_channels(s, &chaninfo, 1/*allchans*/, 1/*verbose*/);
+ } else
+ print_regdomain(&regdomain, verbose);
+}
+
static
DECL_CMD_FUNC(set80211list, arg, d)
{
@@ -2509,14 +3376,23 @@ DECL_CMD_FUNC(set80211list, arg, d)
list_keys(s);
else if (iseq(arg, "caps"))
list_capabilities(s);
- else if (iseq(arg, "wme"))
+ else if (iseq(arg, "wme") || iseq(arg, "wmm"))
list_wme(s);
else if (iseq(arg, "mac"))
list_mac(s);
else if (iseq(arg, "txpow"))
list_txpow(s);
+ else if (iseq(arg, "roam"))
+ list_roam(s);
+ else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
+ list_txparams(s);
+ else if (iseq(arg, "regdomain"))
+ list_regdomain(s, 1);
+ else if (iseq(arg, "countries"))
+ list_countries();
else
errx(1, "Don't know how to list %s for %s", arg, name);
+ LINE_BREAK();
#undef iseq
}
@@ -2674,15 +3550,6 @@ getssid(int s, int ix, void *data, size_t len, int *plen)
}
static void
-printrssi(const char *tag, int rssi)
-{
- if (rssi & 1)
- LINE_CHECK("%s %u.5", tag, rssi/2);
- else
- LINE_CHECK("%s %u", tag, rssi/2);
-}
-
-static void
ieee80211_status(int s)
{
static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
@@ -2690,6 +3557,8 @@ ieee80211_status(int s)
int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
uint8_t data[32];
const struct ieee80211_channel *c;
+ const struct ieee80211_roamparam *rp;
+ const struct ieee80211_txparam *tp;
if (getssid(s, -1, data, sizeof(data), &len) < 0) {
/* If we can't get the SSID, this isn't an 802.11 device. */
@@ -2701,7 +3570,10 @@ ieee80211_status(int s)
* if's doesn't reuse the first interfaces' cached state.
*/
gotcurchan = 0;
+ gotroam = 0;
+ gottxparams = 0;
gothtconf = 0;
+ gotregdomain = 0;
if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
num = 0;
@@ -2736,6 +3608,8 @@ ieee80211_status(int s)
spacer = ' '; /* force first break */
LINE_BREAK();
+ list_regdomain(s, 0);
+
wpa = 0;
if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
switch (val) {
@@ -2776,12 +3650,55 @@ ieee80211_status(int s)
}
if (wpa || verbose) {
+ if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
+ if (val)
+ LINE_CHECK("wps");
+ else if (verbose)
+ LINE_CHECK("-wps");
+ }
+ if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
+ if (val)
+ LINE_CHECK("tsn");
+ else if (verbose)
+ LINE_CHECK("-tsn");
+ }
if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
if (val)
LINE_CHECK("countermeasures");
else if (verbose)
LINE_CHECK("-countermeasures");
}
+#if 0
+ /* XXX not interesting with WPA done in user space */
+ ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ }
+
+ ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ LINE_CHECK("mcastcipher ");
+ printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
+ spacer = ' ';
+ }
+
+ ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ LINE_CHECK("ucastcipher ");
+ printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
+ }
+
+ if (wpa & 2) {
+ ireq.i_type = IEEE80211_IOC_RSNCAPS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ LINE_CHECK("RSN caps 0x%x", ireq.i_val);
+ spacer = ' ';
+ }
+ }
+
+ ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
+ if (ioctl(s, SIOCG80211, &ireq) != -1) {
+ }
+#endif
}
if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
@@ -2876,6 +3793,13 @@ end:
LINE_CHECK("txpowmax %.1f", val/2.);
}
+ if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
+ if (val)
+ LINE_CHECK("dotd");
+ else if (verbose)
+ LINE_CHECK("-dotd");
+ }
+
if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
if (val != IEEE80211_RTS_MAX || verbose)
LINE_CHECK("rtsthreshold %d", val);
@@ -2892,8 +3816,19 @@ end:
}
}
- if (get80211val(s, IEEE80211_IOC_MCAST_RATE, &val) != -1)
- printrate("mcastrate", val, 2*1, 0/*XXX*/);
+ if (!verbose) {
+ gettxparams(s);
+ tp = &txparams.params[chan2mode(c)];
+ printrate("ucastrate", tp->ucastrate,
+ IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
+ printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0);
+ printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0);
+ if (tp->maxretry != 6) /* XXX */
+ LINE_CHECK("maxretry %d", tp->maxretry);
+ } else {
+ LINE_BREAK();
+ list_txparams(s);
+ }
bgscaninterval = -1;
(void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
@@ -2915,23 +3850,17 @@ end:
LINE_CHECK("bgscanintvl %u", bgscaninterval);
if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
LINE_CHECK("bgscanidle %u", val);
- if (IEEE80211_IS_CHAN_A(c) || verbose) {
- if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11A, &val) != -1)
- printrssi("roam:rssi11a", val);
- if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11A, &val) != -1)
- printrate("roam:rate11a", val, -1, -1);
- }
- if (IEEE80211_IS_CHAN_B(c) || verbose) {
- if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11B, &val) != -1)
- printrssi("roam:rssi11b", val);
- if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11B, &val) != -1)
- printrate("roam:rate11b", val, -1, -1);
- }
- if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
- if (get80211val(s, IEEE80211_IOC_ROAM_RSSI_11G, &val) != -1)
- printrssi("roam:rssi11g", val);
- if (get80211val(s, IEEE80211_IOC_ROAM_RATE_11G, &val) != -1)
- printrate("roam:rate11g", val, -1, -1);
+ if (!verbose) {
+ getroam(s);
+ rp = &roamparams.params[chan2mode(c)];
+ if (rp->rssi & 1)
+ LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
+ else
+ LINE_CHECK("roam:rssi %u", rp->rssi/2);
+ LINE_CHECK("roam:rate %u", rp->rate/2);
+ } else {
+ LINE_BREAK();
+ list_roam(s);
}
}
@@ -3061,7 +3990,6 @@ end:
}
}
/* XXX amsdu limit */
- /* XXX 20/40 */
if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
if (val)
LINE_CHECK("shortgi");
@@ -3111,6 +4039,12 @@ end:
else if (verbose)
LINE_CHECK("-dturbo");
}
+ if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
+ if (val)
+ LINE_CHECK("dwds");
+ else if (verbose)
+ LINE_CHECK("-dwds");
+ }
if (opmode == IEEE80211_M_HOSTAP) {
if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
@@ -3134,6 +4068,12 @@ end:
else if (verbose)
LINE_CHECK("doth");
}
+ if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
+ if (!val)
+ LINE_CHECK("-dfs");
+ else if (verbose)
+ LINE_CHECK("dfs");
+ }
if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
if (!val)
LINE_CHECK("-inact");
@@ -3314,6 +4254,113 @@ print_string(const u_int8_t *buf, int len)
}
}
+/*
+ * Virtual AP cloning support.
+ */
+static struct ieee80211_clone_params params = {
+ .icp_opmode = IEEE80211_M_STA, /* default to station mode */
+};
+
+static void
+wlan_create(int s, struct ifreq *ifr)
+{
+ static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
+
+ if (params.icp_parent[0] == '\0')
+ errx(1, "must specify a parent when creating a wlan device");
+ if (params.icp_opmode == IEEE80211_M_WDS &&
+ memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
+ errx(1, "no bssid specified for WDS (use wlanbssid)");
+ ifr->ifr_data = (caddr_t) &params;
+ if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
+ err(1, "SIOCIFCREATE2");
+}
+
+static
+DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
+{
+ strlcpy(params.icp_parent, arg, IFNAMSIZ);
+ clone_setcallback(wlan_create);
+}
+
+static
+DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
+{
+ const struct ether_addr *ea;
+
+ ea = ether_aton(arg);
+ if (ea == NULL)
+ errx(1, "%s: cannot parse bssid", arg);
+ memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
+ clone_setcallback(wlan_create);
+}
+
+static
+DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
+{
+ const struct ether_addr *ea;
+
+ ea = ether_aton(arg);
+ if (ea == NULL)
+ errx(1, "%s: cannot parse addres", arg);
+ memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
+ params.icp_flags |= IEEE80211_CLONE_MACADDR;
+ clone_setcallback(wlan_create);
+}
+
+static
+DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
+{
+#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
+ if (iseq(arg, "sta"))
+ params.icp_opmode = IEEE80211_M_STA;
+ else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
+ params.icp_opmode = IEEE80211_M_AHDEMO;
+ else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
+ params.icp_opmode = IEEE80211_M_IBSS;
+ else if (iseq(arg, "ap") || iseq(arg, "host"))
+ params.icp_opmode = IEEE80211_M_HOSTAP;
+ else if (iseq(arg, "wds"))
+ params.icp_opmode = IEEE80211_M_WDS;
+ else if (iseq(arg, "monitor"))
+ params.icp_opmode = IEEE80211_M_MONITOR;
+ else
+ errx(1, "Don't know to create %s for %s", arg, name);
+ clone_setcallback(wlan_create);
+#undef iseq
+}
+
+static void
+set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ /* NB: inverted sense */
+ if (d)
+ params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
+ else
+ params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
+ clone_setcallback(wlan_create);
+}
+
+static void
+set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ if (d)
+ params.icp_flags |= IEEE80211_CLONE_BSSID;
+ else
+ params.icp_flags &= ~IEEE80211_CLONE_BSSID;
+ clone_setcallback(wlan_create);
+}
+
+static void
+set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ if (d)
+ params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
+ else
+ params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
+ clone_setcallback(wlan_create);
+}
+
static struct cmd ieee80211_cmds[] = {
DEF_CMD_ARG("ssid", set80211ssid),
DEF_CMD_ARG("nwid", set80211ssid),
@@ -3339,6 +4386,8 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD_ARG("roaming", set80211roaming),
DEF_CMD("wme", 1, set80211wme),
DEF_CMD("-wme", 0, set80211wme),
+ DEF_CMD("wmm", 1, set80211wme),
+ DEF_CMD("-wmm", 0, set80211wme),
DEF_CMD("hidessid", 1, set80211hidessid),
DEF_CMD("-hidessid", 0, set80211hidessid),
DEF_CMD("apbridge", 1, set80211apbridge),
@@ -3365,6 +4414,7 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd),
DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd),
DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd),
+ DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd),
DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd),
DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd),
DEF_CMD_ARG("mac:add", set80211addmac),
@@ -3381,13 +4431,13 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD_ARG("bgscanidle", set80211bgscanidle),
DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl),
DEF_CMD_ARG("scanvalid", set80211scanvalid),
- DEF_CMD_ARG("roam:rssi11a", set80211roamrssi11a),
- DEF_CMD_ARG("roam:rssi11b", set80211roamrssi11b),
- DEF_CMD_ARG("roam:rssi11g", set80211roamrssi11g),
- DEF_CMD_ARG("roam:rate11a", set80211roamrate11a),
- DEF_CMD_ARG("roam:rate11b", set80211roamrate11b),
- DEF_CMD_ARG("roam:rate11g", set80211roamrate11g),
+ DEF_CMD_ARG("roam:rssi", set80211roamrssi),
+ DEF_CMD_ARG("roam:rate", set80211roamrate),
DEF_CMD_ARG("mcastrate", set80211mcastrate),
+ DEF_CMD_ARG("ucastrate", set80211ucastrate),
+ DEF_CMD_ARG("mgtrate", set80211mgtrate),
+ DEF_CMD_ARG("mgmtrate", set80211mgtrate),
+ DEF_CMD_ARG("maxretry", set80211maxretry),
DEF_CMD_ARG("fragthreshold", set80211fragthreshold),
DEF_CMD("burst", 1, set80211burst),
DEF_CMD("-burst", 0, set80211burst),
@@ -3414,10 +4464,27 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD("-puren", 0, set80211puren),
DEF_CMD("doth", 1, set80211doth),
DEF_CMD("-doth", 0, set80211doth),
+ DEF_CMD("dfs", 1, set80211dfs),
+ DEF_CMD("-dfs", 0, set80211dfs),
DEF_CMD("htcompat", 1, set80211htcompat),
DEF_CMD("-htcompat", 0, set80211htcompat),
+ DEF_CMD("dwds", 1, set80211dwds),
+ DEF_CMD("-dwds", 0, set80211dwds),
DEF_CMD("inact", 1, set80211inact),
DEF_CMD("-inact", 0, set80211inact),
+ DEF_CMD("tsn", 1, set80211tsn),
+ DEF_CMD("-tsn", 0, set80211tsn),
+ DEF_CMD_ARG("regdomain", set80211regdomain),
+ DEF_CMD_ARG("country", set80211country),
+ DEF_CMD("indoor", 'I', set80211location),
+ DEF_CMD("-indoor", 'O', set80211location),
+ DEF_CMD("outdoor", 'O', set80211location),
+ DEF_CMD("-outdoor", 'I', set80211location),
+ DEF_CMD("anywhere", ' ', set80211location),
+ DEF_CMD("ecm", 1, set80211ecm),
+ DEF_CMD("-ecm", 0, set80211ecm),
+ DEF_CMD("dotd", 1, set80211dotd),
+ DEF_CMD("-dotd", 0, set80211dotd),
DEF_CMD_ARG("htprotmode", set80211htprotmode),
DEF_CMD("ht20", 1, set80211htconf),
DEF_CMD("-ht20", 0, set80211htconf),
@@ -3425,6 +4492,20 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD("-ht40", 0, set80211htconf),
DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */
DEF_CMD("-ht", 0, set80211htconf),
+ /* XXX for testing */
+ DEF_CMD_ARG("chanswitch", set80211chanswitch),
+
+ /* vap cloning support */
+ DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr),
+ DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid),
+ DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev),
+ DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode),
+ DEF_CLONE_CMD("beacons", 1, set80211clone_beacons),
+ DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons),
+ DEF_CLONE_CMD("bssid", 1, set80211clone_bssid),
+ DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid),
+ DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy),
+ DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy),
};
static struct afswtch af_ieee80211 = {
.af_name = "af_ieee80211",
diff --git a/sbin/ifconfig/ifmedia.c b/sbin/ifconfig/ifmedia.c
index 947b961..04e2222 100644
--- a/sbin/ifconfig/ifmedia.c
+++ b/sbin/ifconfig/ifmedia.c
@@ -102,6 +102,11 @@ static struct ifmedia_type_to_subtype *get_toptype_ttos(int);
static struct ifmedia_description *get_subtype_desc(int,
struct ifmedia_type_to_subtype *ttos);
+#define IFM_OPMODE(x) \
+ ((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \
+ IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR))
+#define IFM_IEEE80211_STA 0
+
static void
media_status(int s)
{
@@ -162,10 +167,13 @@ media_status(int s)
break;
case IFM_IEEE80211:
- /* XXX: Different value for adhoc? */
- if (ifmr.ifm_status & IFM_ACTIVE)
- printf("associated");
- else
+ if (ifmr.ifm_status & IFM_ACTIVE) {
+ /* NB: only sta mode associates */
+ if (IFM_OPMODE(ifmr.ifm_active) == IFM_IEEE80211_STA)
+ printf("associated");
+ else
+ printf("running");
+ } else
printf("no carrier");
break;
}
diff --git a/sbin/ifconfig/regdomain.c b/sbin/ifconfig/regdomain.c
new file mode 100644
index 0000000..55c382e
--- /dev/null
+++ b/sbin/ifconfig/regdomain.c
@@ -0,0 +1,636 @@
+/*-
+ * Copyright (c) 2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+#ifndef lint
+static const char rcsid[] = "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <err.h>
+#include <unistd.h>
+
+#include <bsdxml.h>
+
+#include "regdomain.h"
+
+#include <net80211/_ieee80211.h>
+
+#define MAXLEVEL 20
+
+struct mystate {
+ struct regdata *rdp;
+ struct regdomain *rd; /* current domain */
+ struct netband *netband; /* current netband */
+ struct freqband *freqband; /* current freqband */
+ struct country *country; /* current country */
+ netband_head *curband; /* current netband list */
+ int level;
+ struct sbuf *sbuf[MAXLEVEL];
+ int nident;
+};
+
+struct ident {
+ const void *id;
+ void *p;
+ enum { DOMAIN, COUNTRY, FREQBAND } type;
+};
+
+static void
+start_element(void *data, const char *name, const char **attr)
+{
+#define iseq(a,b) (strcasecmp(a,b) == 0)
+ struct mystate *mt;
+ const void *id, *ref, *mode;
+ int i;
+
+ mt = data;
+ if (++mt->level == MAXLEVEL) {
+ /* XXX force parser to abort */
+ return;
+ }
+ mt->sbuf[mt->level] = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
+ id = ref = mode = NULL;
+ for (i = 0; attr[i] != NULL; i += 2) {
+ if (iseq(attr[i], "id")) {
+ id = attr[i+1];
+ } else if (iseq(attr[i], "ref")) {
+ ref = attr[i+1];
+ } else if (iseq(attr[i], "mode")) {
+ mode = attr[i+1];
+ } else
+ printf("%*.*s[%s = %s]\n", mt->level + 1,
+ mt->level + 1, "", attr[i], attr[i+1]);
+ }
+ if (iseq(name, "rd") && mt->rd == NULL) {
+ if (mt->country == NULL) {
+ mt->rd = calloc(1, sizeof(struct regdomain));
+ mt->rd->name = strdup(id);
+ mt->nident++;
+ LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
+ } else
+ mt->country->rd = (void *)strdup(ref);
+ return;
+ }
+ if (iseq(name, "defcc") && mt->rd != NULL) {
+ mt->rd->cc = (void *)strdup(ref);
+ return;
+ }
+ if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
+ if (mode == NULL) {
+ /* XXX complain */
+ return;
+ }
+ if (iseq(mode, "11b"))
+ mt->curband = &mt->rd->bands_11b;
+ else if (iseq(mode, "11g"))
+ mt->curband = &mt->rd->bands_11g;
+ else if (iseq(mode, "11a"))
+ mt->curband = &mt->rd->bands_11a;
+ else if (iseq(mode, "11ng"))
+ mt->curband = &mt->rd->bands_11ng;
+ else if (iseq(mode, "11na"))
+ mt->curband = &mt->rd->bands_11na;
+ /* XXX else complain */
+ return;
+ }
+ if (iseq(name, "band") && mt->netband == NULL) {
+ if (mt->curband == NULL) {
+ /* XXX complain */
+ return;
+ }
+ mt->netband = calloc(1, sizeof(struct netband));
+ LIST_INSERT_HEAD(mt->curband, mt->netband, next);
+ return;
+ }
+ if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
+ /* XXX handle inlines and merge into table? */
+ if (mt->netband->band != NULL) {
+ /* XXX complain */
+ } else
+ mt->netband->band = (void *)strdup(ref);
+ return;
+ }
+
+ if (iseq(name, "country") && mt->country == NULL) {
+ mt->country = calloc(1, sizeof(struct country));
+ mt->country->isoname = strdup(id);
+ mt->nident++;
+ LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
+ return;
+ }
+
+ if (iseq(name, "freqband") && mt->freqband == NULL) {
+ mt->freqband = calloc(1, sizeof(struct freqband));
+ mt->freqband->id = strdup(id);
+ mt->nident++;
+ LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
+ return;
+ }
+#undef iseq
+}
+
+static uint32_t
+decode_flag(const char *p, int len)
+{
+#define iseq(a,b) (strcasecmp(a,b) == 0)
+ static const struct {
+ const char *name;
+ int len;
+ uint32_t value;
+ } flags[] = {
+#define FLAG(x) { #x, sizeof(#x), x }
+ FLAG(IEEE80211_CHAN_A),
+ FLAG(IEEE80211_CHAN_B),
+ FLAG(IEEE80211_CHAN_G),
+ FLAG(IEEE80211_CHAN_HT20),
+ FLAG(IEEE80211_CHAN_HT40),
+ FLAG(IEEE80211_CHAN_ST),
+ FLAG(IEEE80211_CHAN_TURBO),
+ FLAG(IEEE80211_CHAN_PASSIVE),
+ FLAG(IEEE80211_CHAN_DFS),
+ FLAG(IEEE80211_CHAN_CCK),
+ FLAG(IEEE80211_CHAN_OFDM),
+ FLAG(IEEE80211_CHAN_2GHZ),
+ FLAG(IEEE80211_CHAN_5GHZ),
+ FLAG(IEEE80211_CHAN_DYN),
+ FLAG(IEEE80211_CHAN_GFSK),
+ FLAG(IEEE80211_CHAN_GSM),
+ FLAG(IEEE80211_CHAN_STURBO),
+ FLAG(IEEE80211_CHAN_HALF),
+ FLAG(IEEE80211_CHAN_QUARTER),
+ FLAG(IEEE80211_CHAN_HT40U),
+ FLAG(IEEE80211_CHAN_HT40D),
+ FLAG(IEEE80211_CHAN_4MSXMIT),
+ FLAG(IEEE80211_CHAN_NOADHOC),
+ FLAG(IEEE80211_CHAN_NOHOSTAP),
+ FLAG(IEEE80211_CHAN_11D),
+ FLAG(IEEE80211_CHAN_FHSS),
+ FLAG(IEEE80211_CHAN_PUREG),
+ FLAG(IEEE80211_CHAN_108A),
+ FLAG(IEEE80211_CHAN_108G),
+#undef FLAG
+ };
+ int i;
+
+ for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
+ if (len == flags[i].len && iseq(p, flags[i].name))
+ return flags[i].value;
+ return 0;
+#undef iseq
+}
+
+static void
+end_element(void *data, const char *name)
+{
+#define iseq(a,b) (strcasecmp(a,b) == 0)
+ struct mystate *mt;
+ int len;
+ char *p;
+
+ mt = data;
+ sbuf_finish(mt->sbuf[mt->level]);
+ p = sbuf_data(mt->sbuf[mt->level]);
+ len = sbuf_len(mt->sbuf[mt->level]);
+
+ /* <freqband>...</freqband> */
+ if (iseq(name, "freqstart") && mt->freqband != NULL) {
+ mt->freqband->freqStart = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "freqend") && mt->freqband != NULL) {
+ mt->freqband->freqEnd = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "chanwidth") && mt->freqband != NULL) {
+ mt->freqband->chanWidth = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "chansep") && mt->freqband != NULL) {
+ mt->freqband->chanSep = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "flags")) {
+ if (mt->freqband != NULL)
+ mt->freqband->flags |= decode_flag(p, len);
+ else if (mt->netband != NULL)
+ mt->netband->flags |= decode_flag(p, len);
+ else {
+ /* XXX complain */
+ }
+ goto done;
+ }
+
+ /* <rd> ... </rd> */
+ if (iseq(name, "name") && mt->rd != NULL) {
+ mt->rd->name = strdup(p);
+ goto done;
+ }
+ if (iseq(name, "sku") && mt->rd != NULL) {
+ mt->rd->sku = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "netband") && mt->rd != NULL) {
+ mt->curband = NULL;
+ goto done;
+ }
+
+ /* <band> ... </band> */
+ if (iseq(name, "freqband") && mt->netband != NULL) {
+ /* XXX handle inline freqbands */
+ goto done;
+ }
+ if (iseq(name, "maxpower") && mt->netband != NULL) {
+ mt->netband->maxPower = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
+ mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
+ goto done;
+ }
+
+ /* <country>...</country> */
+ if (iseq(name, "isocc") && mt->country != NULL) {
+ mt->country->code = strtoul(p, NULL, 0);
+ goto done;
+ }
+ if (iseq(name, "name") && mt->country != NULL) {
+ mt->country->name = strdup(p);
+ goto done;
+ }
+
+ if (len != 0) {
+ printf("Unexpected XML: name \"%s\" data \"%s\"\n", name, p);
+ /* XXX goto done? */
+ }
+ /* </freqband> */
+ if (iseq(name, "freqband") && mt->freqband != NULL) {
+ /* XXX must have start/end frequencies */
+ /* XXX must have channel width/sep */
+ mt->freqband = NULL;
+ goto done;
+ }
+ /* </rd> */
+ if (iseq(name, "rd") && mt->rd != NULL) {
+ mt->rd = NULL;
+ goto done;
+ }
+ /* </band> */
+ if (iseq(name, "band") && mt->netband != NULL) {
+ if (mt->netband->band == NULL) {
+ printf("No frequency band information at line %d\n",
+#if 0
+ XML_GetCurrentLineNumber(parser));
+#else
+ 0);
+#endif
+ }
+ if (mt->netband->maxPower == 0) {
+ /* XXX complain */
+ }
+ /* default max power w/ DFS to max power */
+ if (mt->netband->maxPowerDFS == 0)
+ mt->netband->maxPowerDFS = mt->netband->maxPower;
+ mt->netband = NULL;
+ goto done;
+ }
+ /* </netband> */
+ if (iseq(name, "netband") && mt->netband != NULL) {
+ mt->curband = NULL;
+ goto done;
+ }
+ /* </country> */
+ if (iseq(name, "country") && mt->country != NULL) {
+ if (mt->country->code == 0) {
+ /* XXX must have iso cc */
+ }
+ if (mt->country->name == NULL) {
+ /* XXX must have name */
+ }
+ if (mt->country->rd == NULL) {
+ /* XXX? rd ref? */
+ }
+ mt->country = NULL;
+ goto done;
+ }
+done:
+ sbuf_delete(mt->sbuf[mt->level]);
+ mt->sbuf[mt->level--] = NULL;
+#undef iseq
+}
+
+static void
+char_data(void *data, const XML_Char *s, int len)
+{
+ struct mystate *mt;
+ const char *b, *e;
+
+ mt = data;
+
+ b = s;
+ e = s + len-1;
+ for (; isspace(*b) && b < e; b++)
+ ;
+ for (; isspace(*e) && e > b; e++)
+ ;
+ if (e != b || (*b != '\0' && !isspace(*b)))
+ sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
+}
+
+static void *
+findid(struct regdata *rdp, const void *id, int type)
+{
+ struct ident *ip;
+
+ for (ip = rdp->ident; ip->id != NULL; ip++)
+ if (ip->type == type && strcasecmp(ip->id, id) == 0)
+ return ip->p;
+ return NULL;
+}
+
+/*
+ * Parse an regdomain XML configuration and build the internal representation.
+ */
+int
+lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
+{
+ XML_Parser parser;
+ struct mystate *mt;
+ struct regdomain *dp;
+ struct country *cp;
+ struct freqband *fp;
+ struct netband *nb;
+ const void *id;
+ int i;
+
+ memset(rdp, 0, sizeof(struct regdata));
+ mt = calloc(1, sizeof(struct mystate));
+ if (mt == NULL)
+ return ENOMEM;
+ /* parse the XML input */
+ mt->rdp = rdp;
+ parser = XML_ParserCreate(NULL);
+ XML_SetUserData(parser, mt);
+ XML_SetElementHandler(parser, start_element, end_element);
+ XML_SetCharacterDataHandler(parser, char_data);
+ if (XML_Parse(parser, p, len, 1) != XML_STATUS_OK) {
+ warnx("%s: %s at line %d", __func__,
+ XML_ErrorString(XML_GetErrorCode(parser)),
+ XML_GetCurrentLineNumber(parser));
+ return -1;
+ }
+ XML_ParserFree(parser);
+
+ /* setup the identifer table */
+ rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
+ if (rdp->ident == NULL)
+ return ENOMEM;
+ free(mt);
+ i = 0;
+ LIST_FOREACH(dp, &rdp->domains, next) {
+ rdp->ident[i].id = dp->name;
+ rdp->ident[i].p = dp;
+ rdp->ident[i].type = DOMAIN;
+ i++;
+ }
+ LIST_FOREACH(fp, &rdp->freqbands, next) {
+ rdp->ident[i].id = fp->id;
+ rdp->ident[i].p = fp;
+ rdp->ident[i].type = FREQBAND;
+ i++;
+ }
+ LIST_FOREACH(cp, &rdp->countries, next) {
+ rdp->ident[i].id = cp->isoname;
+ rdp->ident[i].p = cp;
+ rdp->ident[i].type = COUNTRY;
+ i++;
+ }
+
+ /* patch references */
+ LIST_FOREACH(dp, &rdp->domains, next) {
+ if (dp->cc != NULL) {
+ id = dp->cc;
+ dp->cc = findid(rdp, id, COUNTRY);
+ free(__DECONST(char *, id));
+ }
+ LIST_FOREACH(nb, &dp->bands_11b, next)
+ nb->band = findid(rdp, nb->band, FREQBAND);
+ LIST_FOREACH(nb, &dp->bands_11g, next)
+ nb->band = findid(rdp, nb->band, FREQBAND);
+ LIST_FOREACH(nb, &dp->bands_11a, next)
+ nb->band = findid(rdp, nb->band, FREQBAND);
+ LIST_FOREACH(nb, &dp->bands_11ng, next)
+ nb->band = findid(rdp, nb->band, FREQBAND);
+ LIST_FOREACH(nb, &dp->bands_11na, next)
+ nb->band = findid(rdp, nb->band, FREQBAND);
+ }
+ LIST_FOREACH(cp, &rdp->countries, next) {
+ id = cp->rd;
+ cp->rd = findid(rdp, id, DOMAIN);
+ free(__DECONST(char *, id));
+ }
+
+ return 0;
+}
+
+static void
+cleanup_bands(netband_head *head)
+{
+ struct netband *nb;
+
+ for (;;) {
+ nb = LIST_FIRST(head);
+ if (nb == NULL)
+ break;
+ free(nb);
+ }
+}
+
+/*
+ * Cleanup state/resources for a previously parsed regdomain database.
+ */
+void
+lib80211_regdomain_cleanup(struct regdata *rdp)
+{
+
+ free(rdp->ident);
+ rdp->ident = NULL;
+ for (;;) {
+ struct regdomain *dp = LIST_FIRST(&rdp->domains);
+ if (dp == NULL)
+ break;
+ LIST_REMOVE(dp, next);
+ cleanup_bands(&dp->bands_11b);
+ cleanup_bands(&dp->bands_11g);
+ cleanup_bands(&dp->bands_11a);
+ cleanup_bands(&dp->bands_11ng);
+ cleanup_bands(&dp->bands_11na);
+ if (dp->name != NULL)
+ free(__DECONST(char *, dp->name));
+ }
+ for (;;) {
+ struct country *cp = LIST_FIRST(&rdp->countries);
+ if (cp == NULL)
+ break;
+ LIST_REMOVE(cp, next);
+ if (cp->name != NULL)
+ free(__DECONST(char *, cp->name));
+ free(cp);
+ }
+ for (;;) {
+ struct freqband *fp = LIST_FIRST(&rdp->freqbands);
+ if (fp == NULL)
+ break;
+ LIST_REMOVE(fp, next);
+ free(fp);
+ }
+}
+
+struct regdata *
+lib80211_alloc_regdata(void)
+{
+ struct regdata *rdp;
+ struct stat sb;
+ void *xml;
+ int fd;
+
+ rdp = calloc(1, sizeof(struct regdata));
+
+ fd = open(_PATH_REGDOMAIN, O_RDONLY);
+ if (fd < 0) {
+#ifdef DEBUG
+ warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
+#endif
+ free(rdp);
+ return NULL;
+ }
+ if (fstat(fd, &sb) < 0) {
+#ifdef DEBUG
+ warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
+#endif
+ close(fd);
+ free(rdp);
+ return NULL;
+ }
+ xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (xml == MAP_FAILED) {
+#ifdef DEBUG
+ warn("%s: mmap", __func__);
+#endif
+ close(fd);
+ free(rdp);
+ return NULL;
+ }
+ if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
+#ifdef DEBUG
+ warn("%s: error reading regulatory database", __func__);
+#endif
+ munmap(xml, sb.st_size);
+ close(fd);
+ free(rdp);
+ return NULL;
+ }
+ munmap(xml, sb.st_size);
+ close(fd);
+
+ return rdp;
+}
+
+void
+lib80211_free_regdata(struct regdata *rdp)
+{
+ lib80211_regdomain_cleanup(rdp);
+ free(rdp);
+}
+
+/*
+ * Lookup a regdomain by SKU.
+ */
+const struct regdomain *
+lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
+{
+ const struct regdomain *dp;
+
+ LIST_FOREACH(dp, &rdp->domains, next) {
+ if (dp->sku == sku)
+ return dp;
+ }
+ return NULL;
+}
+
+/*
+ * Lookup a regdomain by name.
+ */
+const struct regdomain *
+lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
+{
+ const struct regdomain *dp;
+
+ LIST_FOREACH(dp, &rdp->domains, next) {
+ if (strcasecmp(dp->name, name) == 0)
+ return dp;
+ }
+ return NULL;
+}
+
+/*
+ * Lookup a country by ISO country code.
+ */
+const struct country *
+lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
+{
+ const struct country *cp;
+
+ LIST_FOREACH(cp, &rdp->countries, next) {
+ if (cp->code == cc)
+ return cp;
+ }
+ return NULL;
+}
+
+/*
+ * Lookup a country by ISO/long name.
+ */
+const struct country *
+lib80211_country_findbyname(const struct regdata *rdp, const char *name)
+{
+ const struct country *cp;
+ int len;
+
+ len = strlen(name);
+ LIST_FOREACH(cp, &rdp->countries, next) {
+ if (strcasecmp(cp->isoname, name) == 0 ||
+ strncasecmp(cp->name, name, len) == 0)
+ return cp;
+ }
+ return NULL;
+}
diff --git a/sbin/ifconfig/regdomain.h b/sbin/ifconfig/regdomain.h
new file mode 100644
index 0000000..e7046a9
--- /dev/null
+++ b/sbin/ifconfig/regdomain.h
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _LIB80211_H_
+#define _LIB80211_H_
+
+#include <sys/cdefs.h>
+#include <sys/queue.h>
+
+#include <net80211/ieee80211_regdomain.h>
+
+__BEGIN_DECLS
+
+struct freqband {
+ uint16_t freqStart; /* starting frequency (MHz) */
+ uint16_t freqEnd; /* ending frequency (MHz) */
+ uint8_t chanWidth; /* channel width (MHz) */
+ uint8_t chanSep; /* channel sepaaration (MHz) */
+ uint32_t flags; /* common operational constraints */
+
+ const void *id;
+ LIST_ENTRY(freqband) next;
+};
+
+struct netband {
+ const struct freqband *band; /* channel list description */
+ uint8_t maxPower; /* regulatory cap on tx power (dBm) */
+ uint8_t maxPowerDFS; /* regulatory cap w/ DFS (dBm) */
+ uint32_t flags; /* net80211 channel flags */
+
+ LIST_ENTRY(netband) next;
+};
+typedef LIST_HEAD(, netband) netband_head;
+
+struct country;
+
+struct regdomain {
+ enum RegdomainCode sku; /* regdomain code/SKU */
+ const char *name; /* printable name */
+ const struct country *cc; /* country code for 1-1/default map */
+
+ netband_head bands_11b; /* 11b operation */
+ netband_head bands_11g; /* 11g operation */
+ netband_head bands_11a; /* 11a operation */
+ netband_head bands_11ng;/* 11ng operation */
+ netband_head bands_11na;/* 11na operation */
+
+ LIST_ENTRY(regdomain) next;
+};
+
+struct country {
+ enum ISOCountryCode code;
+ const struct regdomain *rd;
+ const char* isoname;
+ const char* name;
+
+ LIST_ENTRY(country) next;
+};
+
+struct ident;
+
+struct regdata {
+ LIST_HEAD(, country) countries; /* country code table */
+ LIST_HEAD(, regdomain) domains; /* regulatory domains */
+ LIST_HEAD(, freqband) freqbands; /* frequency band table */
+ struct ident *ident; /* identifier table */
+};
+
+#define _PATH_REGDOMAIN "/etc/regdomain.xml"
+
+struct regdata *lib80211_alloc_regdata(void);
+void lib80211_free_regdata(struct regdata *);
+
+int lib80211_regdomain_readconfig(struct regdata *, const void *, size_t);
+void lib80211_regdomain_cleanup(struct regdata *);
+
+const struct regdomain *lib80211_regdomain_findbysku(const struct regdata *,
+ enum RegdomainCode);
+const struct regdomain *lib80211_regdomain_findbyname(const struct regdata *,
+ const char *);
+
+const struct country *lib80211_country_findbycc(const struct regdata *,
+ enum ISOCountryCode);
+const struct country *lib80211_country_findbyname(const struct regdata *,
+ const char *);
+
+__END_DECLS
+
+#endif /* _LIB80211_H_ */
diff --git a/share/man/man4/ath.4 b/share/man/man4/ath.4
index 44705f1..80d094a 100644
--- a/share/man/man4/ath.4
+++ b/share/man/man4/ath.4
@@ -1,5 +1,5 @@
.\"-
-.\" Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+.\" Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
.\" All rights reserved.
.\""
.\" Redistribution and use in source and binary forms, with or without
@@ -12,9 +12,6 @@
.\" similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
.\" redistribution must be conditioned upon including a substantially
.\" similar Disclaimer requirement for further binary redistribution.
-.\" 3. Neither the names of the above-listed copyright holders nor the names
-.\" of any contributors may be used to endorse or promote products derived
-.\" from this software without specific prior written permission.
.\"
.\" NO WARRANTY
.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -31,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"/
-.Dd December 7, 2006
+.Dd April 13, 2008
.Dt ATH 4
.Os
.Sh NAME
@@ -59,7 +56,7 @@ The
.Nm
driver provides support for wireless network adapters based on
the Atheros AR5210, AR5211, and AR5212 programming APIs.
-These APIs are used by a wide variety of chips; all chips with
+These APIs are used by a wide variety of chips; most all chips with
a PCI and/or CardBus interface are supported.
Chip-specific support is provided by the Atheros Hardware Access Layer
(HAL), that is packaged separately.
@@ -97,26 +94,32 @@ To enable encryption, use
.Xr ifconfig 8
as shown below.
.Pp
-By default, the
-.Nm
-driver configures the card for BSS operation (aka infrastructure
-mode).
-This mode requires the use of an access point (base station).
-.Pp
-The
-.Nm
-driver also supports the standard IBSS point-to-point mode
-where stations can communicate amongst themselves without the
-aid of an access point.
-.Pp
-The driver may also be configured to operate in hostap mode.
-In this mode a host may function as an access point (base station).
-Access points are different than operating in IBSS mode.
-They operate in BSS mode.
-They allow for easier roaming and bridge all Ethernet traffic such
-that machines connected via an access point appear to be on the local
-Ethernet segment.
-.Pp
+The driver supports
+.Cm station ,
+.Cm adhoc ,
+.Cm adhoc-demo ,
+.Cm hostap ,
+.Cm wds ,
+and
+.Cm monitor
+mode operation.
+Multiple
+.Cm hostap
+virtual interfaces may be configured for simultaneous
+use on cards that use a 5212 part.
+When multiple interfaces are configured each may have a separate
+mac address that is formed by setting the U/L bits in the mac
+address assigned to the underlying device.
+Any number of
+.Cm wds
+virtual interfaces may be configured together with
+.Cm hostap
+interfaces.
+Multiple
+.Cm station
+interfaces may be operated together with
+.Cm hostap
+interfaces to construct a wireless repeater device.
For more information on configuring this device, see
.Xr ifconfig 8 .
.Pp
@@ -127,8 +130,10 @@ Wireless cards in Cardbus slots may be inserted and ejected on the fly.
.Sh HARDWARE
The
.Nm
-driver supports all Atheros Cardbus or PCI cards,
+driver supports most Atheros Cardbus or PCI cards,
except those that are based on the AR5005VL chipset.
+More recent parts may require a hal that is not part of
+.Fb .
.Pp
A list of cards that are supported can be found at
.Pa http://customerproducts.atheros.com/customerproducts/default.asp .
@@ -144,38 +149,37 @@ Join a specific BSS network with network name
.Pp
Join a specific BSS network with WEP encryption:
.Bd -literal -offset indent
-ifconfig ath0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan0 create wlandev ath0
+ifconfig wlan0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
wepmode on wepkey 0x8736639624
.Ed
.Pp
Join/create an 802.11b IBSS network with network name
.Dq Li my_net :
.Bd -literal -offset indent
-ifconfig ath0 inet 192.168.0.22 netmask 0xffffff00 ssid my_net \e
- mode 11b mediaopt adhoc
+ifconfig wlan0 create wlandev ath0 wlanmode adhoc
+ifconfig wlan0 inet 192.168.0.22 netmask 0xffffff00 ssid my_net \e
+ mode 11b
.Ed
.Pp
Create an 802.11g host-based access point:
.Bd -literal -offset indent
-ifconfig ath0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
- mode 11g mediaopt hostap
+ifconfig wlan0 create wlandev ath0 wlanmode hostap
+ifconfig wlan0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
+ mode 11g
.Ed
.Pp
-Create an 802.11a host-based access point with WEP enabled:
+Create two virtual 802.11a host-based access points, one with
+with WEP enabled and one with no security, and bridge them to
+the fxp0 (wired) device:
.Bd -literal -offset indent
-ifconfig ath0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
- wepmode on wepkey 0x1234567890 mode 11a mediaopt hostap
+ifconfig wlan0 create wlandev ath0 wlanmode hostap \e
+ ssid paying-customers wepmode on wepkey 0x1234567890 \e
+ mode 11a up
+ifconfig wlan1 create wlandev ath0 wlanmode hostap bssid \e
+ ssid freeloaders up
+ifconfig bridge0 create addm wlan0 addm wlan1 addm fxp0 up
.Ed
-.Pp
-Create a host-based wireless bridge to fxp0:
-.Bd -literal -offset indent
-ifconfig ath0 inet up ssid my_ap media DS/11Mbps mediaopt hostap
-sysctl net.inet.ip.check_interface=0
-ifconfig bridge0 create
-ifconfig bridge0 addm ath0 addm fxp0 up
-.Ed
-.Pp
-This will give you the same functionality as an access point.
.Sh DIAGNOSTICS
.Bl -diag
.It "ath%d: unable to attach hardware; HAL status %u"
@@ -252,15 +256,18 @@ The driver was unable to install the device interrupt handler.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr an 4 ,
-.Xr arp 4 ,
.Xr ath_hal 4 ,
-.Xr netintro 4 ,
+.Xr intro 4 ,
+.Xr cardbus 4 ,
.Xr pcic 4 ,
-.Xr wi 4 ,
.Xr wlan 4 ,
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr wlan_xauth 4 ,
+.Xr hostapd 8 ,
.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh HISTORY
The
.Nm
@@ -281,17 +288,7 @@ Intersil PrismGT chip and are not supported by this driver.
.Sh BUGS
There is no software retransmit; only hardware retransmit is used.
.Pp
-The driver does not fully enable power-save operation of the chip;
-consequently power use is suboptimal.
-.Pp
-The driver honors the regulatory domain programmed into the EEPROM of a
-device and does not support overriding this setting.
-This is done to insure compliance with local regulatory agencies when
-operating as an access point.
-Unfortunately this also means that devices purchased for use in one locale
-may not be usable in another.
-Changes are planned to remove this restriction when operating in station mode.
+The driver does not fully enable power-save operation of the chip
+in station mode; consequently power use is suboptimal (e.g. on a laptop).
.Pp
WPA is not supported for 5210 parts.
-.Pp
-Atheros' SuperG functionality is not supported.
diff --git a/share/man/man4/ipw.4 b/share/man/man4/ipw.4
index 02c1f6a..30293f0 100644
--- a/share/man/man4/ipw.4
+++ b/share/man/man4/ipw.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 6, 2007
+.Dd April 13, 2008
.Os
.Dt IPW 4
.Sh NAME
@@ -37,27 +37,24 @@ place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device ipw"
+.Cd "device ipwfw"
.Cd "device pci"
.Cd "device wlan"
.Cd "device firmware"
.Ed
.Pp
Alternatively, to load the driver as a
-module at boot time, place the following lines in
+module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_ipw_load="YES"
-wlan_load="YES"
-firmware_load="YES"
.Ed
.Pp
-In both cases, place the following lines in
+In both cases, place the following line in
.Xr loader.conf 5
-to load the firmware modules:
+to acknowledge the firmware license (see below):
.Bd -literal -offset indent
-ipw_bss_load="YES"
-ipw_ibss_load="YES"
-ipw_monitor_load="YES"
+legal.intel_ipw.license_ack=1
.Ed
.Sh DESCRIPTION
The
@@ -65,19 +62,23 @@ The
driver provides support for the
.Tn Intel
PRO/Wireless 2100 MiniPCI network adapter.
-.Pp
-By default, the
.Nm
-driver configures the adapter for BSS operation (aka infrastructure mode).
-This mode requires the use of an access point.
-.Pp
+supports
+.Cm station ,
+.Cm adhoc ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
For more information on configuring this device, see
.Xr ifconfig 8 .
.Pp
-This driver requires firmware to be loaded before it will work.
-For the loaded firmware to work the license at
+This driver requires the firmware built with the
+.Nm ipwfw
+module to work.
+For the loaded firmware to be enabled for use the license at
.Pa /usr/share/doc/legal/intel_ipw/LICENSE
-must be agreed to and the following line be added to
+must be agreed by adding the following line to
.Xr loader.conf 5 :
.Pp
.Dl "legal.intel_ipw.license_ack=1"
@@ -90,22 +91,25 @@ firmware license
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig ipw0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev ipw0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig ipw0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev ipw0 ssid my_net up"
.Pp
-Join a specific BSS network with 40-bit WEP encryption:
+Join a specific BSS network with 64-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ipw0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x1234567890 weptxkey 1
+ifconfig wlan create wlandev ipw0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
.Ed
.Pp
-Join a specific BSS network with 104-bit WEP encryption:
+Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ipw0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev ipw0 wlanmode adhoc ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
@@ -124,8 +128,9 @@ This should not happen.
The driver failed to load the firmware image using the
.Xr firmware 9
subsystem.
-Verify that the necessary firmware modules are loaded and the
-license agreement
+Verify the
+.Xr ipwfw 4
+firmware module is installed and the license agreement
.Xr loader 8
tunable has been set.
.It "ipw%d: could not load microcode"
@@ -136,14 +141,16 @@ An attempt to upload the firmware image to the onboard microcontroller failed.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr iwi 4 ,
+.Xr ipwfw 4 ,
.Xr pci 4 ,
.Xr wlan 4 ,
-.Xr wpi 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
-.An Damien Bergamini Aq damien@FreeBSD.org .
+.An Damien Bergamini Aq damien.bergamini@free.fr
diff --git a/share/man/man4/iwi.4 b/share/man/man4/iwi.4
index 8a6e9bc..8f82df2 100644
--- a/share/man/man4/iwi.4
+++ b/share/man/man4/iwi.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 6, 2007
+.Dd Apri 13, 2008
.Os
.Dt IWI 4
.Sh NAME
@@ -33,31 +33,28 @@
.Nd "Intel PRO/Wireless 2200BG/2225BG/2915ABG IEEE 802.11 driver"
.Sh SYNOPSIS
To compile this driver into the kernel,
-place the following lines in your
+include the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device iwi"
+.Cd "device iwifw"
.Cd "device pci"
.Cd "device wlan"
.Cd "device firmware"
.Ed
.Pp
Alternatively, to load the driver as a
-module at boot time, place the following lines in
+module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_iwi_load="YES"
-wlan_load="YES"
-firmware_load="YES"
.Ed
.Pp
-In both cases, place the following lines in
+In both cases, place the following line in
.Xr loader.conf 5
-to load the firmware modules:
+to acknowledge the firmware license (see below):
.Bd -literal -offset indent
-iwi_bss_load="YES"
-iwi_ibss_load="YES"
-iwi_monitor_load="YES"
+legal.intel_iwi.license_ack=1
.Ed
.Sh DESCRIPTION
The
@@ -65,19 +62,23 @@ The
driver provides support for
.Tn Intel
PRO/Wireless 2200BG/2915ABG MiniPCI and 2225BG PCI network adapters.
-.Pp
-By default, the
.Nm
-driver configures the adapter for BSS operation (aka infrastructure mode).
-This mode requires the use of an access point.
-.Pp
+supports
+.Cm station ,
+.Cm adhoc ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
For more information on configuring this device, see
.Xr ifconfig 8 .
.Pp
-This driver requires firmware to be loaded before it will work.
-For the loaded firmware to work the license at
+This driver requires the firmware built with the
+.Nm iwifw
+module to work.
+For the loaded firmware to be enabled for use the license at
.Pa /usr/share/doc/legal/intel_iwi/LICENSE
-must be agreed to and the following line be added to
+must be agreed by adding the following line to
.Xr loader.conf 5 :
.Pp
.Dl "legal.intel_iwi.license_ack=1"
@@ -90,22 +91,25 @@ firmware license
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig iwi0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev iwi0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig iwi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev iwi0 ssid my_net up"
.Pp
Join a specific BSS network with 64-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig iwi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x1234567890 weptxkey 1
+ifconfig wlan create wlandev iwi0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
.Ed
.Pp
Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig iwi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev iwi0 wlanmode adhoc ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
@@ -114,7 +118,7 @@ ifconfig iwi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
The driver will reset the hardware.
This should not happen.
.It "iwi%d: firmware error"
-The onboard microcontroller crashes for some reason.
+The onboard microcontroller crashed for some reason.
The driver will reset the hardware.
This should not happen.
.It "iwi%d: timeout waiting for firmware initialization to complete"
@@ -124,8 +128,9 @@ This should not happen.
The driver failed to load the firmware image using the
.Xr firmware 9
subsystem.
-Verify that the necessary firmware modules are loaded and the
-license agreement
+Verify the
+.Xr iwifw 4
+firmware module is installed and the license agreement
.Xr loader 8
tunable has been set.
.It "iwi%d: could not load boot firmware"
@@ -141,14 +146,16 @@ failed.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr ipw 4 ,
+.Xr iwifw 4 ,
.Xr pci 4 ,
.Xr wlan 4 ,
-.Xr wpi 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
-.An Damien Bergamini Aq damien@FreeBSD.org .
+.An Damien Bergamini Aq damien.bergamini@free.fr
diff --git a/share/man/man4/malo.4 b/share/man/man4/malo.4
index 98f702a..e3a5c6b 100644
--- a/share/man/man4/malo.4
+++ b/share/man/man4/malo.4
@@ -31,7 +31,7 @@
.\"
.\" $FreeBSD$
.\"/
-.Dd March 25, 2008
+.Dd April 13, 2008
.Dt MALO 4
.Os
.Sh NAME
@@ -53,41 +53,33 @@ module at boot time, place the following lines in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_malo_load="YES"
-wlan_load="YES"
-firmware_load="YES"
-.Ed
-.Pp
-In both cases, place the following line in
-.Xr loader.conf 5
-to load the firmware module:
-.Bd -literal -offset indent
-malofw_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides support for Marvell Libertas 88W8335 based PCI
-network adapters.
-.Pp
-This driver requires firmware to be loaded before it will work.
-These firmware files are from the
-.Ox
+and Cardbus network adapters.
.Nm
-driver.
+supports
+.Cm station ,
+.Cm adhoc ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Pp
-A package for the firmware which can be installed via
+This driver requires the
+.Nm malofw
+be installed before it will work.
+The firmware files are not publicly available.
+A package of the firmware which can be installed via
.Xr pkg_add 1
-can be found at:
+with:
.Bd -literal -offset indent
http://weongyo.org/project/malo/malo-firmware-1.4.tar.gz
.Ed
-.Pp
-This package must be installed before
-.Xr ifconfig 8
-will work.
-.Pp
-For more information on configuring this device, see
-.Xr ifconfig 8 .
.Sh HARDWARE
The following cards are among those supported by the
.Nm
@@ -102,26 +94,38 @@ U-Khan UW-2054i 88W8335 PCI b/g
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig malo0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev iwn0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig malo0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev iwn0 ssid my_net up"
+.Pp
+Join a specific BSS network with 64-bit WEP encryption:
+.Bd -literal -offset indent
+ifconfig wlan create wlandev iwn0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
+.Ed
.Pp
-Join a specific BSS network with WEP encryption:
+Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig malo0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x8736639624
+ifconfig wlan create wlandev iwn0 wlanmode adhoc ssid my_net \e
+ wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
+.Ed
.Ed
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr arp 4 ,
-.Xr cardbus 4 ,
-.Xr netintro 4 ,
+.Xr malofw 4 ,
.Xr pci 4 ,
+.Xr cardbus 4 ,
.Xr wlan 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh HISTORY
The
.Nm
diff --git a/share/man/man4/ral.4 b/share/man/man4/ral.4
index 65ee974..be64123 100644
--- a/share/man/man4/ral.4
+++ b/share/man/man4/ral.4
@@ -15,7 +15,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 10, 2007
+.Dd April 13, 2008
.Os
.Dt RAL 4
.Sh NAME
@@ -27,7 +27,10 @@ place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device ral"
+.Cd "device ralfw"
.Cd "device wlan"
+.Cd "device wlan_amrr"
+.Cd "device firmware"
.Ed
.Pp
Alternatively, to load the driver as a
@@ -63,6 +66,31 @@ MIMO is the basis of the forthcoming IEEE 802.11n standard.
The transmit speed is user-selectable or can be adapted automatically by the
driver depending on the received signal strength and on the number of hardware
transmission retries.
+.Pp
+.Nm
+supports
+.Cm station ,
+.Cm adhoc ,
+.Cm hostap ,
+.Cm wds ,
+and
+.Cm monitor
+mode operation.
+Only one
+.Cm hostap
+virtual interface may be configured at a time.
+Any number of
+.Cm wds
+virtual interfaces may be configured together with a
+.Cm hostap
+interface.
+Multiple
+.Cm station
+interfaces may be operated together with a
+.Cm hostap
+interface to construct a wireless repeater device.
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Sh HARDWARE
The
.Nm
@@ -163,22 +191,27 @@ An up to date list can be found at
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00"
+.Dl "ifconfig wlan create wlandev ral0 inet 192.168.0.20 netmask 0xffffff00"
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev ral0 inet 192.168.0.20 \e
+ netmask 0xffffff00 ssid my_net
+.Ed
.Pp
Join a specific BSS network with 40-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev ral0 inet 192.168.0.20 \e
+ netmask 0xffffff00 ssid my_net \e
wepmode on wepkey 0x1234567890 weptxkey 1
.Ed
.Pp
Join a specific BSS network with 104-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev ral0 inet 192.168.0.20 \e
+ netmask 0xffffff00 ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
@@ -194,13 +227,16 @@ The driver will reset the hardware.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr arp 4 ,
+.Xr intro 4 ,
.Xr cardbus 4 ,
-.Xr netintro 4 ,
-.Xr pci 4 ,
.Xr wlan 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr wlan_xauth 4 ,
+.Xr hostapd 8 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Rs
.%T "Ralink Technology"
.%O http://www.ralinktech.com/
@@ -210,17 +246,8 @@ The
.Nm
driver first appeared in
.Ox 3.7 .
-.Sh CAVEATS
-PCI
-.Nm
-adapters seem to require a PCI 2.2 compliant motherboard and will likely not
-work with PCI 2.1 only motherboard.
-.Pp
-The
-.Nm
-driver does not implement frame aggregation.
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
.An Damien Bergamini Aq damien@FreeBSD.org .
diff --git a/share/man/man4/rum.4 b/share/man/man4/rum.4
index ddb71a8..c34b245 100644
--- a/share/man/man4/rum.4
+++ b/share/man/man4/rum.4
@@ -16,7 +16,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 7, 2007
+.Dd April 13, 2008
.Os
.Dt RUM 4
.Sh NAME
@@ -45,11 +45,11 @@ if_rum_load="YES"
.Sh DESCRIPTION
The
.Nm
-driver supports USB 2.0 and PCI Express Mini Card wireless adapters based on the
-Ralink RT2501USB and RT2601USB chipsets.
+driver supports USB 2.0 and PCI Express Mini Card wireless adapters
+based on the Ralink RT2501USB and RT2601USB chipsets.
.Pp
-Ralink PCI Express Mini Card adapters show up as normal USB 2.0 devices and are
-thus handled by the
+Ralink PCI Express Mini Card adapters show up as normal USB 2.0
+devices and are thus handled by the
.Nm
driver.
.Pp
@@ -58,15 +58,27 @@ Ralink.
It consists of two integrated chips, an RT2571W MAC/BBP and an RT2528 or
RT5226 radio transceiver.
.Pp
-The RT2601USB chipset consists of two integrated chips, an RT2671 MAC/BBP and
-an RT2527 or RT5225 radio transceiver.
-This chipset uses the MIMO (multiple-input multiple-output) technology with
-multiple antennas to extend the operating range of the adapter and to achieve
-higher throughput.
+The RT2601USB chipset consists of two integrated chips, an RT2671
+MAC/BBP and an RT2527 or RT5225 radio transceiver.
+This chipset uses the MIMO (multiple-input multiple-output) technology
+with multiple antennas to extend the operating range of the adapter
+and to achieve higher throughput.
+.Pp
+.Nm
+supports
+.Cm station ,
+.Cm adhoc ,
+.Cm hostap ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Sh HARDWARE
The
.Nm
-driver supports USB 2.0 and PCI Express Mini Card wireless
+driver supports USB 2.0 wireless
adapters based on the Ralink RT2501USB and RT2601USB chipsets,
including:
.Pp
@@ -108,22 +120,25 @@ including:
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig rum0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev rum0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig rum0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev rum0 ssid my_net up"
.Pp
-Join a specific BSS network with 40-bit WEP encryption:
+Join a specific BSS network with 64-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig rum0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x1234567890 weptxkey 1
+ifconfig wlan create wlandev rum0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
.Ed
.Pp
-Join a specific BSS network with 104-bit WEP encryption:
+Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig rum0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev rum0 wlanmode adhoc ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
@@ -135,13 +150,18 @@ The driver will reset the hardware.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr arp 4 ,
+.Xr intro 4 ,
.Xr netintro 4 ,
.Xr usb 4 ,
.Xr wlan 4 ,
.Xr wlan_amrr 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr wlan_xauth 4 ,
+.Xr ifconfig 8 ,
+.Xr hostapd 8 ,
+.Xr wpa_supplicant 8 .
.Rs
.%T "Ralink Technology"
.%O http://www.ralinktech.com/
@@ -151,16 +171,8 @@ The
.Nm
driver first appeared in
.Ox 4.0 .
-.Sh CAVEATS
-.Pp
-The
-.Nm
-driver supports automatic control of the transmit speed in BSS mode only.
-Therefore the use of a
-.Nm
-adapter in Host AP mode is discouraged.
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
.An Niall O'Higgins Aq niallo@openbsd.org
diff --git a/share/man/man4/ural.4 b/share/man/man4/ural.4
index b400838..453cfb4 100644
--- a/share/man/man4/ural.4
+++ b/share/man/man4/ural.4
@@ -15,7 +15,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 10, 2007
+.Dd April 13, 2008
.Os
.Dt URAL 4
.Sh NAME
@@ -52,6 +52,18 @@ and a radio transceiver (the model of which depends on the card revision).
The RT2522, RT2523, RT2524, RT2525, RT2525e and RT2526 radio transceivers
operate in the 2.4GHz band (802.11b/g) whereas the RT5222 is a dual-band radio
transceiver that can operate in the 2.4GHz and 5.2GHz bands (802.11a).
+.Pp
+.Nm
+supports
+.Cm station ,
+.Cm adhoc ,
+.Cm hostap ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Sh HARDWARE
The
.Nm
@@ -94,22 +106,25 @@ An up to date list can be found at
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig ural0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev ural0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig ural0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev ural0 ssid my_net up"
.Pp
-Join a specific BSS network with 40-bit WEP encryption:
+Join a specific BSS network with 64-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ural0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x1234567890 weptxkey 1
+ifconfig wlan create wlandev ural0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
.Ed
.Pp
-Join a specific BSS network with 104-bit WEP encryption:
+Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig ural0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev ural0 wlanmode adhoc ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
@@ -119,13 +134,18 @@ The driver will reset the hardware.
This should not happen.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr arp 4 ,
+.Xr intro 4 ,
.Xr netintro 4 ,
.Xr usb 4 ,
.Xr wlan 4 ,
.Xr wlan_amrr 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr wlan_xauth 4 ,
+.Xr ifconfig 8 ,
+.Xr hostapd 8 ,
+.Xr wpa_supplicant 8 .
.Rs
.%T "Ralink Technology"
.%O http://www.ralinktech.com/
@@ -135,14 +155,8 @@ The
.Nm
driver first appeared in
.Ox 3.7 .
-.Sh CAVEATS
-.Pp
-The
-.Nm
-driver does not support automatic adaptation of the transmit speed in IBSS
-and HostAP operating modes.
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
-.An Damien Bergamini Aq damien@FreeBSD.org .
+.An Damien Bergamini Aq damien.bergamini@free.fr
diff --git a/share/man/man4/wi.4 b/share/man/man4/wi.4
index 2e35536..5a08527 100644
--- a/share/man/man4/wi.4
+++ b/share/man/man4/wi.4
@@ -31,12 +31,12 @@
.\" $FreeBSD$
.\" $OpenBSD: wi.4tbl,v 1.14 2002/04/29 19:53:50 jsyn Exp $
.\"
-.Dd July 16, 2005
+.Dd April 13, 2008
.Dt WI 4
.Os
.Sh NAME
.Nm wi
-.Nd "Lucent Hermes, Intersil PRISM and Spectrum24 IEEE 802.11 driver"
+.Nd "Lucent Hermes, and Intersil PRISM IEEE 802.11 driver"
.Sh SYNOPSIS
To compile this driver into the kernel,
place the following lines in your
@@ -55,74 +55,30 @@ if_wi_load="YES"
.Sh DESCRIPTION
The
.Nm
-driver provides support for wireless network adapters based around
-the Lucent Hermes, Intersil PRISM-II, Intersil PRISM-2.5, Intersil
-Prism-3, and Symbol Spectrum24 chipsets.
-All five chipsets provide a similar interface to the driver.
-.Pp
-Supported features include 802.11 and 802.3 frames, power management, BSS,
-IBSS, WDS and old-style Lucent ad-hoc operation modes.
-Cards based on the Intersil PRISM chips also support a host-based
-access point mode which allows a card to act as a normal access point
-(with some assistance from the
-.Nm
-driver).
-The Lucent Hermes and Symbol Spectrum24 chipsets do not contain this
-functionality.
-The PRISM family of chips do not support the WDS functionality.
+driver provides support for 802.11b wireless network adapters based around
+the Lucent Hermes, Intersil PRISM-II, Intersil PRISM-2.5, and Intersil
+Prism-3 chipsets.
+All chipsets provide a similar interface to the driver.
+Only the Intersil chipsets support access point operation.
+Functionality, such as WPA support, depends on the specific version
+of the firmware used.
All host/device interaction is via programmed I/O, even on those cards
that support a DMA interface.
.Pp
-The
-.Nm
-driver encapsulates all IP and ARP traffic as 802.11 frames, however
-it can receive either 802.11 or 802.3 frames.
-Transmit speed is selectable between 1Mbps, 2Mbps, 5.5 Mbps and 11Mbps
-depending on your hardware.
-Most hardware supports 11Mbps where the signal quality allows, but
-falls back to slower speeds when it does not.
-Except for the Lucent WaveLAN Bronze cards, all cards supported by the
-.Nm
-driver support WEP for encryption.
-To enable encryption, use
-.Xr ifconfig 8
-as shown below.
-.Pp
-By default, the
-.Nm
-driver configures the card for BSS operation (aka infrastructure
-mode).
-This mode requires the use of an access point (base station).
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Pp
-The
.Nm
-driver also supports a point-to-point mode
-where stations can communicate amongst themselves without the
-aid of an access point.
-Note that there are two possible point-to-point modes.
-One mode, referred to as
-.Dq "ad-hoc demo mode" ,
-or
-.Dq "legacy Lucent ad-hoc mode" ,
-predates the IEEE 802.11 specification and so may not interoperate
-with cards from different vendors.
-The standardized point-to-point mode is called IBSS (or confusingly
-just ad-hoc mode), but is not supported by cards with very old
-firmware revisions.
-If your cards supports IBSS mode, it is recommended that you use it in
-preference to the
-.Dq "ad-hoc demo mode"
-in new installations.
-.Pp
-Cards supported by the driver based on the Intersil PRISM family of chips also
-have a host-based access point mode which allows the card to
-act as an access point (base station).
-Access points are different than operating in IBSS mode.
-They operate in BSS mode.
-They allow for easier roaming and bridge all Ethernet traffic such
-that machines connected via an access point appear to be on the local
-Ethernet segment.
-.Pp
+supports
+.Cm station ,
+.Cm adhoc ,
+.Cm adhoc-demo ,
+.Cm hostap ,
+and
+.Cm monitor
+mode operation.
+Only one
+virtual interface may be configured at a time.
For more information on configuring this device, see
.Xr ifconfig 8 .
.Pp
@@ -151,7 +107,6 @@ driver:
.Pp
.Bl -column -compact "Linksys Instant Wireless WPC11 2.5" "Spectrum24" "PCI or PCMCIA"
.Em "Card Chip Bus"
-3Com AirConnect 3CRWE737A Spectrum24 PCMCIA
3Com AirConnect 3CRWE777A Prism-II PCI
Accton airDirect WN3301 PCMCIA
ACTIONTEC HWC01170 Prism-2.5 PCMCIA
@@ -191,7 +146,6 @@ ELSA MC-11 PCMCIA
ELSA XI300 Prism-II PCMCIA
ELSA XI800 Prism-II CF
EMTAC A2424i Prism-II PCMCIA
-Ericsson Wireless LAN CARD C11 Spectrum24 PCMCIA
Farallon Skyline Prism-II PCMCIA
Gemtek WL-311 Prism-2.5 PCMCIA
Hawking Technology WE110P Prism-2.5 PCMCIA
@@ -199,7 +153,6 @@ Home Wireless Networks Prism-II PCMCIA
IBM High Rate Wireless Hermes PCMCIA
ICOM SL-1100 Prism-II PCMCIA
I-O DATA WN-B11/PCM Prism-II PCMCIA
-Intel PRO/Wireless 2011 Spectrum24 PCMCIA
Intersil Prism II Prism-II PCMCIA
Intersil Mini-PCI Prism-2.5 PCI
Intersil ISL37100P Prism-3 PCMCIA
@@ -240,8 +193,6 @@ SMC 2632 EZ Connect Prism-II PCMCIA
Socket Low Power WLAN-CF Prism-II CF
Sony PCWA-C100 Lucent PCMCIA
Sony PEGA-WL110 Prism-2.5 PCMCIA
-Symbol Spectrum24 Spectrum24 PCMCIA
-Symbol LA-4100 Spectrum24 CF
TDK LAK-CD011WL Prism-II PCMCIA
Toshiba Wireless LAN Card Prism-II PCMCIA
U.S.\& Robotics Wireless Card 2410 Prism-II PCMCIA
@@ -284,52 +235,53 @@ DLink DWL520
.Sh EXAMPLES
Join an existing BSS network (ie: connect to an access point):
.Pp
-.Dl "ifconfig wi0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev wi0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig wi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev wi0 inet 192.168.0.20 \e
+ netmask 0xffffff00 ssid my_net
.Pp
Join a specific BSS network with WEP encryption:
.Bd -literal -offset indent
-ifconfig wi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x8736639624
+ifconfig wlan create wlandev wi0 inet 192.168.0.20 \e
+ netmask 0xffffff00 ssid my_net \e
+ wepmode on wepkey 0x8736639624 weptxkey 1
.Ed
.Pp
Join a Lucent legacy demo ad-hoc network with network name
.Dq Li my_net :
.Bd -literal -offset indent
-ifconfig wi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- media DS/11Mbps mediaopt adhoc,link0
+ifconfig wlan create wlandev wi0 wlanmode ahdemo \e
+ inet 192.168.0.20 netmask 0xffffff00 ssid my_net
.Ed
.Pp
Join/create an IBSS network with network name
.Dq Li my_net :
.Bd -literal -offset indent
-ifconfig wi0 inet 192.168.0.22 netmask 0xffffff00 ssid my_net \e
- media DS/11Mbps mediaopt adhoc
+ifconfig wlan create wlandev wi0 wlanmode adhoc wi0 \e
+ inet 192.168.0.22 netmask 0xffffff00 ssid my_net
.Ed
.Pp
Create a host-based access point (Prism only):
.Bd -literal -offset indent
-ifconfig wi0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
- media DS/11Mbps mediaopt hostap
+ifconfig wlan create wlandev wi0 wlanmode hostap \e
+ inet 192.168.0.10 netmask 0xffffff00 ssid my_ap
.Ed
.Pp
-Create a host-based access point with WEP enabled (Prism only):
+Create a host-based access point with WEP enabled (Prism only)
+and plumb it into bridge to fxp0:
.Bd -literal -offset indent
-ifconfig wi0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
- wepmode on wepkey 0x1234567890 media DS/11Mbps \e
- mediaopt hostap
-.Ed
-.Pp
-Create a host-based wireless bridge to fxp0 (Prism only):
-.Bd -literal -offset indent
-ifconfig wi0 inet up ssid my_ap media DS/11Mbps mediaopt hostap
-sysctl net.inet.ip.check_interface=0
+ifconfig wlan0 create wlandev wi0 wlanmode hostap \e
+ inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
+ wepmode on wepkey 0x1234567890 weptxkey 1
ifconfig bridge0 create
-ifconfig bridge0 addm wi0 addm fxp0 up
+ifconfig bridge0 addm wlan0 addm fxp0 up
.Ed
.Pp
This will give you the same functionality as an access point.
@@ -347,16 +299,18 @@ The WaveLAN card failed to generate an interrupt to acknowledge a transmit
command.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr an 4 ,
-.Xr arp 4 ,
-.Xr ath 4 ,
-.Xr netintro 4 ,
+.Xr intro 4 ,
.Xr pccard 4 ,
.Xr pccbb 4 ,
.Xr pcic 4 ,
.Xr wlan 4 ,
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr wlan_xauth 4 ,
+.Xr hostapd 8 ,
.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Rs
.%T HCF Light programming specification
.%O http://www.wavelan.com
@@ -367,24 +321,21 @@ The
device driver first appeared in
.Fx 3.0 .
.Sh AUTHORS
-The
+The original
.Nm
driver was written by
.An Bill Paul Aq wpaul@ctr.columbia.edu .
This man page comes from
.Ox .
.Sh CAVEATS
-Different regulatory domains have different default channels for adhoc
-mode.
-See
-.Xr ifconfig 8
-for information on how to change the channel.
-The Intersil Prism family of chips' host-based access point mode has
-bugs for station firmware versions prior to 0.8.3.
-The driver currently precludes hostap functionality with older station
-firmware.
-The best version of the station firmware for the Prism family of chips
-seems to be 1.4.9.
+The driver will reject devices with old firmware to
+avoid dealing with numerous defects.
+Unfortunately the driver does not support downloading new firmware
+to the card so if new firmware is needed users will have to boot
+a different system to accomplish this.
+.Pp
+Intersil Prism cards must have firmware versions 0.8.0 or later and
+version 1.7.0 or later are required to support functionality such as WPA.
Some users of Prism-II and 2.5 based cards report that station firmware
version 1.3.4 works better for them in hostap than 1.4.9.
Older versions of the Prism station firmware have a number of issues
@@ -394,15 +345,8 @@ later.
The IBSS/adhoc mode appears to have problems for some people with
older versions of station firmware.
.Pp
-Lucent cards prior to firmware version 6.0.6 do not support IBSS
-mode.
-These cards support only the pre-802.11 mode known as
-.Dq "demo ad-hoc mode"
-which does not interoperate with stations in IBSS mode.
-.Pp
-Prism cards prior to version 0.8.0 do not support IBSS mode.
-.Pp
-Symbol cards prior to version 2.50.00 do not support IBSS mode.
+Lucent cards prior to firmware version 6.0.6 do not implement IBSS
+mode and are not supported.
.Sh BUGS
Not all the new messages are documented here, and many of them are
indications of transient errors that are not indications of serious
diff --git a/share/man/man4/wlan.4 b/share/man/man4/wlan.4
index d636395..6bfad82 100644
--- a/share/man/man4/wlan.4
+++ b/share/man/man4/wlan.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 6, 2007
+.Dd April 12, 2008
.Dt WLAN 4
.Os
.Sh NAME
@@ -41,50 +41,96 @@ Where a device does not directly support 802.11 functionality
this layer fills in.
The
.Nm
-is required for the
-.Xr an 4 ,
-.Xr ath 4 ,
-.Xr ipw 4 ,
-.Xr iwi 4 ,
-.Xr ral 4 ,
-.Xr rum 4 ,
-.Xr ural 4 ,
-.Xr wi 4 ,
-.Xr wpi 4 ,
-and
-.Xr zyd 4
-drivers, with other drivers to follow.
+module is required by all native 802.11 drivers as well as the
+.Xr ndis 4
+support.
.Pp
-The
.Nm
-module supports multi-mode devices capable of
+supports multi-mode devices capable of
operating in both 2.4GHz and 5GHz bands and supports numerous
-802.11 protocols: 802.11a, 802.11b, and 802.11g.
+802.11 standards: 802.11a, 802.11b, 802.11g, and 802.11n.
The WPA, 802.11i, and 802.1x security protocols are supported
through a combination of in-kernel code and user-mode applications.
-The WME and WMM multi-media protocols are supported entirely within
+The WME/WMM multi-media protocols are supported entirely within
the
.Nm
module but require a suitably capable hardware device.
+Likewise the 802.11h specification is supported only by suitably
+capable devices.
.Pp
-The
+Drivers provide 802.11 functionality through
+.Nm
+interfaces that are created at runtime using interface cloning.
+This is done with the
+.Xr ifconfig 8
+.Cm create
+command or using the
+.Va vaps_IFX
+variable in
+.Xr rc.conf 5 .
+Some drivers support the creation of multiple
+.Nm
+interfaces that share the same underlying device;
+this is the way by which ``multi-bss support'' is provided but it
+can also be used to create WDS links and other interesting applications.
+.Pp
+There are several types of
.Nm
-module defines several mechanisms by which plugin modules may
-be used to extend functionality.
+interfaces that may be created:
+.Bl -tag -width monitor
+.It Cm sta
+A client station in an infrastructure bss
+(i.e. one that associates to an access point).
+.It Cm hostap
+An access point in an infrastructure bss.
+.It Cm adhoc
+A station in an IBSS network.
+.It Cm ahdemo
+A station operating in ``adhoc demo mode''.
+This is essentially an IBSS station that does not use management
+frames (e.g. no beacons are transmitted).
+An
+.Cm ahdemo
+interface is especially useful for applications that want to transmit
+and receive raw 802.11 packets.
+.It Cm monitor
+An interface used exclusively for capturing 802.11 frames.
+In particular this specified to have read-only properties
+which enables it to be operated on frequencies where one
+would otherwise not be allowed.
+.It Cm wds
+A station that passes 4-address 802.11 traffic for the purpose
+of tunneling traffic over a wireless link.
+Typically this station would share the same MAC address as a
+.Cm hostap
+interface.
+It may be possible to create
+.Cm wds
+interfaces without a companion
+.Cm hostap
+interface but that is not guaranteed; one may need to create a
+.Cm hostap
+interface that does not send beacon frames before
+.Cm wds
+interfaces may be created.
+.El
+.Pp
+More types are planned to support
+802.11s mesh nodes (station and ap).
+Note that an interface's type cannot be changed once it is created.
+.Pp
+.Nm
+defines several mechanisms by which plugin modules may
+be used to extend its' functionality.
Cryptographic support such as WEP, TKIP, and AES-CCMP are implemented
-as modules that are loaded on demand (if not statically configured
-into a system).
+as standalone modules (if not statically configured into a system)
+that register with
+.Nm .
Similarly there is an authenticator framework for defining 802.11
authentication services and a framework for integrating access
control mechanisms specific to the 802.11 protocol.
.Sh DEBUGGING
-If the associated interface is marked for debugging with, for example,
-.Pp
-.Dl "ifconfig wi0 debug"
-.Pp
-then messages describing the operation of the 802.11 protocol will
-be sent to the console.
-Complete debugging controls are available using:
+Debugging controls are available using:
.Pp
.Dl "sysctl net.wlan.X.debug=mask"
.Pp
@@ -101,19 +147,15 @@ For example,
enables debugging messages related to scanning for an access point,
adhoc neighbor, or an unoccupied channel when operation as an access point.
The
-.Xr 80211debug
+.Xr wlandebug 8
tool provides a more user-friendly mechanism for doing the same thing.
+Note that
.Pp
-Many drivers will also display the contents of each 802.11 frame
-sent and received when the interface is marked with
-both debugging and
-.Cm link2 ;
-e.g.,
+.Dl "sysctl net.wlan.debug=mask"
.Pp
-.Dl "ifconfig wi0 debug link2"
-.Pp
-Beware however that some management frames may be processed entirely within
-the device and not be received by the host.
+defines the initial value of the debugging flags for each cloned
+.Nm
+interface; this is useful to enable debug messages during interface creation.
.Sh COMPATIBILITY
The module name of
.Nm
@@ -122,8 +164,11 @@ was used to be compatible with
.Sh SEE ALSO
.Xr an 4 ,
.Xr ath 4 ,
+.Xr bwi 4 ,
.Xr ipw 4 ,
.Xr iwi 4 ,
+.Xr iwn 4 ,
+.Xr mwl 4 ,
.Xr netintro 4 ,
.Xr ral 4 ,
.Xr rum 4 ,
@@ -137,7 +182,7 @@ was used to be compatible with
.Xr wpi 4 ,
.Xr zyd 4
.Sh STANDARDS
-More information can be found in the IEEE 802.11 Standard.
+More information can be found in the IEEE 802.11 Standards.
.Sh HISTORY
The
.Nm
@@ -152,7 +197,8 @@ software from which this work began.
brought the code into
.Fx
and then rewrote it to support multi-mode devices,
-802.11g, WPA/802.11i, WME, and add the extensible frameworks
+802.11g, 802.11n, WPA/802.11i, WME, multi-bss, and
+add the extensible frameworks
for cryptographic, authentication, and access control plugins.
This manual page was written by
.An Tom Rhodes Aq trhodes@FreeBSD.org .
diff --git a/share/man/man4/wlan_acl.4 b/share/man/man4/wlan_acl.4
index 1a95441..9a05aad 100644
--- a/share/man/man4/wlan_acl.4
+++ b/share/man/man4/wlan_acl.4
@@ -40,12 +40,11 @@ module implements a MAC-based access control plugin for use
with 802.11 devices operating as an access point.
The
.Nm
-module is an 802.11 access control plugin module for use with the
-.Xr wlan 4
-module.
-This module is automatically loaded if an application configures
-an access control policy for an 802.11 device operating as an access
-point.
+must be loaded for
+.Xr ifconfig 8
+to handle the
+.Cm mac:*
+requests.
.Sh SEE ALSO
.Xr wlan 4 ,
.Xr ifconfig 8
diff --git a/share/man/man4/wlan_amrr.4 b/share/man/man4/wlan_amrr.4
index 2883b7a..f810a9c 100644
--- a/share/man/man4/wlan_amrr.4
+++ b/share/man/man4/wlan_amrr.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 10, 2007
+.Dd April 13, 2008
.Dt WLAN_AMRR 4
.Os
.Sh NAME
@@ -37,15 +37,15 @@
The
.Nm
module implements the Adaptive Multi-Rate Retry tx rate control
-algorithm for use with 802.11 devices.
-The
-.Nm
-module is an 802.11 tx rate control module for use by the
-device-independent implementation.
+algorithm for use by 802.11 device drivers.
.Sh SEE ALSO
+.Xr bwi 4 ,
+.Xr iwn 4 ,
+.Xr ral 4 ,
.Xr rum 4 ,
.Xr ural 4 ,
.Xr wlan 4 ,
+.Xr wpi 4 ,
.Xr zyd 4
.Sh STANDARDS
More information can be found in the paper describing the
diff --git a/share/man/man4/wlan_xauth.4 b/share/man/man4/wlan_xauth.4
index cf806a9..b1fdfc5 100644
--- a/share/man/man4/wlan_xauth.4
+++ b/share/man/man4/wlan_xauth.4
@@ -36,7 +36,9 @@
.Sh DESCRIPTION
The
.Nm
-module implements an authenticator plugin that is intended
+module is a
+.Xr wlan 4
+authenticator plugin
for use with user-mode authentication implementations such
as
.Nm hostapd .
@@ -44,13 +46,10 @@ It hooks into the 802.11 layer and does nothing.
As a result, 802.11 stations that associate are not authorized to
send or receive frames until they are authorized by an external agent;
typically using a protocol such as WPA, 802.1x, or 802.11i.
-The
-.Nm
-module is an 802.11 authenticator plugin module for use by the
-.Xr wlan 4
-module.
-This module is automatically loaded if an application configures
-an 802.11 device for operation as an AP with WPA or 802.1x authentication.
+.Pp
+This module is automatically loaded by the rc script that normally
+starts
+.Xr hostapd 8 .
.Sh SEE ALSO
.Xr wlan 4
.Sh STANDARDS
diff --git a/share/man/man4/wpi.4 b/share/man/man4/wpi.4
index bc6e796..c573856 100644
--- a/share/man/man4/wpi.4
+++ b/share/man/man4/wpi.4
@@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 6, 2007
+.Dd Apri 13, 2008
.Os
.Dt WPI 4
.Sh NAME
@@ -38,6 +38,7 @@ place the following lines in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device wpi"
+.Cd "device wpifw"
.Cd "device pci"
.Cd "device wlan"
.Cd "device wlan_amrr"
@@ -45,40 +46,41 @@ kernel configuration file:
.Ed
.Pp
Alternatively, to load the driver as a
-module at boot time, place the following lines in
+module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_wpi_load="YES"
-wlan_load="YES"
-wlan_amrr_load="YES"
-firmware_load="YES"
.Ed
.Pp
In both cases, place the following line in
.Xr loader.conf 5
-to load the firmware modules:
+to acknowledge the firmware license (see below):
.Bd -literal -offset indent
-wpifw_load="YES"
+legal.intel_wpi.license_ack=1
.Ed
.Sh DESCRIPTION
The
.Nm
-driver is an experimental driver providing support for the
+driver provides support for the
.Tn Intel
3945ABG Wireless network adapter
-.Pp
-By default, the
.Nm
-driver configures the adapter for BSS operation (aka infrastructure mode).
-This mode requires the use of an access point.
-.Pp
+supports
+.Cm station ,
+.Cm adhoc ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
For more information on configuring this device, see
.Xr ifconfig 8 .
.Pp
-This driver requires firmware to be loaded before it will work.
-For the loaded firmware to work the license at
-.Pa /usr/share/doc/legal/intel_wpi/LICENSE
-must be agreed to and the following line be added to
+This driver requires the firmware built with the
+.Nm wpifw
+module to work.
+For the loaded firmware to be enabled for use the license at
+.Pa /usr/share/doc/legal/intel_iwi/LICENSE
+must be agreed by adding the following line to
.Xr loader.conf 5 :
.Pp
.Dl "legal.intel_wpi.license_ack=1"
@@ -91,35 +93,38 @@ firmware license
.Sh EXAMPLES
Join an existing BSS network (i.e., connect to an access point):
.Pp
-.Dl "ifconfig wpi0 inet 192.168.0.20 netmask 0xffffff00"
+.Bd -literal -offset indent
+ifconfig wlan create wlandev wpi0 inet 192.168.0.20 \e
+ netmask 0xffffff00
+.Ed
.Pp
Join a specific BSS network with network name
.Dq Li my_net :
.Pp
-.Dl "ifconfig wpi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Dl "ifconfig wlan create wlandev wpi0 ssid my_net up"
.Pp
-Join a specific BSS network with 40-bit WEP encryption:
+Join a specific BSS network with 64-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig wpi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
- wepmode on wepkey 0x1234567890 weptxkey 1
+ifconfig wlan create wlandev wpi0 ssid my_net \e
+ wepmode on wepkey 0x1234567890 weptxkey 1 up
.Ed
.Pp
-Join a specific BSS network with 104-bit WEP encryption:
+Join a specific BSS network with 128-bit WEP encryption:
.Bd -literal -offset indent
-ifconfig wpi0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+ifconfig wlan create wlandev wpi0 wlanmode adhoc ssid my_net \e
wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
.Ed
.Sh DIAGNOSTICS
.Bl -diag
+.It "wpi%d: could not load firmware image '%s'"
+The driver failed to load the firmware image using the
.Xr firmware 9
subsystem.
-Verify that the necessary firmware modules are loaded and the
-license agreement
+Verify the
+.Xr wpifw
+firmware module is installed and the license agreement
.Xr loader 8
tunable has been set.
-.It "wpi%d: could not load firmware"
-An attempt to upload the firmware image to the onboard microcontroller failed.
-This should not happen.
.It "wpi%d: fatal firmware error"
An unknown error has occurred in the uploaded firmware, you may have to
unload/reload the driver to continue.
@@ -129,23 +134,27 @@ Data transmission is not possible in this state.
.Sh BUGS
Not all the error messages are documented here.
.Pp
-Automatic recovery of firmware failures is not currently supported, nor is
-detection of toggling the radio switch on.
-.Pp
Background scanning is not currently supported.
.El
.Sh SEE ALSO
-.Xr altq 4 ,
-.Xr ipw 4 ,
-.Xr iwi 4 ,
+.Xr wpifw 4 ,
.Xr pci 4 ,
.Xr wlan 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh AUTHORS
.An -nosplit
-The
+The original
.Nm
-driver was written by
-.An Damien Bergamini Aq damien.bergamini@free.fr
-and
-.An Benjamin Close Aq benjsc@FreeBSD.org .
+driver was written for
+.Ox
+by
+.An Damien Bergamini Aq damien.bergamini@free.fr .
+.An Benjamin Close Aq benjsc@FreeBSD.org
+ported
+.Nm
+to
+.Fx .
diff --git a/share/man/man4/zyd.4 b/share/man/man4/zyd.4
index 6599a53..23eb9c6 100644
--- a/share/man/man4/zyd.4
+++ b/share/man/man4/zyd.4
@@ -32,7 +32,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
.\" THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd September 6, 2007
+.Dd Apri 13, 2008
.Dt ZYD 4
.Os
.Sh NAME
@@ -64,34 +64,16 @@ The
driver provides support for wireless network adapters based around
the ZyDAS ZD1211 and ZD1211B USB chips.
.Pp
-These are the modes the
.Nm
-driver can operate in:
-.Bl -tag -width "IBSS-masterXX"
-.It BSS mode
-Also known as
-.Em infrastructure
-mode, this is used when associating with an access point, through
-which all traffic passes.
-This mode is the default.
-.It monitor mode
-In this mode the driver is able to receive packets without
-associating with an access point.
-This disables the internal receive filter and enables the card to
-capture packets from networks which it wouldn't normally have access to,
-or to scan for access points.
-.El
-.Pp
-.Nm
-supports software WEP.
-Wired Equivalent Privacy (WEP) is the de facto encryption standard
-for wireless networks.
-It can be typically configured in one of three modes:
-no encryption; 40-bit encryption; or 104-bit encryption.
-Unfortunately, due to serious weaknesses in WEP protocol
-it is strongly recommended that it not be used as the
-sole mechanism to secure wireless communication.
-WEP is not enabled by default.
+supports
+.Cm station ,
+.Cm adhoc ,
+and
+.Cm monitor
+mode operation.
+Only one virtual interface may be configured at any time.
+For more information on configuring this device, see
+.Xr ifconfig 8 .
.Sh HARDWARE
The following devices are known to be supported by the
.Nm
@@ -143,24 +125,20 @@ driver:
.El
.Sh EXAMPLES
The following
-examples configures zyd0 to join whatever network is available on boot,
-using WEP key
+examples configures zyd0 to join any BSS network using WEP key
.Dq 0x1deadbeef1 ,
channel 11:
.Bd -literal -offset indent
-inet 192.168.1.1 netmask 255.255.255.0 wepkey 0x1deadbeef1 channel 11
-.Ed
-.Pp
-Configure zyd0 for WEP, using hex key
-.Dq 0x1deadbeef1 :
-.Bd -literal -offset indent
-# ifconfig zyd0 wepkey 0x1deadbeef1
+ifconfig wlan create wlandev zyd0 channel 11 \e
+ wepmode on wepkey 0x1deadbeef1 weptxkey 1 \e
+ inet 192.168.1.1 netmask 255.255.255.0
.Ed
.Pp
Join an existing BSS network,
.Dq my_net :
.Bd -literal -offset indent
-# ifconfig zyd0 192.168.0.2 netmask 0xffffff00 ssid my_net
+ifconfig wlan create wlandev zyd0 192.168.0.2 \e
+ netmask 0xffffff00 ssid my_net
.Ed
.Sh DIAGNOSTICS
.Bl -diag
@@ -181,16 +159,19 @@ The driver will reset the hardware.
This should not happen.
.El
.Sh SEE ALSO
-.Xr arp 4 ,
.Xr intro 4 ,
.Xr netintro 4 ,
.Xr usb 4 ,
.Xr wlan 4 ,
.Xr wlan_amrr 4 ,
-.Xr ifconfig 8
+.Xr wlan_ccmp 4 ,
+.Xr wlan_tkip 4 ,
+.Xr wlan_wep 4 ,
+.Xr ifconfig 8 ,
+.Xr wpa_supplicant 8 .
.Sh AUTHORS
.An -nosplit
-The
+The original
.Nm
driver was written by
.An Florian Stoehr Aq ich@florian-stoehr.de ,
diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile
index 3e1d832..12ee000 100644
--- a/share/man/man5/Makefile
+++ b/share/man/man5/Makefile
@@ -55,6 +55,7 @@ MAN= acct.5 \
protocols.5 \
quota.user.5 \
rc.conf.5 \
+ regdomain.5 \
reiserfs.5 \
remote.5 \
resolver.5 \
diff --git a/share/man/man5/regdomain.5 b/share/man/man5/regdomain.5
new file mode 100644
index 0000000..f46a4af
--- /dev/null
+++ b/share/man/man5/regdomain.5
@@ -0,0 +1,48 @@
+.\" Copyright (c) 2008 Sam Leffler, Errno Consulting
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.Dd Apri 13, 2008
+.Dt REGDOMAIN 5
+.Os
+.Sh NAME
+.Nm regdomain.xml
+.Nd "802.11 wireless regulatory definitions"
+.Sh DESCRIPTION
+The
+.Nm
+file describes regulations for the operation of IEEE 802.11 wireless radios.
+.Pp
+This information is used by the
+.Xr ifconfig 8
+program to construct regulatory state for download to the system.
+This file should be changed only to reflect changes in regulations.
+.Sh FILES
+.Bl -tag -width /etc/regdomain.xml -compact
+.It Pa /etc/regdomain.xml
+XML database of 802.11 regulatory constraints
+.El
+.Sh SEE ALSO
+.Xr wlan 4
+.Xr ifconfig 8 ,
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 8a3566a..4d0a1d7 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -239,8 +239,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/arm/conf/AVILA b/sys/arm/conf/AVILA
index 53b2681..c436eab 100644
--- a/sys/arm/conf/AVILA
+++ b/sys/arm/conf/AVILA
@@ -130,8 +130,6 @@ device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_sta
-device wlan_scan_ap
device wlan_xauth
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/arm/conf/HL200 b/sys/arm/conf/HL200
index fc0529c..225a12b 100644
--- a/sys/arm/conf/HL200
+++ b/sys/arm/conf/HL200
@@ -147,5 +147,3 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
diff --git a/sys/arm/conf/KB920X b/sys/arm/conf/KB920X
index 60e3da1..54f631d 100644
--- a/sys/arm/conf/KB920X
+++ b/sys/arm/conf/KB920X
@@ -135,5 +135,3 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 34564a5..5aef1fd 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -768,8 +768,6 @@ device wlan_tkip #802.11 TKIP support
device wlan_xauth #802.11 external authenticator support
device wlan_acl #802.11 MAC ACL support
device wlan_amrr #AMRR transmit rate control algorithm
-device wlan_scan_ap #802.11 AP mode scanning
-device wlan_scan_sta #802.11 STA mode scanning
device token #Generic TokenRing
device fddi #Generic FDDI
device arcnet #Generic Arcnet
diff --git a/sys/conf/files b/sys/conf/files
index 125d2ba..fd41e73 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -770,6 +770,9 @@ dev/le/lance.c optional le
dev/led/led.c standard
dev/lge/if_lge.c optional lge
dev/lmc/if_lmc.c optional lmc
+dev/malo/if_malo.c optional malo
+dev/malo/if_malohal.c optional malo
+dev/malo/if_malo_pci.c optional malo pci
dev/mc146818/mc146818.c optional mc146818
dev/mca/mca_bus.c optional mca
dev/mcd/mcd.c optional mcd isa nowerror
@@ -918,7 +921,6 @@ dev/puc/pucdata.c optional puc pci
dev/quicc/quicc_core.c optional quicc
dev/ral/rt2560.c optional ral
dev/ral/rt2661.c optional ral
-dev/ral/if_ralrate.c optional ral
dev/ral/if_ral_pci.c optional ral pci
dev/random/harvest.c standard
dev/random/hash.c optional random
@@ -1677,24 +1679,32 @@ net/zlib.c optional crypto | geom_uzip | ipsec | \
mxge | ppp_deflate | netgraph_deflate
net80211/ieee80211.c optional wlan
net80211/ieee80211_acl.c optional wlan_acl
+net80211/ieee80211_adhoc.c optional wlan
net80211/ieee80211_amrr.c optional wlan_amrr
net80211/ieee80211_crypto.c optional wlan
net80211/ieee80211_crypto_ccmp.c optional wlan_ccmp
net80211/ieee80211_crypto_none.c optional wlan
net80211/ieee80211_crypto_tkip.c optional wlan_tkip
net80211/ieee80211_crypto_wep.c optional wlan_wep
+net80211/ieee80211_ddb.c optional wlan ddb
+net80211/ieee80211_dfs.c optional wlan
net80211/ieee80211_freebsd.c optional wlan
+net80211/ieee80211_hostap.c optional wlan
net80211/ieee80211_ht.c optional wlan
net80211/ieee80211_input.c optional wlan
net80211/ieee80211_ioctl.c optional wlan
+net80211/ieee80211_monitor.c optional wlan
net80211/ieee80211_node.c optional wlan
net80211/ieee80211_output.c optional wlan
+net80211/ieee80211_phy.c optional wlan
net80211/ieee80211_power.c optional wlan
net80211/ieee80211_proto.c optional wlan
net80211/ieee80211_regdomain.c optional wlan
+net80211/ieee80211_rssadapt.c optional wlan_rssadapt
net80211/ieee80211_scan.c optional wlan
-net80211/ieee80211_scan_ap.c optional wlan_scan_ap
-net80211/ieee80211_scan_sta.c optional wlan_scan_sta
+net80211/ieee80211_scan_sta.c optional wlan
+net80211/ieee80211_sta.c optional wlan
+net80211/ieee80211_wds.c optional wlan
net80211/ieee80211_xauth.c optional wlan_xauth
netatalk/aarp.c optional netatalk
netatalk/at_control.c optional netatalk
diff --git a/sys/conf/options b/sys/conf/options
index 09acf41..eec0e9d 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -734,6 +734,11 @@ ATH_RXBUF opt_ath.h
ATH_DIAGAPI opt_ath.h
ATH_TX99_DIAG opt_ath.h
+# options for the Marvell 8335 wireless driver
+MALO_DEBUG opt_malo.h
+MALO_TXBUF opt_malo.h
+MALO_RXBUF opt_malo.h
+
# dcons options
DCONS_BUF_SIZE opt_dcons.h
DCONS_POLL_HZ opt_dcons.h
@@ -763,5 +768,10 @@ XFS
# Interrupt filtering
INTR_FILTER
+# 802.11 support layer
+IEEE80211_DEBUG opt_wlan.h
+IEEE80211_DEBUG_REFCNT opt_wlan.h
+IEEE80211_AMPDU_AGE opt_wlan.h
+
#Disable code to dispatch tcp offloading
TCP_OFFLOAD_DISABLE opt_inet.h
diff --git a/sys/contrib/dev/ral/LICENSE b/sys/contrib/dev/ral/LICENSE
new file mode 100644
index 0000000..5acf6db
--- /dev/null
+++ b/sys/contrib/dev/ral/LICENSE
@@ -0,0 +1,16 @@
+$FreeBSD$
+
+Copyright (c) 2005-2008, Ralink Technology Corp.
+ Paul Lin <paul_lin@ralinktech.com.tw>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/sys/contrib/dev/ral/Makefile b/sys/contrib/dev/ral/Makefile
new file mode 100644
index 0000000..8560f42
--- /dev/null
+++ b/sys/contrib/dev/ral/Makefile
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+FILES= rt2561s.fw.uu rt2561.fw.uu rt2661.fw.uu rt2860.fw.uu
+
+rt2561s.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2561s_ucode, sizeof(rt2561s_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2561s.fw) > ${.TARGET}
+
+rt2561.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2561_ucode, sizeof(rt2561_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2561.fw) > ${.TARGET}
+
+rt2661.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2661_ucode, sizeof(rt2661_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2661.fw) > ${.TARGET}
+
+rt2860.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2860_ucode, sizeof(rt2860_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2860.fw) > ${.TARGET}
+
+clean:
+ rm -f build build.c ${FILES}
+
+.include <bsd.prog.mk>
diff --git a/sys/contrib/dev/ral/rt2561.fw.uu b/sys/contrib/dev/ral/rt2561.fw.uu
new file mode 100644
index 0000000..9e511d4
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2561.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2561.fw
+M`AP2`A/+PHPB(@`"%@_"K\*-=8R4=8J3TJ\B`AC:$AOH0`,"`AZ0(0+@]2V0
+M``/@$@@E`+```,X!`%X0`&\1`/(@`4TA`7`B`80P`8\Q`=50`9]1`?)2`@9@
+M```"%)``"N`@Y0,P!P/2""(2%Z4BD"$`X/41Y1'$,U3@)"'U@N0T(?6#X$2`
+M\.41Q#-4X"0L]8+D-"'U@^41\,0S5.`D+?6"Y#0A]8/E+?#DD"$#\"(2$3&0
+M(0#@]3%@!1(;BH`#$AL]Y)`A`_"O+1(<8B)U,?^0`0#@5/?PD`$!X%3^\%0^
+M\.20``OP\)`A`_"O+1(<8B)^*W^`?0,2!`Z0-,W@(./YD"$4$@@!D#3`$@@-
+MD"$8$@@!D#3($@@-D"$<$@@!D#3$$@@-D#3,=`'PH^!$!/"0`0'@1`'P1$#P
+MD``+X$00\.20(0/PKRT2'&(BD`$`X%3W\)`!`>!4_O!4O_"0``O@5._PY)`A
+M`_"O+1(<8B)^*W^`?0,2!`[DD"$#\*\M$AQB(M(%A2TCY)`A`_`B$AITP@#D
+MD"$#\*\M$AQB(H4M)9``"^!4^__PY)``!_"0``IT!/#DD``(\)`A`."0``GP
+MD``'=''P[T0$D``+\.20(0/P(I`A`.#_5!_U,*/@]2>/)A((D.20(0/PKRT2
+M'&(BD"$`X/4L$A@3Y)`A`_"O+1(<8B(2&5/DD"$#\*\M$AQB(N20(0/PKRT2
+M'&(BCA6/%LKMRLGKR3`*!']*@`)_0LOOR^K#E`10`H`!PT`$RT0@RX46@H45
+M@^OPH^3PA1:"A16#HZ/E&O#E&846@H45@Z.CH_#E%B0$]8+D-17U@W0/\.46
+M)`7U@N0U%?6#Y/#E%B0&]8+D-17U@^3PY18D!_6"Y#45]8-T$/#JD!J<D_OJ
+M9`%@".ID`F`#N@,$RT0(R^46)`CU@N0U%?6#Z_#E%B05]8+D-17U@W3_\.46
+M)!;U@N0U%?6#Z?#E%B0)]8+D-17U@W0$\"4:]1KD-1GU&>K#E`1``P(#UNI@
+M`[H!'^HD`?WD,_SE&JX9>`/#,\XSSMCY_Q('EHX9CQH"`[;J)/_]Y#3__'X`
+M?PL2!X3,[LS-[\WE&L3X5`_(:/_E&<14\$C^$@>6C!N-'.HD__WD-/_\?@!_
+M"Q('A,SNS,WOS>4:Q/A4#\AH_^49Q%3P2/X2!Y:.&8\:Y1Q%&V`(!1KE&G`"
+M!1GJ)/_]Y#3__'X`?P,2!X33Y1R?Y1N>4!CE'$4;8!*Z`P_E%B0)]8+D-17U
+M@^!$@/#E%B0*]8+D-17U@^4:\.49_^46)`OU@N0U%?6#[_"`+>4:5#__Y18D
+M"O6"Y#45]8/O\.4:KAEX!L[#$\X3V/G_Y18D"_6"Y#45]8/O\(46@H45@^!$
+M`?`BCA*/$XT4Y12BX9()Y30D&?6"Y#4S]8/@_>4T)!KU@N0U,_6#X/NB"9(*
+M=1D`=1H:$@(?,`D$?\B``G_HY1,D&/6"Y#42]8/O\.4Q8`1_`H`"?P'E$R09
+M]8+D-1+U@^_PY30D&?6"Y#4S]8/@_WT:?``2#F3E$R0:]8+D-1+U@^_PY1,D
+M&_6"Y#42]8/N\.4Q8&#E$R0<_^0U$O[E-"02_>0U,_QU&Q%[!A(4J^43)"+_
+MY#42_GPP?1!U&Q%[!A(4J^43)"C_Y#42_GPP?0AU&Q%[!A(4J^4T)!CU@N0U
+M,_6#X/_E$R0M]8+D-1+U@^_P@#_E$R0<_^0U$OY\,'T0=1L1>P82%*OE$R0B
+M_^0U$OY\,'T(=1L1>P82%*OE$R0H_^0U$OY\,'T0=1L1>P82%*OE$R0N]8+D
+M-1+U@^3PY1,D+_6"Y#42]8/D\.4T)!'U@N0U,_6#X/_#$__E$R0P]8+D-1+U
+M@^_P,`E!Y1,D,/6"Y#42]8/@_^4Q8`1^`(`"?A#O3O#E,6`&?@!_`(`/Y10P
+MX`9^`'__@`1^`'\`Y1,D,?6"Y#42]8/O\"+E$R0P]8+D-1+U@^!$0/#E%##@
+M#^4T)!#U@N0U,_6#X/^``G\`Y1,D,?6"Y#42]8/O\"+E-"01]8+D-3/U@^`P
+MYSOE-"0<]8+D-3/U@^!E*W`#=2O_Y30D'?6"Y#4S]8/@_Q(<8GXB?Q`2&'R.
+M,X\TD"(NX/ZCX(XS]33#(M(*Y30D&_6"Y#4S]8/@<#J%-(*%,X/`@\""X/ZC
+MX/^%-(*%,X.CH^#\H^#]P^^=_^Z<_M""T(/PH^_PTY0`[F2`E(!0`P(')X#&
+MA32"A3.#X/ZCX,/N9("4@%`#`@<G$AQ!A32"A3.#X/RCX/W#G^YD@/CL9("8
+M0""%-(*%,X/`@\""HZ/@_J/@_^V?_^R>T(+0@_"C[_#""H4T@H4S@^#^H^#_
+MY30D$/6"Y#4S]8/@_</OG?WNE`#\$A9:4"R%-(*%,X/`@\""X/ZCX/^%-(*%
+M,X.CH^#\H^#]P^^=_^Z<T(+0@_"C[_#""B`*`P(&-WXB?Q`2&'R.,X\TCX*.
+M@^#^H^#3E`#N9("4@$`-?B)_$*TTK#,2%0Z`&A(;JX4T@H4S@^Z/\!('ZWXB
+M?S"M-*PS$A4.D"(NX/ZCX/]E-'`#[F4S<`+3(HXSCS3#(N^-\*2H\,^,\*0H
+MSHWPI"[^(KP`"[X`*>^-\(3_K?`BY,SX=?`([R__[C/^[#/\[IWLF$`%_.Z=
+M_@_5\.GDSOTB[?CU\.Z$(-(<_JWP=?`([R__[3/]0`>84`;5\/(BPYC]#]7P
+MZB+%\/BCX"CPQ?#XY8(5@G`"%8/@./`BX/RCX/VCX/ZCX/\B[/"C[?"C[O"C
+M[_`BI"6"]8+E\#6#]8,BT(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3
+M:&#OHZ.C@-^*@XF"Y'/D_Y`PC.3P[Y`;49-$@)`PC?"C=`'PH^3PD#",X/YT
+M-B_XQN[&H^#^[Y`;49-$@&Y@`1\/[\.4"4#((@````#E,!(()0BQ``D?`0F'
+M`@H;`PIO!`JV!0LI!@N8!P``"]#"`1(`!I`P.N#U$N4F(.4(D#28X%3^\"*0
+M-)C@1`'PY28PY@_E)S#F!5,2_8`20Q("@`WE)S#F!4,2`H`#4Q+]Y28PYP_E
+M)S#G!5,2]X`20Q((@`WE)S#G!4,2"(`#4Q+W0Q(!0Q($D#`ZY1+P(L(!$@`&
+MD#`ZX/42Y28@Y0B0-)C@5/[P(I`TF.!$`?#E)E3`8!SE)S#F!5,2_8`#0Q("
+MY2<PYP53$O>`'T,2"(`:Y2<PY@5#$@*``U,2_>4G,.<%0Q((@`-3$O=#$@%#
+M$@20,#KE$O`BP@$2``:0,#K@]1)#$@%#$@3E)C#E7)`TF.!$`?#E)E3`8!SE
+M)S#F!5,2_8`#0Q("Y2<PYP53$O>`,$,2"(`KY2<PY@5#$@*``U,2_>4G,.<%
+M0Q((@`-3$O?E)_14'_^0,#3@5.!/\.3U+)`P.N42\(`5D#28X%3^\.4G]%0?
+M_Y`P-.!4X$_PD#`UX/424Q+@Y1+P(L(!$@`&D#`ZX/42Y28PY3R0-)C@1`'P
+MY2<PY@53$OV``T,2`N4G,.<%4Q+W@`-#$@CE)E3`8`A#$@%#$@2`!E,2_D,2
+M!)`P.N42\"*0-)C@5/[P(L(!$@`&D#`ZX/42Y2<PY@5#$@*``U,2_>4G,.<%
+M0Q((@`-3$O?E)E3`8`A3$OY3$ON`!D,2`4,2!)`TF.!$`?"0,#KE$O`B(`(3
+M$AP>KRE^`!(<=*\U?@`2''O2`I`P.N#U$N4F(.4-P@$2``:0-)C@5/[P(I`T
+MF.!$`?#E)E3`8"S"`1(`!N4G,.8%4Q+]@`-#$@+E)S#G!5,2]X`#0Q((0Q(!
+M0Q($D#`ZY1+P(C`!`P(+T!(5P-(!(L(!$@`&Y28@Y0F0-)C@5/[P@%60-)C@
+M1`'PY28PY@_E)S#F!5,2_8`20Q("@`WE)S#F!4,2`H`#4Q+]Y28PYP_E)S#G
+M!5,2]X`20Q((@`WE)S#G!4,2"(`#4Q+W0Q(!4Q+[D#`ZY1+PD#`ZX/42(N4F
+M,.4L(`,AT@,2'!YU-09U*0FO*7X`$AQTD#`ZX/424Q+^0Q($Y1+PD#28X$0!
+M\"*0-)C@5/[P(N4Q9`%P01(:U$`#`@U/$AME4"!^*W^`?0,2!`Y_`1(9>$`)
+MT@D2#^[D]2\B$@U0=2\!(G\!$AEX4`1U+P(BT@D2#^[D]2\B$AH=4%$2&\N0
+M,/3@]2I^,'_LH^#]Y/L2&2OD__X2'#:0``IT`O"0``O@1`+_\/V0`05T(/"0
+M`0;@1"#P[52_D``+\)`TS.!$`?"CX$0!\*/@1`'PT@02&CI00Q(:5WXP?^!\
+M,'WL=1L1>P82%*N0,/7@=?`@I/^N\!(<-I``"^!4_?_P_>20``3PD`$&X%3?
+M\)``"G1`\$V0``OPP@02&OY0.!(:5WXP?^!\''V"=1L2>P82%*N0``1T`O"0
+M``KPY/_^$APVD``+X%3]\.20``3PD`$&X%3?\,($$ALH4"42&E=_`A(9>)`!
+M!.!4?_"0``O@5/W_\.20``3P[U2_D``+\,($$AK44"T2&E=^,'_@?!Q]@G4;
+M$GL&$A2KD``$=`+PD``*\)`!!N!4W_"0``O@5+_PP@0BD#3-X/D@X_CE*_1@
+M9I`TP!((`84T@H4S@W7P(.4K$@@9Y8(D!/6"Y#6#]8,2"`V0-,@2"`&%-(*%
+M,X-U\"#E*Q((&>6")`CU@N0U@_6#$@@-D#30$@@!A32"A3.#=?`@Y2L2"!GE
+M@B0,]8+D-8/U@Q((#>4T)/#_Y3,TWO[O>`7.PQ/.$]CY]2N%-(*%,X-U\"`2
+M"!GE@B0$]8+D-8/U@Q((`9`TP!((#84T@H4S@W7P(.4K$@@9Y8(D"/6"Y#6#
+M]8,2"`&0-,@2"`V%-(*%,X-U\"#E*Q((&>6")`SU@N0U@_6#$@@!D#3$$@@-
+MD`$!X$1`\)`!`.!$"/#I1`20-,WPD#3,X$0!\*/@1`'PH^!$`?`BCQ6,%HT7
+MY17#E`105N45E`!`!GH`>V"`!'H`>\#E%\3X5`_(:/_E%L14\$C^Y160&HZ3
+M_7P`$@>6[RO[[CKZY1?$^%0/R&C_Y1;$5/!(_N45D_U\`!('ENU,8&,+NP`!
+M"H!<>@![&N47KA9X`L,SSC/.V/DD"__D/O[E%9`:CI/]?``2!Y;O>`+#,\XS
+MSMCY*_ON.OKE%ZX6>`+#,\XSSMCY)`O_Y#[^Y160&HZ3_7P`$@>6[4Q@!W0$
+M*_OD.OK/Z\_.ZLXBY2X48!T48#T48%T4<`,"#]<D!&`#`@_M(`T#`@_M=2X!
+M(I``"N#_,.4#1"#PY4!%/V`#`@_M=2X"$AF;$AMXKR@2&J@BD`$#X/\PYW;O
+M1("0`0/P$@A1$AG>$AN[=2X#KR)^`!(<*B+E0$4_<"$2%$$2&W@2&;X2&[L2
+M'`0P#0MU+@&O,GX`$APJ(N3U+B*0``K@_S#E+$0@\!(401(;>!(9OA(;NQ(<
+M!'4N!"+E0$4_<!`P#0IU+@&O,OX2'"HBY/4N(I``!'0"\)``"O`P"3+E-$4S
+M<`+#(H4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O
+M\.4T13-P`L,B$@7M4/.0``K@(.4#,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZC
+MX/^%-(*%,X.CH^#\H^#]P^^=_^Z<T(+0@_"C[_#E-$4S<`+#(A(%[5#S@+6%
+M-(*%,X/@_J/@_Q(6ZM,B$AK^0`42&M101'XP?^!\''V"=1L2>P82%*N0``1T
+M`O"0``KPY/_^$APVD``+X%2_\%1___#DD##I\.]4_9``"_#DD``$\-()$@_N
+MY/4O$AL34$A^,'_@?!Q]@G4;$GL&$A2KD``$=`+PD``*\.3__A(<-I``"^!4
+MO_!4_?#DD``$\/\2&7A0!'4O!R*0`03@5'_PT@D2#^[D]2\BPJ_D]2_UB'6H
+M#W6)$?6X]>AUD`]U,?]U*_^0(B[PH_"0(D[PH_#"!<((P@#"!\($D``*=/_P
+MD``+=`'PD`$#=/_PY)`!!/"0`05T__#DD`$&\)``!/"0,.AT$/"0`0?PD`$(
+M!/"0`0ET2/"0`0IT?_"0`0)T'_"0`0!T%/"0`0%T(/"0``#@1(#P=4D`=4H!
+MP@'2KR(2&M10+1(82)`!!N!4W_!^,'_@?!Q]@G4;$GL&$A2KD``$=`+PD``*
+M\-()$@_NY/4O(A(;*%!0$AA(D``+X%3]\.20``3PD`$#=(#PD`$$X$2`\'\"
+M$AEX4`1U+P4B?C!_X'P<?8)U&Q)[!A(4JY``!'0"\)``"O#2"1(/[I`!!.!4
+M?_#D]2\BD#`P=`+P=1$'=1+0D#`PX##@#N42%1)P`A41Y1)%$7#KY1)%$7`2
+M$AITD"$`X&`'D#28X$0$\,,BY)`T6/"0-#)T'_!U$0=U$M"0-('@9`-@#N42
+M%1)P`A41Y1)%$7#JY1)%$7`2$AITD"$`X&`'D#28X$0$\,,BD#28X$0$\.20
+M``'PTR*0,#K@]1`2'%=0)N4G,.8%4Q#]@`-#$`+E)S#G!5,0]X`#0Q`(4Q#^
+M0Q`$D#`ZY1#P$AQ,4$B0`0/@]1!4'&`^Y1!4X_"CX/40\.4G,.8%0Q`"@`-3
+M$/WE)S#G!4,0"(`#4Q#W4Q#^0Q`$D#`ZY1#PKRE^`!(<=*\U?@`2''LB$AJ_
+M4'(2'$&%-(*%,X/@_*/@PY_U$NR>]1'3Y1*4`.419("4@$`&KA&O$H`$?@!_
+M`(X1CQ+E-"00]8+D-3/U@^##E1+U$N25$?41PV2`E(!0!>3U$?42Y30D$O_D
+M-3/^K1)[`1(9*Y`!!70@\)`!!N!$(/!U+P,BP.#`\,"#P(+`T'70",*OD"(N
+MX/ZCX(XS]33E+R7@))OU@N0T&_6#Y)/^=`&3RN[*^1((2Q(;]E`"T@<2%RP2
+M`!X2&NE0!:\E$AQB,`4;Y2]P%R`$%!(23I(`P@72"Z(`Y#/U%*\C$A:BTJ_0
+MT-""T(/0\-#@,I`IH.!P8Y`PC.3PHW3"\*-T`?"CY/"0,(S@_Y`IH/"0,(SD
+M\*-TQ?"C=`'PH^3PD#",X/^0*:'PD#",Y/"C=,3PHW0!\*/D\)`PC.#_D"FB
+M\)`PC.3PHW3#\*-T`?"CY/"0,(S@D"FC\"*.%H\7C!B-&>3_[\.;4%/E&S#@
+M$N]\`"49_>PU&(V"]8/@]1R`'^4;,.$3[WP`)1G][#48C8+U@^23]1R`!^49
+M+_CF]1SE&S#D#^47+_6"Y#46]8/E'/"`!N47+_BF'`^`J"*,$XT4[R0>]8+D
+M/O6#X/RCX$Q@0>\D'O6"Y#[U@^#\H^#U@HR#X/RCX/V%%(*%$X/@^J/@^]/M
+MF^ID@/CL9("80!/O)![U@N0^]8/@_*/@SNS._X"OK12L$Q(8KR(2&K]03^4T
+M)!+_Y#4S_N4T)!#U@N0U,_6#X/WD^Q(9*^4T)!#U@N0U,_6#X/]^`!(<-I``
+M"G1`\)``"^!$0/_PD``*=(#P3Y``"_"0,.ET`?!U+P8BD#`ZX/_E)S#F$C`,
+M!N]4]?Z`!.]$"O[/[L^`$#`,!N]$"OZ`!.]4]?[/[L_/5/[/ST0$SY`P.N_P
+M,`P)?PA^`!(<=(`'?R)^`1(<=+(,(L#@P/#`@\""P-!UT`C"K\*,PHW3Y4J4
+M`.5)E`!`".5*%4IP`A5)T^5,E`#E2Y0`0`CE3!5,<`(52Q(`#M*,TJ_0T-""
+MT(/0\-#@,L/OE`3N9("4@$`,T^V4!.QD@)2`4`$BP^^4_.YD@)1_0`S3[93\
+M[&2`E']0`2+3[Y0$[F2`E(!0#</ME/SL9("4?T`"TR+#(N3^[_1@070$+O6"
+MY#0A]8/@M/\C=`0N]8+D-"'U@^_P,`L-=`@N]8+D-"'U@^44\)```G0!\"*^
+M`PJ0``)T`?#D_H#"#H"_(HX3CQ02'$'#[Y44_^Z5$\WOS?S3[90`[&2`E(!`
+M!<[LSH`$?@!_`<SNS.R0``7PD``&[_"0``1T4?"0``O@1`+P(C`'/.4O<#C"
+M!Y`B+N#^H^".$?42D").X/ZCX/^0(B[N\*/O\)`B3N41\*/E$O".,X\T,`@%
+M$A>EP@C""1(/[B)_@'XIY/W\CX*.@^#[=$4M^,;KQG0$+_6"Y#[U@^#[=$$M
+M^,;KQG0(+__D/OX-O0`!#.UD!$QPSR*0(0#@Q#-4X"00]8+D-"&K@OH2&ZN+
+M@HJ#[H_P$@?K?B)_,,WKS<SJS!(5#N20(0/PKRT2'&(BD```=`[P````Y/`2
+M$3$2'(B0-)C@1`'PY3"T!0H2'$Q0#1(5P(`(Y3"T!P,2$M02#RJ`Y(#^(N3_
+MY3`D_G`LY/[NPY4L4!)T`<CNR`B``L,SV/S/3\\.@.B0,#3@5.#^Y2=4'V_T
+MSD[.[O`BD#3.X$0"\)`TS>!4_O"0-,W@(./YD`$1X%0B_[\B`].``<-0\)`!
+M`.!4]_"0`0'@5+_P(N\D'O6"Y#[U@^#\H^#[RNS*)![U@N0\]8/@_*/@_>\D
+M'O6"Y#[U@^SPH^WPSNK.S^O/(N\D'O6"Y#[U@^#ZH^#[[20>]8+D//6#ZO"C
+MZ_#O)![U@N0^]8/L\*/M\"+`X,#0PJ_"CL*/T^5`E`#E/Y0`0`WE0!5`<`(5
+M/Q(<:]*.TJ_0T-#@,A(:OU`B?C!_X'P<?8)U&Q)[!A(4JY``!'0"\)``"O#2
+M"1(/[N3U+R*.$X\4C17K8`D4<!NO%1(9_B)^,'_@K!.M%'4;$7L&$A2KKQ42
+M&?XB$A=LD"$!X/4H=$$E*/CF]3)T124H^.;U(I`A`.!@`](-(L(-(LWOS9`!
+M`N`PYP+#(GXJ?P`2!`Z0`03@1(#PD`$"X$2`\-,BD#0PY/"C\*-T'_"CY/"0
+M`1#@(.$#`(#VD`$2X"#A`P"`]B+D_W0V+_CFD#",\.^0&U&3D#"-\*-T`?"C
+MY/`/OPGC(N3_[Y`;6Y.0,(SP[Y`;49.0,(WPHW0!\*/D\`^_">,BY)``!?#O
+M8`*``G\!D``&[_"0``1T4?"0``O@1`+P(I`P\.#U*I``"N`PY`Z0,/+@8`B0
+M``IT$/#3(L,BD##PX/4JD``*X##D#I`P\N!P")``"G00\-,BPR*0-,[@1`+P
+MD#3-X%3^\)`!`.!4]_"0`0'@5+_P(I```70.\)`TF.!4^_"0-%AT`?"0,#!T
+M!/`B`@0+%@P2&"0P2&!LD,@``0(#"P\*#@D-"`SOQ#,S5,#_D`$`X%0_3_"0
+M`0+@1(#P(I`!`^`PYPQT@/"0`03@5'_PTR+#(I``"N`PX0QT`O"0``O@5/WP
+MTR+#(I``"N`PX@QT!/"0``O@5/OPTR+#(I``"N`PY@QT0/"0``O@5+_PTR+#
+M(I``"N`PYPQT@/"0``O@5'_PTR+#(I`!!>`PY0QT(/"0`0;@5-_PTR+#(N3U
+M,9``"G3_\)`B+G0A\*-T$/`B4E-4$A46$!%``"``0/\_/[TH(0#E-"01]8+D
+M-3/U@^`PY@+3(L,BD#!`=#+PHW2P\*-T`?"CY/`B=3$!D``*=/_PD``+X$0@
+M\"(+T1-3%6L1Q@`)&0,0E1O:D#!DX/VCX/[M)>#_[C/^(I`P0'0R\*-TL/"C
+MY/"C\"*0`0#@1`CPD`$!X$1`\"(2&K]0"-()$@_NY/4O(I``"N`PX`5T`?#3
+M(L,BD``*X##E!70@\-,BPR*0-#!T'_"CY/"C\*/P(GA_Y/;8_76!3`(7W,*O
+MPHS"C1(`#M*O(L*.CC^/0!(<:]*.(I`P/._P[D2`H_`BD#!XX/VCX/[M_R+E
+M2D5)<`/3@`'#(N5,14MP`].``<,BP@OD]102%J(BPH]UC?5UBT$BCDF/2M*,
+M(HY+CTS2C"(```````#"#>3U+B(`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"K#``
+`
+end
diff --git a/sys/contrib/dev/ral/rt2561s.fw.uu b/sys/contrib/dev/ral/rt2561s.fw.uu
new file mode 100644
index 0000000..43d4353
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2561s.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2561s.fw
+M`APM`@??PHPB(@`"&4/"K\*-=8R4=8J3TJ\B`AJ<$@C?0`,"`AZ0(0+@]2V0
+M``/@$@0_`+```,X!`%X0`&\1`/(@`4TA`7`B`80P`8\Q`=50`9]1`?)2`@9@
+M```"%)``"N`@Y0,P!P/2""(2%"LBD"$`X/41Y1'$,U3@)"'U@N0T(?6#X$2`
+M\.41Q#-4X"0L]8+D-"'U@^41\,0S5.`D+?6"Y#0A]8/E+?#DD"$#\"(2!<N0
+M(0#@]3%@!1(;X(`#$ANFY)`A`_"O+1(0Z")U,?^0`0#@5/?PD`$!X%3^\%0^
+M\.20``OP\)`A`_"O+1(0Z")^*W^`?0,2"ON0-,W@(./YD"$4$@0;D#3`$@0G
+MD"$8$@0;D#3($@0GD"$<$@0;D#3$$@0GD#3,=`'PH^!$!/"0`0'@1`'P1$#P
+MD``+X$00\.20(0/PKRT2$.@BD`$`X%3W\)`!`>!4_O!4O_"0``O@5._PY)`A
+M`_"O+1(0Z")^*W^`?0,2"OODD"$#\*\M$A#H(M(%A2TCY)`A`_`B$A.NP@#D
+MD"$#\*\M$A#H(H4M)9``"^!4^__PY)``!_"0``IT!/#DD``(\)`A`."0``GP
+MD``'=''P[T0$D``+\.20(0/P(I`A`.#_5!_U,*/@]2>/)A(48N20(0/PKRT2
+M$.@BD"$`X/4L$A>CY)`A`_"O+1(0Z"(2&NWDD"$#\*\M$A#H(N20(0/PKRT2
+M$.@BY3%D`7!!$@CM0`,"`YT2#C10('XK?X!]`Q(*^W\!$@K80`G2"1(.1^3U
+M+R(2"61U+P$B?P$2"MA0!'4O`B+2"1(.1^3U+R(2")!041(*K)`P].#U*GXP
+M?^RCX/WD^Q(-H.3__A(.*9``"G0"\)``"^!$`O_P_9`!!70@\)`!!N!$(/#M
+M5+^0``OPD#3,X$0!\*/@1`'PH^!$`?#2!!((K5!#$@J[?C!_X'PP?>QU&Q%[
+M!A(3R)`P]>!U\""D_Z[P$@XID``+X%3]__#]Y)``!/"0`0;@5-_PD``*=$#P
+M39``"_#"!!())5`X$@J[?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#D__X2
+M#BF0``O@5/WPY)``!/"0`0;@5-_PP@02"4]0)1(*NW\"$@K8D`$$X%1_\)``
+M"^!4_?_PY)``!/#O5+^0``OPP@02".U0+1(*NWXP?^!\''U^=1L2>P82$\B0
+M``1T`O"0``KPD`$&X%3?\)``"^!4O_#"!"+OC?"DJ/#/C/"D*,Z-\*0N_B*\
+M``N^`"GOC?"$_ZWP(N3,^'7P".\O_^XS_NPS_.Z=[)A`!?SNG?X/U?#IY,[]
+M(NWX]?#NA"#2'/ZM\'7P".\O_^TS_4`'F%`&U?#R(L.8_0_5\.HBQ?#XH^`H
+M\,7P^.6"%8)P`A6#X#CP(N#\H^#]H^#^H^#_(NSPH^WPH^[PH^_P(J0E@O6"
+MY?`U@_6#(M"#T(+XY)-P$G0!DW`-HZ.3^'0!D_6"B(/D<W0"DVA@[Z.CHX#?
+MBH.)@N1SY2X48!T48#T48%T4<`,"!1@D!&`#`@4N(`T#`@4N=2X!(I``"N#_
+M,.4#1"#PY4!%/V`#`@4N=2X"$AL2$AO.KR@2&X\BD`$#X/\PYW;O1("0`0/P
+M$@A5$AM5$AP!=2X#KR)^`!(<12+E0$4_<"$2%]@2&\X2&S42'`$2'!\P#0MU
+M+@&O,GX`$AQ%(N3U+B*0``K@_S#E+$0@\!(7V!(;SA(;-1(<`1(<'W4N!"+E
+M0$4_<!`P#0IU+@&O,OX2'$4BY/4N(A())4`%$@CM4$1^,'_@?!Q]?G4;$GL&
+M$A/(D``$=`+PD``*\.3__A(.*9``"^!4O_!4?__PY)`PZ?#O5/V0``OPY)``
+M!/#2"1(.1^3U+Q().E!(?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#D__X2
+M#BF0``O@5+_P5/WPY)``!/#_$@K84`1U+P<BD`$$X%1_\-()$@Y'Y/4O(L*O
+MY/4O]8AUJ`]UB1'UN/7H=9`/=3'_=2O_D"(N\*/PD").\*/PP@7"",(`P@?"
+M!)``"G3_\)``"W0!\)`!`W3_\.20`03PD`$%=/_PY)`!!O"0``3PD##H=!#P
+MD`$'\)`!"`3PD`$)=$CPD`$*='_PD`$"=!_PD`$`=!3PD`$!="#PD```X$2`
+M\'5)`'5*`<(!TJ\B$@CM4"T2"GB0`0;@5-_P?C!_X'P<?7YU&Q)[!A(3R)``
+M!'0"\)``"O#2"1(.1^3U+R(2"4]04!(*>)``"^!4_?#DD``$\)`!`W2`\)`!
+M!.!$@/!_`A(*V%`$=2\%(GXP?^!\''U^=1L2>P82$\B0``1T`O"0``KPT@D2
+M#D>0`03@5'_PY/4O(I`P.N#U$!(<7%`FY2<PY@53$/V``T,0`N4G,.<%4Q#W
+M@`-#$`A3$/Y#$`20,#KE$/`2'%%02)`!`^#U$%0<8#[E$%3C\*/@]1#PY2<P
+MY@5#$`*``U,0_>4G,.<%0Q`(@`-3$/=3$/Y#$`20,#KE$/"O*7X`$AQPKS5^
+M`!(<=R(2",I0<A(0S84T@H4S@^#\H^##G_42[)[U$=/E$I0`Y1%D@)2`0`:N
+M$:\2@`1^`'\`CA&/$N4T)!#U@N0U,_6#X,.5$O42Y)41]1'#9("4@%`%Y/41
+M]1+E-"02_^0U,_ZM$GL!$@V@D`$%="#PD`$&X$0@\'4O`R+`X,#PP(/`@L#0
+M==`(PJ^0(B[@_J/@CC/U-.4O)>`D\?6"Y#0;]8/DD_YT`9/*[LKY$@1E$@D7
+M4`+2!Q(9CA(`'A()`E`%KR42$.@P!1OE+W`7(`04$A,HD@#"!=(+H@#D,_44
+MKR,2$/'2K]#0T(+0@]#PT.`RY/^0,(SD\.^0&[J31("0,(WPHW0!\*/D\)`P
+MC.#^=#8O^,;NQJ/@_N^0&[J31(!N8`$?#^_#E`E`R"*0,/#@]2J0``K@,.0.
+MD##RX&`(D``*=!#PTR+#(I`P\.#U*I``"N`PY`Z0,/+@<`B0``IT$/#3(L,B
+MD`$#X##G#'2`\)`!!.!4?_#3(L,BD``*X##@!70!\-,BPR*0``K@,.$,=`+P
+MD``+X%3]\-,BPR*0``K@,.(,=`3PD``+X%3[\-,BPR*0``K@,.4%="#PTR+#
+M(I``"N`PY@QT0/"0``O@5+_PTR+#(I``"N`PYPQT@/"0``O@5'_PTR+#(I`!
+M!>`PY0QT(/"0`0;@5-_PTR+#(I`TS>#Y(./XY2OT8&:0-,`2!!N%-(*%,X-U
+M\"#E*Q($,^6")`3U@N0U@_6#$@0GD#3($@0;A32"A3.#=?`@Y2L2!#/E@B0(
+M]8+D-8/U@Q($)Y`TT!($&X4T@H4S@W7P(.4K$@0SY8(D#/6"Y#6#]8,2!"?E
+M-"3P_^4S--[^[W@%SL,3SA/8^?4KA32"A3.#=?`@$@0SY8(D!/6"Y#6#]8,2
+M!!N0-,`2!">%-(*%,X-U\"#E*Q($,^6")`CU@N0U@_6#$@0;D#3($@0GA32"
+MA3.#=?`@Y2L2!#/E@B0,]8+D-8/U@Q($&Y`TQ!($)Y`!`>!$0/"0`0#@1`CP
+MZ40$D#3-\)`TS.!$`?"CX$0!\*/@1`'P(I`TSN!$`O"0-,W@5/[PD#3-X"#C
+M^9`!$>!4(O^_(@/3@`'#4/"0`0#@5/?PD`$!X%2_\"*0`0#@1`CPD`$!X$1`
+M\"*0-,[@1`+PD#3-X%3^\)`!`.!4]_"0`0'@5+_P(LWOS9`!`N`PYP+#(GXJ
+M?P`2"ON0`03@1(#PD`$"X$2`\-,BCA*/$XT4Y12BX9()Y30D&?6"Y#4S]8/@
+M_>4T)!KU@N0U,_6#X/NB"9(*=1D`=1H:$A$Y,`D$?\B``G_HY1,D&/6"Y#42
+M]8/O\.4Q8`1_`H`"?P'E$R09]8+D-1+U@^_PY30D&?6"Y#4S]8/@_WT:?``2
+M#-KE$R0:]8+D-1+U@^_PY1,D&_6"Y#42]8/N\.4Q8&#E$R0<_^0U$O[E-"02
+M_>0U,_QU&Q%[!A(3R.43)"+_Y#42_GPP?1!U&Q%[!A(3R.43)"C_Y#42_GPP
+M?0AU&Q%[!A(3R.4T)!CU@N0U,_6#X/_E$R0M]8+D-1+U@^_P@#_E$R0<_^0U
+M$OY\,'T0=1L1>P82$\CE$R0B_^0U$OY\,'T(=1L1>P82$\CE$R0H_^0U$OY\
+M,'T0=1L1>P82$\CE$R0N]8+D-1+U@^3PY1,D+_6"Y#42]8/D\.4T)!'U@N0U
+M,_6#X/_#$__E$R0P]8+D-1+U@^_P,`E!Y1,D,/6"Y#42]8/@_^4Q8`1^`(`"
+M?A#O3O#E,6`&?@!_`(`/Y10PX`9^`'__@`1^`'\`Y1,D,?6"Y#42]8/O\"+E
+M$R0P]8+D-1+U@^!$0/#E%##@#^4T)!#U@N0U,_6#X/^``G\`Y1,D,?6"Y#42
+M]8/O\"*/%8P6C1?E%<.4!%!6Y164`$`&>@![8(`$>@![P.47Q/A4#\AH_^46
+MQ%3P2/[E%9`;=9/]?``2`[#O*_ON.OKE%\3X5`_(:/_E%L14\$C^Y163_7P`
+M$@.P[4Q@8PN[``$*@%QZ`'L:Y1>N%G@"PS/.,\[8^20+_^0^_N45D!MUD_U\
+M`!(#L.]X`L,SSC/.V/DK^^XZ^N47KA9X`L,SSC/.V/DD"__D/O[E%9`;=9/]
+M?``2`[#M3&`'=`0K^^0Z^L_KS\[JSB*.$X\4C17K8`D4<!NO%1(."B)^,'_@
+MK!.M%'4;$7L&$A/(KQ42#@HBCA./%!(0S</OE13_[I43S>_-_-/ME`#L9("4
+M@$`%SNS.@`1^`'\!S.[,[)``!?"0``;O\)``!'11\)``"^!$`O`BY)``!?#O
+M8`*``G\!D``&[_"0``1T4?"0``O@1`+P(I`P/._P[D2`H_`BY30D$?6"Y#4S
+M]8/@,.8"TR+#(I``!'0"\)``"O`P"3+E-$4S<`+#(H4T@H4S@\"#P(+@_J/@
+M_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O\.4T13-P`L,B$@[N4/.0``K@
+M(.4#,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZCX/^%-(*%,X.CH^#\H^#]P^^=
+M_^Z<T(+0@_"C[_#E-$4S<`+#(A(.[E#S@+6%-(*%,X/@_J/@_Q(-R-,BY30D
+M$?6"Y#4S]8/@,.<[Y30D'/6"Y#4S]8/@92MP`W4K_^4T)!WU@N0U,_6#X/\2
+M$.A^(G\0$AH^CC./-)`B+N#^H^".,_4TPR+2"N4T)!OU@N0U,_6#X'`ZA32"
+MA3.#P(/`@N#^H^#_A32"A3.#HZ/@_*/@_</OG?_NG/[0@M"#\*/O\-.4`.YD
+M@)2`4`,"$"B`QH4T@H4S@^#^H^##[F2`E(!0`P(0*!(0S84T@H4S@^#\H^#]
+MPY_N9(#X[&2`F$`@A32"A3.#P(/`@J.CX/ZCX/_MG__LGM""T(/PH^_PP@J%
+M-(*%,X/@_J/@_^4T)!#U@N0U,_6#X/W#[YW][I0`_!(0A5`LA32"A3.#P(/`
+M@N#^H^#_A32"A3.#HZ/@_*/@_</OG?_NG-""T(/PH^_PP@H@"@,"#SA^(G\0
+M$AH^CC./-(^"CH/@_J/@TY0`[F2`E(!`#7XB?Q"M-*PS$AA"@!H2$-B%-(*%
+M,X/NC_`2!`5^(G\PK32L,Q(80I`B+N#^H^#_931P`^YE,W`"TR*.,X\TPR+#
+M[Y0$[F2`E(!`#-/ME`3L9("4@%`!(L/OE/SN9("4?T`,T^V4_.QD@)1_4`$B
+MT^^4!.YD@)2`4`W#[93\[&2`E']``M,BPR*0,'C@_:/@_NW_(I`P9.#]H^#^
+M[27@_^XS_B+""^3U%!(0\2+D_N_T8$%T!"[U@N0T(?6#X+3_(W0$+O6"Y#0A
+M]8/O\#`+#70(+O6"Y#0A]8/E%/"0``)T`?`BO@,*D``"=`'PY/Z`P@Z`OR*.
+M%8\6RNW*R>O),`H$?TJ``G]"R^_+ZL.4!%`"@`'#0`3+1"#+A1:"A16#Z_"C
+MY/"%%H*%%8.CH^4:\.49A1:"A16#HZ.C\.46)`3U@N0U%?6#=`_PY18D!?6"
+MY#45]8/D\.46)`;U@N0U%?6#Y/#E%B0']8+D-17U@W00\.J0&X.3^^ID`6`(
+MZF0"8`.Z`P3+1`C+Y18D"/6"Y#45]8/K\.46)!7U@N0U%?6#=/_PY18D%O6"
+MY#45]8/I\.46)`GU@N0U%?6#=`3P)1KU&N0U&?49ZL.4!$`#`A+PZF`#N@$?
+MZB0!_>0S_.4:KAEX`\,SSC/.V/G_$@.PCAF/&@(2T.HD__WD-/_\?@!_"Q(#
+MGLSNS,WOS>4:Q/A4#\AH_^49Q%3P2/X2`[",&XT<ZB3__>0T__Q^`'\+$@.>
+MS.[,S>_-Y1K$^%0/R&C_Y1G$5/!(_A(#L(X9CQKE'$4;8`@%&N4:<`(%&>HD
+M__WD-/_\?@!_`Q(#GM/E')_E&YY0&.4<11M@$KH##^46)`GU@N0U%?6#X$2`
+M\.46)`KU@N0U%?6#Y1KPY1G_Y18D"_6"Y#45]8/O\(`MY1I4/__E%B0*]8+D
+M-17U@^_PY1JN&7@&SL,3SA/8^?_E%B0+]8+D-17U@^_PA1:"A16#X$0!\"*0
+M,#!T`O!U$0=U$M"0,##@,.`.Y1(5$G`"%1'E$D41<.OE$D41<!(2$ZZ0(0#@
+M8`>0-)C@1`3PPR+DD#18\)`T,G0?\'41!W42T)`T@>!D`V`.Y1(5$G`"%1'E
+M$D41<.KE$D41<!(2$ZZ0(0#@8`>0-)C@1`3PPR*0-)C@1`3PY)```?#3(I``
+M`70.\)`TF.!4^_"0-%AT`?"0,#!T!/`BCA:/%XP8C1GD_^_#FU!3Y1LPX!+O
+M?``E&?WL-1B-@O6#X/4<@!_E&S#A$^]\`"49_>PU&(V"]8/DD_4<@`?E&2_X
+MYO4<Y1LPY`_E%R_U@N0U%O6#Y1SP@`;E%R_XIAP/@*@BD"$`X,0S5.`D$/6"
+MY#0AJX+Z$A#8BX**@^Z/\!($!7XB?S#-Z\W,ZLP2&$+DD"$#\*\M$A#H(N4P
+M$@0_%(,`%/$!%5D"%>T#%D$$%H@%%OL&%VH'```7HL(!$@`&D#`ZX/42Y28@
+MY0B0-)C@5/[P(I`TF.!$`?#E)C#F#^4G,.8%4Q+]@!)#$@*`#>4G,.8%0Q("
+M@`-3$OWE)C#G#^4G,.<%4Q+W@!)#$@B`#>4G,.<%0Q((@`-3$O=#$@%#$@20
+M,#KE$O`BP@$2``:0,#K@]1+E)B#E")`TF.!4_O`BD#28X$0!\.4F5,!@'.4G
+M,.8%4Q+]@`-#$@+E)S#G!5,2]X`?0Q((@!KE)S#F!4,2`H`#4Q+]Y2<PYP5#
+M$@B``U,2]T,2`4,2!)`P.N42\"+"`1(`!I`P.N#U$D,2`4,2!.4F,.5<D#28
+MX$0!\.4F5,!@'.4G,.8%4Q+]@`-#$@+E)S#G!5,2]X`P0Q((@"OE)S#F!4,2
+M`H`#4Q+]Y2<PYP5#$@B``U,2]^4G]%0?_Y`P-.!4X$_PY/4LD#`ZY1+P@!60
+M-)C@5/[PY2?T5!__D#`TX%3@3_"0,#7@]1)3$N#E$O`BP@$2``:0,#K@]1+E
+M)C#E/)`TF.!$`?#E)S#F!5,2_8`#0Q("Y2<PYP53$O>``T,2".4F5,!@"$,2
+M`4,2!(`&4Q+^0Q($D#`ZY1+P(I`TF.!4_O`BP@$2``:0,#K@]1+E)S#F!4,2
+M`H`#4Q+]Y2<PYP5#$@B``U,2]^4F5,!@"%,2_E,2^X`&0Q(!0Q($D#28X$0!
+M\)`P.N42\"(@`A,2'#FO*7X`$AQPKS5^`!(<=]("D#`ZX/42Y28@Y0W"`1(`
+M!I`TF.!4_O`BD#28X$0!\.4F5,!@+,(!$@`&Y2<PY@53$OV``T,2`N4G,.<%
+M4Q+W@`-#$@A#$@%#$@20,#KE$O`B,`$#`A>B$ACTT@$BP@$2``;E)B#E"9`T
+MF.!4_O"`59`TF.!$`?#E)C#F#^4G,.8%4Q+]@!)#$@*`#>4G,.8%0Q("@`-3
+M$OWE)C#G#^4G,.<%4Q+W@!)#$@B`#>4G,.<%0Q((@`-3$O=#$@%3$ON0,#KE
+M$O"0,#K@]1(BY28PY2P@`R'2`Q(<.74U!G4I":\I?@`2''"0,#K@]1)3$OY#
+M$@3E$O"0-)C@1`'P(I`TF.!4_O`BY/_E,"3^<"SD_N[#E2Q0$G0!R.[("(`"
+MPS/8_,]/SPZ`Z)`P-.!4X/[E)U0?;_3.3L[N\"*0*:#@<&.0,(SD\*-TPO"C
+M=`'PH^3PD#",X/^0*:#PD#",Y/"C=,7PHW0!\*/D\)`PC.#_D"FA\)`PC.3P
+MHW3$\*-T`?"CY/"0,(S@_Y`IHO"0,(SD\*-TP_"C=`'PH^3PD#",X)`IH_`B
+MC!.-%.\D'O6"Y#[U@^#\H^!,8$'O)![U@N0^]8/@_*/@]8*,@^#\H^#]A12"
+MA1.#X/JCX/O3[9OJ9(#X[&2`F$`3[R0>]8+D/O6#X/RCX,[LSO^`KZT4K!,2
+M&G$B$@C*4$_E-"02_^0U,_[E-"00]8+D-3/U@^#]Y/L2#:#E-"00]8+D-3/U
+M@^#_?@`2#BF0``IT0/"0``O@1$#_\)``"G2`\$^0``OPD##I=`'P=2\&(I`P
+M.N#_Y2<PYA(P#`;O5/7^@`3O1`K^S^[/@!`P#`;O1`K^@`3O5/7^S^[/SU3^
+MS\]$!,^0,#KO\#`,"7\(?@`2''"`!W\B?@$2''"R#"+`X,#PP(/`@L#0==`(
+MPJ_"C,*-T^5*E`#E290`0`CE2A5*<`(52=/E3)0`Y4N4`$`(Y4P53'`"%4L2
+M``[2C-*OT-#0@M"#T/#0X#(P!SSE+W`XP@>0(B[@_J/@CA'U$I`B3N#^H^#_
+MD"(N[O"C[_"0(D[E$?"CY1+PCC./-#`(!1(4*\((P@D2#D<B?X!^*>3]_(^"
+MCH/@^W1%+?C&Z\9T!"_U@N0^]8/@^W1!+?C&Z\9T""__Y#[^#;T``0SM9`1,
+M<,\BD```=`[P````Y/`2!<L2'(20-)C@1`'PY3"T!0H2'%%0#1(8](`(Y3"T
+M!P,2!N@2!&N`Y(#^(N\D'O6"Y#[U@^#\H^#[RNS*)![U@N0\]8/@_*/@_>\D
+M'O6"Y#[U@^SPH^WPSNK.S^O/(N\D'O6"Y#[U@^#ZH^#[[20>]8+D//6#ZO"C
+MZ_#O)![U@N0^]8/L\*/M\"+`X,#0PJ_"CL*/T^5`E`#E/Y0`0`WE0!5`<`(5
+M/Q(<9]*.TJ_0T-#@,A((RE`B?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#2
+M"1(.1^3U+R(2&<Z0(0'@]2AT024H^.;U,G1%)2CXYO4BD"$`X&`#T@TBP@TB
+MD#0PY/"C\*-T'_"CY/"0`1#@(.$#`(#VD`$2X"#A`P"`]B+D_W0V+_CFD#",
+M\.^0&[J3D#"-\*-T`?"CY/`/OPGC(N3_[Y`;Q).0,(SP[Y`;NI.0,(WPHW0!
+M\*/D\`^_">,B`@0+%@P2&"0P2&!LD,@``0(#"P\*#@D-"`SOQ#,S5,#_D`$`
+MX%0_3_"0`0+@1(#P(N3U,9``"G3_\)`B+G0A\*-T$/`B4E-4$A46$!%``"``
+M0/\_/[TH(0"0,$!T,O"C=+#PHW0!\*/D\")U,0&0``IT__"0``O@1"#P(@(?
+M!V<8GP9@``D:Q04O'!&0,$!T,O"C=+#PH^3PH_`B$@C*4`C2"1(.1^3U+R*0
+M-#!T'_"CY/"C\*/P(GA_Y/;8_76!3`(:!\*OPHS"C1(`#M*O(L*.CC^/0!(<
+M9]*.(N5*14EP`].``<,BY4Q%2W`#TX`!PR+"CW6-]76+02*.28]*THPBCDN/
+M3-*,(@```````,(-Y/4N(@``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"NV``
+`
+end
diff --git a/sys/contrib/dev/ral/rt2661.fw.uu b/sys/contrib/dev/ral/rt2661.fw.uu
new file mode 100644
index 0000000..ab02267
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2661.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2661.fw
+M`A+>`A3PPHPB(@`"%N7"K\*-=8R4=8J3TJ\B`AF)Y3`2#^4`/P``K0$!%0(!
+MJ0,!_00"1`4"MP8#)@<```->P@$2``:0,#K@]1+E)B#E")`TF.!4_O`BD#28
+MX$0!\.4F,.8/Y2<PY@53$OV`$D,2`H`-Y2<PY@5#$@*``U,2_>4F,.</Y2<P
+MYP53$O>`$D,2"(`-Y2<PYP5#$@B``U,2]T,2`4,2!)`P.N42\"+"`1(`!I`P
+M.N#U$N4F(.4(D#28X%3^\"*0-)C@1`'PY294P&`<Y2<PY@53$OV``T,2`N4G
+M,.<%4Q+W@!]#$@B`&N4G,.8%0Q("@`-3$OWE)S#G!4,2"(`#4Q+W0Q(!0Q($
+MD#`ZY1+P(L(!$@`&D#`ZX/420Q(!0Q($Y28PY5R0-)C@1`'PY294P&`<Y2<P
+MY@53$OV``T,2`N4G,.<%4Q+W@#!#$@B`*^4G,.8%0Q("@`-3$OWE)S#G!4,2
+M"(`#4Q+WY2?T5!__D#`TX%3@3_#D]2R0,#KE$O"`%9`TF.!4_O#E)_14'_^0
+M,#3@5.!/\)`P->#U$E,2X.42\"+"`1(`!I`P.N#U$N4F,.4\D#28X$0!\.4G
+M,.8%4Q+]@`-#$@+E)S#G!5,2]X`#0Q((Y294P&`(0Q(!0Q($@`93$OY#$@20
+M,#KE$O`BD#28X%3^\"+"`1(`!I`P.N#U$N4G,.8%0Q("@`-3$OWE)S#G!4,2
+M"(`#4Q+WY294P&`(4Q+^4Q+[@`9#$@%#$@20-)C@1`'PD#`ZY1+P(B`"$Q(<
+MG*\I?@`2'/*O-7X`$ASYT@*0,#K@]1+E)B#E#<(!$@`&D#28X%3^\"*0-)C@
+M1`'PY294P&`LP@$2``;E)S#F!5,2_8`#0Q("Y2<PYP53$O>``T,2"$,2`4,2
+M!)`P.N42\"(P`0,"`UX2"8'2`2+"`1(`!N4F(.4)D#28X%3^\(!5D#28X$0!
+M\.4F,.8/Y2<PY@53$OV`$D,2`H`-Y2<PY@5#$@*``U,2_>4F,.</Y2<PYP53
+M$O>`$D,2"(`-Y2<PYP5#$@B``U,2]T,2`5,2^Y`P.N42\)`P.N#U$B+E)C#E
+M+"`#(=(#$AR<=34&=2D)KRE^`!(<\I`P.N#U$E,2_D,2!.42\)`TF.!$`?`B
+MD#28X%3^\"(2'')``P(%7Y`A`N#U+9```^`2#^4#\0`$#P$#GQ`#L!$$,R`$
+MCB$$L2($Q3`$T#$%%E`$X%$%,U(%1V````55D``*X"#E`S`'`](((A(8MB*0
+M(0#@]1'E$<0S5.`D(?6"Y#0A]8/@1(#PY1'$,U3@)"SU@N0T(?6#Y1'PQ#-4
+MX"0M]8+D-"'U@^4M\.20(0/P(A((QY`A`.#U,6`%$@E<@`,2"6WDD"$#\*\M
+M$AS@(G4Q_Y`!`.!4]_"0`0'@5/[P5#[PY)``"_#PD"$#\*\M$AS@(GXK?X!]
+M`Q(*3Y`TS>`@X_F0(102#\&0-,`2#\V0(1@2#\&0-,@2#\V0(1P2#\&0-,02
+M#\V0-,QT`?"CX$0$\)`!`>!$`?!$0/"0``O@1!#PY)`A`_"O+1(<X"*0`0#@
+M5/?PD`$!X%3^\%2_\)``"^!4[_#DD"$#\*\M$AS@(GXK?X!]`Q(*3^20(0/P
+MKRT2'.`BT@6%+2/DD"$#\"(2&R/"`.20(0/PKRT2'.`BA2TED``+X%3[__#D
+MD``'\)``"G0$\.20``CPD"$`X)``"?"0``=T<?#O1`20``OPY)`A`_`BD"$`
+MX/]4'_4PH^#U)X\F$@`>Y)`A`_"O+1(<X"*0(0#@]2P2&.WDD"$#\*\M$AS@
+M(A(:`N20(0/PKRT2'.`BY)`A`_"O+1(<X"*.%8\6RNW*R>O),`H$?TJ``G]"
+MR^_+ZL.4!%`"@`'#0`3+1"#+A1:"A16#Z_"CY/"%%H*%%8.CH^4:\.49A1:"
+MA16#HZ.C\.46)`3U@N0U%?6#=`_PY18D!?6"Y#45]8/D\.46)`;U@N0U%?6#
+MY/#E%B0']8+D-17U@W00\.J0&TN3^^ID`6`(ZF0"8`.Z`P3+1`C+Y18D"/6"
+MY#45]8/K\.46)!7U@N0U%?6#=/_PY18D%O6"Y#45]8/I\.46)`GU@N0U%?6#
+M=`3P)1KU&N0U&?49ZL.4!$`#`@<7ZF`#N@$?ZB0!_>0S_.4:KAEX`\,SSC/.
+MV/G_$@]6CAF/&@(&]^HD__WD-/_\?@!_"Q(/1,SNS,WOS>4:Q/A4#\AH_^49
+MQ%3P2/X2#U:,&XT<ZB3__>0T__Q^`'\+$@]$S.[,S>_-Y1K$^%0/R&C_Y1G$
+M5/!(_A(/5HX9CQKE'$4;8`@%&N4:<`(%&>HD__WD-/_\?@!_`Q(/1-/E')_E
+M&YY0&.4<11M@$KH##^46)`GU@N0U%?6#X$2`\.46)`KU@N0U%?6#Y1KPY1G_
+MY18D"_6"Y#45]8/O\(`MY1I4/__E%B0*]8+D-17U@^_PY1JN&7@&SL,3SA/8
+M^?_E%B0+]8+D-17U@^_PA1:"A16#X$0!\"*0-,W@^2#C^.4K]&!FD#3`$@_!
+MA32"A3.#=?`@Y2L2#]GE@B0$]8+D-8/U@Q(/S9`TR!(/P84T@H4S@W7P(.4K
+M$@_9Y8(D"/6"Y#6#]8,2#\V0--`2#\&%-(*%,X-U\"#E*Q(/V>6")`SU@N0U
+M@_6#$@_-Y30D\/_E,S3>_N]X!<[#$\X3V/GU*X4T@H4S@W7P(!(/V>6")`3U
+M@N0U@_6#$@_!D#3`$@_-A32"A3.#=?`@Y2L2#]GE@B0(]8+D-8/U@Q(/P9`T
+MR!(/S84T@H4S@W7P(.4K$@_9Y8(D#/6"Y#6#]8,2#\&0-,02#\V0`0'@1$#P
+MD`$`X$0(\.E$!)`TS?"0-,S@1`'PH^!$`?"CX$0!\"+O)![U@N0^]8/@^J/@
+M^^TD'O6"Y#SU@^KPH^OP[R0>]8+D/O6#[/"C[?`B``"0``!T#O````#D\!((
+MQQ(=!I`TF.!$`?#E,+0%"A(<RE`-$@F!@`CE,+0'`Q()T!(0UX#D@/XBPJ_D
+M]2_UB'6H#W6)$?6X]>AUD`]U,?]U*_^0(B[PH_"0(D[PH_#"!<((P@#"!\($
+MD``*=/_PD``+=`'PD`$#=/_PY)`!!/"0`05T__#DD`$&\)``!/"0,.AT$/"0
+M`0?PD`$(!/"0`0ET2/"0`0IT?_"0`0)T'_"0`0!T%/"0`0%T(/"0``#@1(#P
+M=4D`=4H!P@'2KR)U,0&0``IT__"0``O@1"#P(N3U,9``"G3_\)`B+G0A\*-T
+M$/`BD#`ZX/_E)S#F$C`,!N]4]?Z`!.]$"O[/[L^`$#`,!N]$"OZ`!.]4]?[/
+M[L_/5/[/ST0$SY`P.N_P,`P)?PA^`!(<\H`'?R)^`1(<\K(,(I`P.N#U$!(<
+MU5`FY2<PY@53$/V``T,0`N4G,.<%4Q#W@`-#$`A3$/Y#$`20,#KE$/`2',I0
+M2)`!`^#U$%0<8#[E$%3C\*/@]1#PY2<PY@5#$`*``U,0_>4G,.<%0Q`(@`-3
+M$/=3$/Y#$`20,#KE$/"O*7X`$ASRKS5^`!(<^2*.$H\3C13E%*+AD@GE-"09
+M]8+D-3/U@^#]Y30D&O6"Y#4S]8/@^Z()D@IU&0!U&AH2!6`P"01_R(`"?^CE
+M$R08]8+D-1+U@^_PY3%@!'\"@`)_`>43)!GU@N0U$O6#[_#E-"09]8+D-3/U
+M@^#_?1I\`!(0$>43)!KU@N0U$O6#[_#E$R0;]8+D-1+U@^[PY3%@8.43)!S_
+MY#42_N4T)!+]Y#4S_'4;$7L&$A70Y1,D(O_D-1+^?#!]$'4;$7L&$A70Y1,D
+M*/_D-1+^?#!]"'4;$7L&$A70Y30D&/6"Y#4S]8/@_^43)"WU@N0U$O6#[_"`
+M/^43)!S_Y#42_GPP?1!U&Q%[!A(5T.43)"+_Y#42_GPP?0AU&Q%[!A(5T.43
+M)"C_Y#42_GPP?1!U&Q%[!A(5T.43)"[U@N0U$O6#Y/#E$R0O]8+D-1+U@^3P
+MY30D$?6"Y#4S]8/@_\,3_^43)##U@N0U$O6#[_`P"4'E$R0P]8+D-1+U@^#_
+MY3%@!'X`@`)^$.].\.4Q8`9^`'\`@`_E%##@!GX`?_^`!'X`?P#E$R0Q]8+D
+M-1+U@^_P(N43)##U@N0U$O6#X$1`\.44,.`/Y30D$/6"Y#4S]8/@_X`"?P#E
+M$R0Q]8+D-1+U@^_P(N4T)!'U@N0U,_6#X##G.^4T)!SU@N0U,_6#X&4K<`-U
+M*__E-"0=]8+D-3/U@^#_$AS@?B)_$!(95HXSCS20(B[@_J/@CC/U-,,BT@KE
+M-"0;]8+D-3/U@^!P.H4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_
+M[IS^T(+0@_"C[_#3E`#N9("4@%`#`@UH@,:%-(*%,X/@_J/@P^YD@)2`4`,"
+M#6@2'+^%-(*%,X/@_*/@_<.?[F2`^.QD@)A`((4T@H4S@\"#P(*CH^#^H^#_
+M[9__[)[0@M"#\*/O\,(*A32"A3.#X/ZCX/_E-"00]8+D-3/U@^#]P^^=_>Z4
+M`/P2%S!0+(4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#
+M\*/O\,(*(`H#`@QX?B)_$!(95HXSCS2/@HZ#X/ZCX-.4`.YD@)2`0`U^(G\0
+MK32L,Q(6,X`:$APUA32"A3.#[H_P$@^K?B)_,*TTK#,2%C.0(B[@_J/@_V4T
+M<`/N93-P`M,BCC./-,,BY3%D`7!!$AN#0`,"#T,2'`!0('XK?X!]`Q(*3W\!
+M$AHG0`G2"1(1F^3U+R(2!T]U+P$B?P$2&B=0!'4O`B+2"1(1F^3U+R(2&LQ0
+M41(<59`P].#U*GXP?^RCX/WD^Q(9VN3__A(<M)``"G0"\)``"^!$`O_P_9`!
+M!70@\)`!!N!$(/#M5+^0``OPD#3,X$0!\*/@1`'PH^!$`?#2!!(:Z5!#$AL&
+M?C!_X'PP?>QU&Q%[!A(5T)`P]>!U\""D_Z[P$ARTD``+X%3]__#]Y)``!/"0
+M`0;@5-_PD``*=$#P39``"_#"!!(;K5`X$AL&?C!_X'P=?0!U&Q)[!A(5T)``
+M!'0"\)``"O#D__X2'+20``O@5/WPY)``!/"0`0;@5-_PP@02&]=0)1(;!G\"
+M$AHGD`$$X%1_\)``"^!4_?_PY)``!/#O5+^0``OPP@02&X-0+1(;!GXP?^!\
+M'7T`=1L2>P82%="0``1T`O"0``KPD`$&X%3?\)``"^!4O_#"!"+OC?"DJ/#/
+MC/"D*,Z-\*0N_B*\``N^`"GOC?"$_ZWP(N3,^'7P".\O_^XS_NPS_.Z=[)A`
+M!?SNG?X/U?#IY,[](NWX]?#NA"#2'/ZM\'7P".\O_^TS_4`'F%`&U?#R(L.8
+M_0_5\.HBQ?#XH^`H\,7P^.6"%8)P`A6#X#CP(N#\H^#]H^#^H^#_(NSPH^WP
+MH^[PH^_P(J0E@O6"Y?`U@_6#(M"#T(+XY)-P$G0!DW`-HZ.3^'0!D_6"B(/D
+M<W0"DVA@[Z.CHX#?BH.)@N1SCQ6,%HT7Y17#E`105N45E`!`!GH`>V"`!'H`
+M>\#E%\3X5`_(:/_E%L14\$C^Y160&SV3_7P`$@]6[RO[[CKZY1?$^%0/R&C_
+MY1;$5/!(_N45D_U\`!(/5NU,8&,+NP`!"H!<>@![&N47KA9X`L,SSC/.V/DD
+M"__D/O[E%9`;/9/]?``2#U;O>`+#,\XSSMCY*_ON.OKE%ZX6>`+#,\XSSMCY
+M)`O_Y#[^Y160&SV3_7P`$@]6[4Q@!W0$*_OD.OK/Z\_.ZLXBY2X48!T48#T4
+M8%T4<`,"$80D!&`#`A&:(`T#`A&:=2X!(I``"N#_,.4#1"#PY4!%/V`#`A&:
+M=2X"$AI*$AP3KR@2&U<BD`$#X/\PYW;O1("0`0/P$AA"$AJ-$AQ%=2X#KR)^
+M`!(<J"+E0$4_<"$2%682'!,2&FT2'$42'(XP#0MU+@&O,GX`$ARH(N3U+B*0
+M``K@_S#E+$0@\!(59A(<$Q(:;1(<11(<CG4N!"+E0$4_<!`P#0IU+@&O,OX2
+M'*@BY/4N(I``!'0"\)``"O`P"3+E-$4S<`+#(H4T@H4S@\"#P(+@_J/@_X4T
+M@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O\.4T13-P`L,B$@PN4/.0``K@(.4#
+M,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZCX/^%-(*%,X.CH^#\H^#]P^^=_^Z<
+MT(+0@_"C[_#E-$4S<`+#(A(,+E#S@+6%-(*%,X/@_J/@_Q(7P-,B$ANM0`42
+M&X-01'XP?^!\'7T`=1L2>P82%="0``1T`O"0``KPY/_^$ARTD``+X%2_\%1_
+M__#DD##I\.]4_9``"_#DD``$\-()$A&;Y/4O$AO"4$A^,'_@?!U]`'4;$GL&
+M$A70D``$=`+PD``*\.3__A(<M)``"^!4O_!4_?#DD``$\/\2&B=0!'4O!R*0
+M`03@5'_PT@D2$9OD]2\B>'_D]MC]=8%,`A,E`@B0Y).C^.23HT`#]H`!\@C?
+M](`IY).C^%0')`S(PS/$5`]$(,B#0`3T5H`!1O;?Y(`+`0($"!`@0("0``KD
+M?@&38+RC_U0_,.4)5!_^Y).C8`$.SU3`)>!@J$"XY).C^N23H_CDDZ/(Q8+(
+MRL6#RO"CR,6"R,K%@\K?Z=[G@+X2&X-0+1(9(I`!!N!4W_!^,'_@?!U]`'4;
+M$GL&$A70D``$=`+PD``*\-()$A&;Y/4O(A(;UU!0$ADBD``+X%3]\.20``3P
+MD`$#=(#PD`$$X$2`\'\"$AHG4`1U+P4B?C!_X'P=?0!U&Q)[!A(5T)``!'0"
+M\)``"O#2"1(1FY`!!.!4?_#D]2\BD#`P=`+P=1$'=1+0D#`PX##@#N42%1)P
+M`A41Y1)%$7#KY1)%$7`2$ALCD"$`X&`'D#28X$0$\,,BY)`T6/"0-#)T'_!U
+M$0=U$M"0-('@9`-@#N42%1)P`A41Y1)%$7#JY1)%$7`2$ALCD"$`X&`'D#28
+MX$0$\,,BD#28X$0$\.20``'PTR(2&VY0<A(<OX4T@H4S@^#\H^##G_42[)[U
+M$=/E$I0`Y1%D@)2`0`:N$:\2@`1^`'\`CA&/$N4T)!#U@N0U,_6#X,.5$O42
+MY)41]1'#9("4@%`%Y/41]1+E-"02_^0U,_ZM$GL!$AG:D`$%="#PD`$&X$0@
+M\'4O`R+`X,#PP(/`@L#0==`(PJ^0(B[@_J/@CC/U-.4O)>`D)?6"Y#0<]8/D
+MD_YT`9/*[LKY$A`+$AR`4`+2!Q(8`A(#7Q(;F%`%KR42'.`P!1OE+W`7(`04
+M$A/RD@#"!=(+H@#D,_44KR,2%WC2K]#0T(+0@]#PT.`RD"F@X'!CD#",Y/"C
+M=,+PHW0!\*/D\)`PC.#_D"F@\)`PC.3PHW3%\*-T`?"CY/"0,(S@_Y`IH?"0
+M,(SD\*-TQ/"C=`'PH^3PD#",X/^0*:+PD#",Y/"C=,/PHW0!\*/D\)`PC."0
+M*:/P(HX6CQ>,&(T9Y/_OPYM04^4;,.`2[WP`)1G][#48C8+U@^#U'(`?Y1LP
+MX1/O?``E&?WL-1B-@O6#Y)/U'(`'Y1DO^.;U'.4;,.0/Y1<O]8+D-1;U@^4<
+M\(`&Y1<O^*8<#X"H(HP3C13O)![U@N0^]8/@_*/@3&!![R0>]8+D/O6#X/RC
+MX/6"C(/@_*/@_844@H43@^#ZH^#[T^V;ZF2`^.QD@)A`$^\D'O6"Y#[U@^#\
+MH^#.[,[_@*^M%*P3$@AC(A(;;E!/Y30D$O_D-3/^Y30D$/6"Y#4S]8/@_>3[
+M$AG:Y30D$/6"Y#4S]8/@_WX`$ARTD``*=$#PD``+X$1`__"0``IT@/!/D``+
+M\)`PZ70!\'4O!B+`X,#PP(/`@L#0==`(PJ_"C,*-T^5*E`#E290`0`CE2A5*
+M<`(52=/E3)0`Y4N4`$`(Y4P53'`"%4L2``[2C-*OT-#0@M"#T/#0X#+#[Y0$
+M[F2`E(!`#-/ME`3L9("4@%`!(L/OE/SN9("4?T`,T^V4_.QD@)1_4`$BT^^4
+M!.YD@)2`4`W#[93\[&2`E']``M,BPR+D_N_T8$%T!"[U@N0T(?6#X+3_(W0$
+M+O6"Y#0A]8/O\#`+#70(+O6"Y#0A]8/E%/"0``)T`?`BO@,*D``"=`'PY/Z`
+MP@Z`OR*.$X\4$AR_P^^5%/_NE1/-[\W\T^V4`.QD@)2`0`7.[,Z`!'X`?P',
+M[LSLD``%\)``!N_PD``$=%'PD``+X$0"\"(P!SSE+W`XP@>0(B[@_J/@CA'U
+M$I`B3N#^H^#_D"(N[O"C[_"0(D[E$?"CY1+PCC./-#`(!1(8ML((P@D2$9LB
+MY/^0,(SD\.^0&^R31("0,(WPHW0!\*/D\)`PC.#^=#8O^,;NQJ/@_N^0&^R3
+M1(!N8`$?#^_#E`E`R")_@'XIY/W\CX*.@^#[=$4M^,;KQG0$+_6"Y#[U@^#[
+M=$$M^,;KQG0(+__D/OX-O0`!#.UD!$QPSR*0(0#@Q#-4X"00]8+D-"&K@OH2
+M'#6+@HJ#[H_P$@^K?B)_,,WKS<SJS!(6,^20(0/PKRT2'.`BY/_E,"3^<"SD
+M_N[#E2Q0$G0!R.[("(`"PS/8_,]/SPZ`Z)`P-.!4X/[E)U0?;_3.3L[N\"*0
+M-,[@1`+PD#3-X%3^\)`TS>`@X_F0`1'@5"+_OR(#TX`!PU#PD`$`X%3W\)`!
+M`>!4O_`B[R0>]8+D/O6#X/RCX/O*[,HD'O6"Y#SU@^#\H^#][R0>]8+D/O6#
+M[/"C[?#.ZL[/Z\\BP.#`T,*OPH["C]/E0)0`Y3^4`$`-Y4`50'`"%3\2'.G2
+MCM*OT-#0X#(2&VY0(GXP?^!\'7T`=1L2>P82%="0``1T`O"0``KPT@D2$9OD
+M]2\BCA./%(T5ZV`)%'`;KQ42&JTB?C!_X*P3K11U&Q%[!A(5T*\5$AJM(A(8
+M?9`A`>#U*'1!)2CXYO4R=$4E*/CF]2*0(0#@8`/2#2+"#2+-[\V0`0+@,.<"
+MPR)^*G\`$@I/D`$$X$2`\)`!`N!$@/#3(I`T,.3PH_"C=!_PH^3PD`$0X"#A
+M`P"`]I`!$N`@X0,`@/8BY/]T-B_XYI`PC/#OD!OLDY`PC?"C=`'PH^3P#[\)
+MXR+D_^^0&_:3D#",\.^0&^R3D#"-\*-T`?"CY/`/OPGC(N20``7P[V`"@`)_
+M`9``!N_PD``$=%'PD``+X$0"\"*0,/#@]2J0``K@,.0.D##RX&`(D``*=!#P
+MTR+#(I`P\.#U*I``"N`PY`Z0,/+@<`B0``IT$/#3(L,BD#3.X$0"\)`TS>!4
+M_O"0`0#@5/?PD`$!X%2_\"*0``%T#O"0-)C@5/OPD#18=`'PD#`P=`3P(@($
+M"Q8,$A@D,$A@;)#(``$"`PL/"@X)#0@,[\0S,U3`_Y`!`.!4/T_PD`$"X$2`
+M\"*0`0/@,.<,=(#PD`$$X%1_\-,BPR*0``K@,.$,=`+PD``+X%3]\-,BPR*0
+M``K@,.(,=`3PD``+X%3[\-,BPR*0``K@,.8,=$#PD``+X%2_\-,BPR*0``K@
+M,.<,=(#PD``+X%1_\-,BPR*0`07@,.4,="#PD`$&X%3?\-,BPR)24U02%180
+M$4``(`!`_S\_O2@A`.4T)!'U@N0U,_6#X##F`M,BPR*0,$!T,O"C=+#PHW0!
+M\*/D\"(-Q11X%I`3:@`)&;(20AQDD#!DX/VCX/[M)>#_[C/^(I`P0'0R\*-T
+ML/"CY/"C\"*0`0#@1`CPD`$!X$1`\"(2&VY0"-()$A&;Y/4O(I``"N`PX`5T
+M`?#3(L,BD``*X##E!70@\-,BPR*0-#!T'_"CY/"C\*/P(L*OPHS"C1(`#M*O
+M(L*.CC^/0!(<Z=*.(I`P/._P[D2`H_`BD#!XX/VCX/[M_R+E2D5)<`/3@`'#
+M(N5,14MP`].``<,BP@OD]102%W@BPH]UC?5UBT$BCDF/2M*,(HY+CTS2C"(`
+M``````#"#>3U+B(`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"0\\`
+`
+end
diff --git a/sys/dev/ral/rt2661_ucode.h b/sys/contrib/dev/ral/rt2661_ucode.h
index 556ac5f..db09b02 100644
--- a/sys/dev/ral/rt2661_ucode.h
+++ b/sys/contrib/dev/ral/rt2661_ucode.h
@@ -1,8 +1,8 @@
/* $FreeBSD$ */
-/* OpenBSD: microcode.h,v 1.1 2006/01/09 20:03:40 damien Exp */
+/* $OpenBSD: microcode.h,v 1.5 2008/03/06 09:18:04 deraadt Exp $ */
/*-
- * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ * Copyright (c) 2005-2008, Ralink Technology, Corp.
* Paul Lin <paul_lin@ralinktech.com.tw>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -23,7 +23,7 @@
* RT2561S and RT2661 chipsets.
*/
-static const uint8_t rt2561_ucode[] = {
+static const unsigned char rt2561_ucode[] = {
0x02, 0x1c, 0x12, 0x02, 0x13, 0xcb, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x16, 0x0f, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x18, 0xda, 0x12, 0x1b, 0xe8,
@@ -771,7 +771,7 @@ static const uint8_t rt2561_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xac, 0x30
};
-static const uint8_t rt2561s_ucode[] = {
+static const unsigned char rt2561s_ucode[] = {
0x02, 0x1c, 0x2d, 0x02, 0x07, 0xdf, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x19, 0x43, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x1a, 0x9c, 0x12, 0x08, 0xdf,
@@ -1519,7 +1519,7 @@ static const uint8_t rt2561s_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xbb, 0x60
};
-static const uint8_t rt2661_ucode[] = {
+static const unsigned char rt2661_ucode[] = {
0x02, 0x12, 0xde, 0x02, 0x14, 0xf0, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x16, 0xe5, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x19, 0x89, 0xe5, 0x30, 0x12,
@@ -2266,3 +2266,751 @@ static const uint8_t rt2661_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0xcf
};
+
+static const unsigned char rt2860_ucode[] = {
+ 0x02, 0x00, 0xe2, 0x02, 0x02, 0xec, 0x22, 0x22, 0xff, 0xff, 0xff,
+ 0x02, 0x01, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x1e,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x01, 0x6e, 0xc0, 0xe0, 0xc0,
+ 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0xc2,
+ 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10, 0x09, 0x90, 0x04, 0x16, 0xe0,
+ 0x30, 0xe3, 0x0d, 0x74, 0x08, 0xf0, 0xe5, 0x55, 0x60, 0x06, 0x64,
+ 0x03, 0x60, 0x02, 0xd2, 0x03, 0x90, 0x04, 0x14, 0xe0, 0x20, 0xe7,
+ 0x03, 0x02, 0x00, 0xd5, 0x74, 0x80, 0xf0, 0x90, 0x70, 0x12, 0xe0,
+ 0xf5, 0x36, 0x90, 0x04, 0x04, 0xe0, 0x24, 0xcf, 0x60, 0x30, 0x14,
+ 0x60, 0x42, 0x24, 0xe2, 0x60, 0x47, 0x14, 0x60, 0x55, 0x24, 0x21,
+ 0x70, 0x60, 0xe5, 0x55, 0x24, 0xfe, 0x60, 0x07, 0x14, 0x60, 0x08,
+ 0x24, 0x02, 0x70, 0x08, 0x7d, 0x01, 0x80, 0x28, 0x7d, 0x02, 0x80,
+ 0x24, 0x90, 0x70, 0x10, 0xe0, 0xf5, 0x50, 0x85, 0x36, 0x40, 0xd2,
+ 0x01, 0x80, 0x3e, 0xe5, 0x55, 0x64, 0x03, 0x60, 0x04, 0xe5, 0x55,
+ 0x70, 0x04, 0x7d, 0x02, 0x80, 0x09, 0x85, 0x36, 0x41, 0xd2, 0x02,
+ 0x80, 0x29, 0xad, 0x55, 0xaf, 0x36, 0x12, 0x02, 0xc8, 0x80, 0x20,
+ 0x90, 0x70, 0x10, 0xe0, 0xf5, 0x47, 0x90, 0x70, 0x11, 0xe0, 0xf5,
+ 0x44, 0x12, 0x10, 0x25, 0x80, 0x06, 0x90, 0x70, 0x10, 0xe0, 0xf5,
+ 0x45, 0xe4, 0xfd, 0xaf, 0x36, 0x12, 0x02, 0xc8, 0xd2, 0x04, 0x90,
+ 0x70, 0x13, 0xe4, 0xf0, 0xd2, 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0,
+ 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7f, 0xe4, 0xf6, 0xd8,
+ 0xfd, 0x75, 0x81, 0x7d, 0x02, 0x01, 0x29, 0x02, 0x02, 0x0b, 0xe4,
+ 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01,
+ 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54,
+ 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20,
+ 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf,
+ 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x90, 0x03, 0x9a, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff,
+ 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3,
+ 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40,
+ 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93,
+ 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3,
+ 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde,
+ 0xe7, 0x80, 0xbe, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82,
+ 0xc0, 0xd0, 0xe8, 0xc0, 0xe0, 0xe9, 0xc0, 0xe0, 0xea, 0xc0, 0xe0,
+ 0xeb, 0xc0, 0xe0, 0xec, 0xc0, 0xe0, 0xed, 0xc0, 0xe0, 0xee, 0xc0,
+ 0xe0, 0xef, 0xc0, 0xe0, 0xc2, 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10,
+ 0x12, 0xd2, 0xaf, 0xd0, 0xe0, 0xff, 0xd0, 0xe0, 0xfe, 0xd0, 0xe0,
+ 0xfd, 0xd0, 0xe0, 0xfc, 0xd0, 0xe0, 0xfb, 0xd0, 0xe0, 0xfa, 0xd0,
+ 0xe0, 0xf9, 0xd0, 0xe0, 0xf8, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83,
+ 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83,
+ 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0xc2, 0xaf, 0x30, 0x45,
+ 0x03, 0x12, 0x10, 0x0c, 0x30, 0x58, 0x0a, 0xe5, 0x54, 0x60, 0x04,
+ 0x15, 0x54, 0x80, 0x02, 0xc2, 0x58, 0x30, 0x59, 0x0a, 0xe5, 0x50,
+ 0x60, 0x04, 0x15, 0x50, 0x80, 0x02, 0xc2, 0x59, 0xd5, 0x53, 0x07,
+ 0x30, 0x60, 0x04, 0x15, 0x46, 0xd2, 0x04, 0x30, 0x45, 0x03, 0x12,
+ 0x10, 0x0f, 0xc2, 0x8d, 0xd2, 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0,
+ 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x12, 0x03, 0x0e, 0x30, 0x45,
+ 0x03, 0x12, 0x10, 0x03, 0x30, 0x01, 0x06, 0x20, 0x09, 0x03, 0x12,
+ 0x10, 0x1c, 0x30, 0x02, 0x06, 0x20, 0x0a, 0x03, 0x12, 0x10, 0x1f,
+ 0x30, 0x03, 0x06, 0x20, 0x0b, 0x03, 0x12, 0x10, 0x1f, 0x30, 0x04,
+ 0x06, 0x20, 0x0c, 0x03, 0x12, 0x10, 0x22, 0x20, 0x13, 0x09, 0x20,
+ 0x11, 0x06, 0xe5, 0x2b, 0x45, 0x2c, 0x60, 0x03, 0xd3, 0x80, 0x01,
+ 0xc3, 0x92, 0xa9, 0x12, 0x03, 0x3e, 0x80, 0xbf, 0xd0, 0x83, 0xd0,
+ 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d,
+ 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83,
+ 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3,
+ 0x80, 0xdf, 0x8a, 0x83, 0x89, 0x82, 0xe4, 0x73, 0xc8, 0xef, 0xc8,
+ 0xe6, 0xfa, 0x08, 0xe6, 0x4a, 0x60, 0x0c, 0xc8, 0xef, 0xc8, 0x08,
+ 0xe6, 0x16, 0x18, 0x70, 0x01, 0x16, 0xc3, 0x22, 0xed, 0x24, 0xff,
+ 0xfd, 0xec, 0x34, 0xff, 0xc8, 0xef, 0xc8, 0xf6, 0x08, 0xc6, 0xed,
+ 0xc6, 0xd3, 0x22, 0xc2, 0x43, 0xd2, 0x45, 0xe4, 0xf5, 0x20, 0xf5,
+ 0x21, 0xf5, 0x53, 0xf5, 0x46, 0xf5, 0x2b, 0xf5, 0x2c, 0xc2, 0x42,
+ 0xf5, 0x51, 0xf5, 0x52, 0xf5, 0x55, 0x90, 0x04, 0x18, 0x74, 0x80,
+ 0xf0, 0x90, 0x04, 0x1a, 0x74, 0x08, 0xf0, 0x22, 0xef, 0xf4, 0x60,
+ 0x1f, 0xe4, 0xfe, 0x12, 0x03, 0x7d, 0xe0, 0xb4, 0xff, 0x12, 0x12,
+ 0x03, 0x7d, 0xef, 0xf0, 0x74, 0x1c, 0x2e, 0xf5, 0x82, 0xe4, 0x34,
+ 0x70, 0xf5, 0x83, 0xed, 0xf0, 0x22, 0x0e, 0xbe, 0x04, 0xe3, 0x22,
+ 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75,
+ 0xd0, 0x08, 0xc2, 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10, 0x06, 0xd2,
+ 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0,
+ 0x32, 0xc2, 0xaf, 0x12, 0x00, 0x06, 0x12, 0x02, 0xa2, 0x12, 0x03,
+ 0x27, 0xe4, 0xf5, 0x22, 0xf5, 0x47, 0x90, 0x04, 0x00, 0x74, 0x80,
+ 0xf0, 0xd2, 0xaf, 0x22, 0x75, 0x89, 0x02, 0xe4, 0xf5, 0x8c, 0xf5,
+ 0x8a, 0xf5, 0x88, 0xf5, 0xb8, 0xf5, 0xe8, 0x75, 0x90, 0x18, 0xd2,
+ 0x8c, 0x75, 0xa8, 0x05, 0x22, 0x30, 0x45, 0x03, 0x12, 0x10, 0x15,
+ 0xe5, 0x20, 0x70, 0x03, 0x20, 0x10, 0x03, 0x30, 0x11, 0x03, 0x43,
+ 0x87, 0x01, 0x22, 0xce, 0xef, 0xce, 0xee, 0x60, 0x08, 0x7f, 0xff,
+ 0x12, 0x03, 0x93, 0x1e, 0x80, 0xf5, 0x22, 0xc8, 0xef, 0xc8, 0xe6,
+ 0x60, 0x03, 0x16, 0xc3, 0x22, 0xed, 0x14, 0xf6, 0xd3, 0x22, 0xc8,
+ 0xef, 0xc8, 0xe6, 0x60, 0x06, 0x16, 0xe6, 0x24, 0xff, 0xb3, 0x22,
+ 0xc3, 0x22, 0x74, 0x14, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x70, 0xf5,
+ 0x83, 0x22, 0xef, 0x90, 0x03, 0x91, 0x93, 0x90, 0x04, 0x00, 0x73,
+ 0x0a, 0x18, 0xef, 0x60, 0x03, 0x1f, 0x80, 0xfa, 0x22, 0x01, 0x3b,
+ 0x00, 0xc1, 0xae, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc0, 0x26, 0x74, 0x04, 0xc0, 0xe0, 0xc0, 0x82, 0xc0, 0x83,
+ 0x75, 0x26, 0x0a, 0x22, 0xc0, 0x26, 0x74, 0x04, 0xc0, 0xe0, 0xc0,
+ 0x82, 0xc0, 0x83, 0x75, 0x26, 0x18, 0x22, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x10, 0x28, 0x02,
+ 0x10, 0x44, 0x02, 0x10, 0x45, 0x02, 0x12, 0xa9, 0x02, 0x12, 0xaa,
+ 0x02, 0x13, 0x45, 0x02, 0x13, 0x46, 0xc3, 0x22, 0xff, 0xff, 0x02,
+ 0x15, 0x48, 0x02, 0x16, 0x1e, 0x02, 0x13, 0xfd, 0x02, 0x13, 0x47,
+ 0x30, 0x05, 0x06, 0x20, 0x0d, 0x03, 0x12, 0x16, 0x9b, 0x30, 0x06,
+ 0x06, 0x20, 0x0e, 0x03, 0x12, 0x18, 0xbd, 0x30, 0x07, 0x06, 0x20,
+ 0x0f, 0x03, 0x12, 0x18, 0xfe, 0x22, 0x22, 0x90, 0x04, 0x14, 0xe0,
+ 0x20, 0xe7, 0x03, 0x02, 0x12, 0x9c, 0x90, 0x70, 0x12, 0xe0, 0xf5,
+ 0x56, 0x90, 0x04, 0x04, 0xe0, 0x12, 0x02, 0x4f, 0x10, 0x87, 0x31,
+ 0x10, 0xb2, 0x51, 0x10, 0xbd, 0x52, 0x10, 0xbd, 0x53, 0x10, 0xbd,
+ 0x54, 0x11, 0x71, 0x60, 0x10, 0xfe, 0x61, 0x11, 0xbf, 0x62, 0x10,
+ 0xfe, 0x63, 0x11, 0xe8, 0x70, 0x12, 0x12, 0x71, 0x12, 0x3c, 0x72,
+ 0x12, 0x6e, 0x80, 0x00, 0x00, 0x12, 0x9c, 0x20, 0x02, 0x03, 0x30,
+ 0x03, 0x1d, 0x7d, 0x02, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04,
+ 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56,
+ 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x85, 0x56,
+ 0x41, 0xd2, 0x02, 0x02, 0x12, 0x9c, 0x90, 0x70, 0x11, 0xe0, 0x24,
+ 0xff, 0x92, 0x47, 0x02, 0x12, 0x9c, 0x90, 0x04, 0x04, 0xe0, 0x25,
+ 0xe0, 0x24, 0x5d, 0xf5, 0x57, 0x90, 0x70, 0x10, 0xe0, 0xff, 0x74,
+ 0x47, 0x25, 0x57, 0xf8, 0xc6, 0xef, 0xc6, 0x90, 0x70, 0x11, 0xe0,
+ 0xff, 0x74, 0x48, 0x25, 0x57, 0xf8, 0xc6, 0xef, 0xc6, 0xe4, 0xfd,
+ 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0,
+ 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x70, 0x03, 0x02,
+ 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x1f,
+ 0xf5, 0x62, 0xe0, 0x54, 0x80, 0xf5, 0x64, 0x90, 0x70, 0x10, 0xe0,
+ 0xff, 0x7e, 0x00, 0x90, 0x04, 0x04, 0xe0, 0xb4, 0x61, 0x04, 0x7d,
+ 0x00, 0x80, 0x02, 0x7d, 0x07, 0xef, 0xc8, 0xed, 0xc8, 0x08, 0x80,
+ 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xf5, 0x61, 0x8e,
+ 0x60, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x60, 0x24, 0xff, 0x92, 0x2d,
+ 0xe0, 0x54, 0x60, 0xc4, 0x13, 0x54, 0x07, 0x14, 0xf5, 0x63, 0x75,
+ 0x65, 0x80, 0x75, 0x66, 0x23, 0x75, 0x67, 0x06, 0x75, 0x68, 0x18,
+ 0x75, 0x69, 0x15, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90,
+ 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5,
+ 0x56, 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90,
+ 0x70, 0x11, 0xe0, 0x54, 0x1f, 0xf5, 0x6e, 0x90, 0x70, 0x10, 0xe0,
+ 0xf5, 0x6b, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x60, 0x24, 0xff, 0x92,
+ 0x2c, 0xe0, 0x54, 0x60, 0xc4, 0x13, 0x54, 0x07, 0x14, 0xf5, 0x6f,
+ 0x75, 0x71, 0x40, 0x75, 0x72, 0x24, 0x75, 0x73, 0x05, 0x75, 0x74,
+ 0x17, 0x75, 0x75, 0xe7, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8,
+ 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0,
+ 0xe5, 0x56, 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f,
+ 0x90, 0x70, 0x10, 0xe0, 0x60, 0x04, 0xd2, 0x1a, 0x80, 0x02, 0xd2,
+ 0x22, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14,
+ 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4,
+ 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90, 0x70, 0x10,
+ 0xe0, 0xfe, 0x90, 0x70, 0x11, 0xe0, 0xfd, 0xed, 0xf8, 0xe6, 0xf5,
+ 0x57, 0xfd, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74,
+ 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x70,
+ 0x03, 0x02, 0x12, 0x9c, 0x80, 0x7d, 0x90, 0x70, 0x10, 0xe0, 0xfe,
+ 0x90, 0x70, 0x11, 0xe0, 0xfd, 0xed, 0xf5, 0x82, 0x8e, 0x83, 0xe0,
+ 0xf5, 0x57, 0xfd, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14,
+ 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4,
+ 0x60, 0x62, 0x80, 0x53, 0xe4, 0xf5, 0x7a, 0x75, 0x7b, 0x01, 0xf5,
+ 0x6a, 0xc2, 0x2d, 0xf5, 0x23, 0xf5, 0x3b, 0xd2, 0x2e, 0xf5, 0x77,
+ 0xc2, 0x2c, 0xf5, 0x24, 0x75, 0x76, 0x18, 0xad, 0x57, 0xaf, 0x56,
+ 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90,
+ 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x60, 0x30, 0x80, 0x21, 0x90,
+ 0x70, 0x10, 0xe0, 0x24, 0xff, 0x92, 0x4a, 0xd2, 0x05, 0xad, 0x57,
+ 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0,
+ 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x60, 0x0d, 0x90,
+ 0x70, 0x24, 0xe0, 0x44, 0x10, 0xf0, 0x90, 0x02, 0x2c, 0x74, 0xff,
+ 0xf0, 0xe5, 0x23, 0x24, 0xff, 0x92, 0x06, 0xe5, 0x24, 0x24, 0xff,
+ 0x92, 0x07, 0x22, 0x22, 0xe5, 0x76, 0x60, 0x04, 0x15, 0x76, 0x80,
+ 0x03, 0x75, 0x76, 0x18, 0xe5, 0x76, 0x70, 0x43, 0x30, 0x2d, 0x0d,
+ 0x7f, 0x6c, 0xad, 0x61, 0xac, 0x60, 0x12, 0x02, 0x7b, 0x50, 0x02,
+ 0xd2, 0x18, 0x30, 0x2c, 0x0b, 0x7f, 0x7c, 0xad, 0x6b, 0x12, 0x03,
+ 0x61, 0x50, 0x02, 0xd2, 0x20, 0xe5, 0x77, 0x60, 0x09, 0x7f, 0x7a,
+ 0x12, 0x03, 0x6f, 0x50, 0x02, 0xd2, 0x21, 0xe5, 0x6a, 0x60, 0x09,
+ 0x7f, 0x7a, 0x12, 0x03, 0x6f, 0x50, 0x02, 0xd2, 0x19, 0xe5, 0x77,
+ 0x60, 0x07, 0xe5, 0x7b, 0xb4, 0x03, 0x02, 0xd2, 0x23, 0xe5, 0x23,
+ 0x24, 0xff, 0x92, 0x06, 0xe5, 0x24, 0x24, 0xff, 0x92, 0x07, 0x90,
+ 0x70, 0x60, 0xe5, 0x7b, 0xf0, 0xa3, 0xe5, 0x6a, 0xf0, 0xa3, 0xe5,
+ 0x20, 0xf0, 0xa3, 0xe5, 0x23, 0xf0, 0xe5, 0x6c, 0xa3, 0xf0, 0xa3,
+ 0xe5, 0x6d, 0xf0, 0xa2, 0x2d, 0xe4, 0x33, 0xa3, 0xf0, 0xa3, 0xe5,
+ 0x62, 0xf0, 0x90, 0x70, 0x6b, 0xe5, 0x76, 0xf0, 0xe5, 0x53, 0x70,
+ 0x0e, 0xe5, 0x4f, 0x45, 0x4e, 0x60, 0x08, 0xe5, 0x4f, 0x15, 0x4f,
+ 0x70, 0x02, 0x15, 0x4e, 0x22, 0x22, 0x22, 0xc2, 0x4b, 0xc2, 0x4c,
+ 0xe5, 0x44, 0x12, 0x02, 0x4f, 0x13, 0x69, 0x00, 0x13, 0xc4, 0x04,
+ 0x13, 0xc0, 0x08, 0x13, 0xa8, 0x10, 0x13, 0x74, 0x20, 0x13, 0x86,
+ 0x60, 0x13, 0x91, 0xa0, 0x00, 0x00, 0x13, 0xc6, 0x85, 0x48, 0x43,
+ 0x85, 0x4a, 0x42, 0x85, 0x4c, 0x5e, 0x80, 0x52, 0xe5, 0x48, 0xc4,
+ 0x54, 0x0f, 0xf5, 0x43, 0xe5, 0x4a, 0xc4, 0x54, 0x0f, 0xf5, 0x42,
+ 0xe5, 0x4c, 0x80, 0x1b, 0x85, 0x49, 0x43, 0x85, 0x4b, 0x42, 0x85,
+ 0x4d, 0x5e, 0x80, 0x35, 0xe5, 0x49, 0xc4, 0x54, 0x0f, 0xf5, 0x43,
+ 0xe5, 0x4b, 0xc4, 0x54, 0x0f, 0xf5, 0x42, 0xe5, 0x4d, 0xc4, 0x54,
+ 0x0f, 0xf5, 0x5e, 0x80, 0x1e, 0xe5, 0x47, 0xb4, 0x04, 0x06, 0x53,
+ 0x5e, 0xfb, 0x75, 0x42, 0x09, 0xe5, 0x47, 0xb4, 0x05, 0x0e, 0x43,
+ 0x5e, 0x04, 0x75, 0x42, 0x09, 0x80, 0x06, 0xd2, 0x4b, 0x80, 0x02,
+ 0xd2, 0x4c, 0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0xf5, 0x27, 0xe5, 0x42,
+ 0xc4, 0x54, 0xf0, 0xff, 0xe5, 0x43, 0x54, 0x0f, 0x4f, 0xf5, 0x5f,
+ 0x90, 0x70, 0x44, 0xf0, 0xa3, 0xe5, 0x5e, 0xf0, 0xa3, 0xe5, 0x4a,
+ 0xf0, 0xa3, 0xe5, 0x48, 0xf0, 0xa3, 0xe5, 0x4c, 0xf0, 0xa3, 0xe5,
+ 0x44, 0xf0, 0xa3, 0xe5, 0x42, 0xf0, 0xa3, 0xe5, 0x43, 0xf0, 0xd2,
+ 0x60, 0x22, 0x90, 0x70, 0x40, 0xe0, 0x04, 0xf0, 0x90, 0x70, 0x42,
+ 0xe5, 0x47, 0xf0, 0xe5, 0x47, 0x60, 0x10, 0x24, 0xc0, 0x70, 0x03,
+ 0x12, 0x15, 0x28, 0x12, 0x14, 0x1e, 0xc2, 0xaf, 0xc2, 0x04, 0xd2,
+ 0xaf, 0x22, 0xc2, 0xaf, 0x90, 0x04, 0x14, 0xe0, 0x54, 0x0e, 0x60,
+ 0x14, 0xe5, 0x47, 0xb4, 0x02, 0x0b, 0xe5, 0x44, 0xb4, 0x20, 0x06,
+ 0x75, 0x4e, 0x08, 0x75, 0x4f, 0x00, 0xd2, 0x28, 0x80, 0x08, 0xe5,
+ 0x4e, 0x45, 0x4f, 0x24, 0xff, 0x92, 0x28, 0xd2, 0xaf, 0x90, 0x04,
+ 0x14, 0xe0, 0xa2, 0xe4, 0x92, 0x29, 0x74, 0x1e, 0xf0, 0xe5, 0x5f,
+ 0x54, 0x0f, 0xf5, 0x2d, 0xe5, 0x27, 0x70, 0x13, 0x30, 0x28, 0x05,
+ 0xe5, 0x5f, 0x20, 0xe5, 0x0b, 0x30, 0x29, 0x19, 0xe5, 0x5f, 0x54,
+ 0x30, 0xff, 0xbf, 0x30, 0x11, 0xe5, 0x27, 0x70, 0x05, 0x75, 0x27,
+ 0x0c, 0x80, 0x02, 0x15, 0x27, 0xd2, 0x6c, 0xd2, 0x6d, 0x80, 0x0f,
+ 0xe5, 0x5f, 0x30, 0xe6, 0x06, 0xc2, 0x6c, 0xd2, 0x6d, 0x80, 0x04,
+ 0xd2, 0x6c, 0xc2, 0x6d, 0xe5, 0x47, 0x64, 0x03, 0x70, 0x21, 0x30,
+ 0x4b, 0x06, 0xc2, 0x6c, 0xd2, 0x6d, 0x80, 0x18, 0xe5, 0x27, 0x70,
+ 0x03, 0x30, 0x4c, 0x11, 0xc2, 0x4c, 0xe5, 0x27, 0x70, 0x05, 0x75,
+ 0x27, 0x07, 0x80, 0x02, 0x15, 0x27, 0xd2, 0x6c, 0xd2, 0x6d, 0x90,
+ 0x70, 0x46, 0xe5, 0x2d, 0xf0, 0x20, 0x69, 0x07, 0xe5, 0x5e, 0x20,
+ 0xe0, 0x02, 0xb2, 0x68, 0x20, 0x6b, 0x07, 0xe5, 0x5e, 0x20, 0xe1,
+ 0x02, 0xb2, 0x6a, 0x20, 0x6d, 0x07, 0xe5, 0x5e, 0x20, 0xe2, 0x02,
+ 0xb2, 0x6c, 0x90, 0x70, 0x47, 0xe5, 0x2d, 0xf0, 0x75, 0x2e, 0x40,
+ 0x20, 0x69, 0x04, 0xa2, 0x68, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x68,
+ 0x04, 0xa2, 0xe3, 0x80, 0x01, 0x33, 0x92, 0x73, 0x92, 0x72, 0x20,
+ 0x6b, 0x04, 0xa2, 0x6a, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x6a, 0x04,
+ 0xa2, 0xe3, 0x80, 0x01, 0x33, 0x92, 0x75, 0x92, 0x74, 0x20, 0x6d,
+ 0x04, 0xa2, 0x6c, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x6c, 0x04, 0xa2,
+ 0xe3, 0x80, 0x01, 0x33, 0x92, 0x71, 0x92, 0x70, 0x90, 0x10, 0x2f,
+ 0xe5, 0x2e, 0xf0, 0x22, 0xe4, 0x90, 0x02, 0x29, 0xf0, 0x30, 0x47,
+ 0x04, 0xaf, 0x45, 0x80, 0x04, 0xe5, 0x45, 0xf4, 0xff, 0x90, 0x02,
+ 0x28, 0xef, 0xf0, 0x22, 0x8f, 0x50, 0xd2, 0x59, 0x22, 0x8f, 0x54,
+ 0xd2, 0x58, 0x22, 0xe4, 0xf5, 0x38, 0xc2, 0xaf, 0xe5, 0x51, 0x14,
+ 0x60, 0x45, 0x14, 0x60, 0x61, 0x24, 0x02, 0x60, 0x03, 0x02, 0x16,
+ 0x02, 0xd2, 0x59, 0x75, 0x55, 0x01, 0x90, 0x02, 0x08, 0xe0, 0x54,
+ 0xfe, 0xf0, 0xe0, 0x20, 0xe1, 0x22, 0x90, 0x04, 0x34, 0xe0, 0xb4,
+ 0x02, 0x1b, 0xa3, 0xe0, 0xb4, 0x02, 0x16, 0xa3, 0xe0, 0xb4, 0x02,
+ 0x11, 0x7f, 0x20, 0x12, 0x15, 0x3e, 0x90, 0x10, 0x04, 0xe0, 0x54,
+ 0xf3, 0xf0, 0x75, 0x51, 0x01, 0x80, 0x74, 0xe5, 0x50, 0x70, 0x05,
+ 0x75, 0x38, 0x03, 0x80, 0x6b, 0x90, 0x12, 0x00, 0xe0, 0x54, 0x03,
+ 0x70, 0x11, 0x7f, 0x20, 0x12, 0x15, 0x3e, 0x90, 0x02, 0x08, 0xe0,
+ 0x54, 0xfb, 0xf0, 0x75, 0x51, 0x02, 0x80, 0x52, 0xe5, 0x50, 0x70,
+ 0x02, 0x80, 0x47, 0x90, 0x02, 0x08, 0xe0, 0x20, 0xe3, 0x3c, 0x90,
+ 0x04, 0x37, 0xe0, 0x64, 0x22, 0x70, 0x34, 0x90, 0x12, 0x04, 0x74,
+ 0x0a, 0xf0, 0x90, 0x13, 0x28, 0xe0, 0x54, 0xf0, 0xf0, 0xa3, 0xe0,
+ 0x54, 0xf0, 0xf0, 0xa3, 0xe0, 0x54, 0xfa, 0xf0, 0x90, 0x04, 0x01,
+ 0xe0, 0x44, 0x10, 0xf0, 0xe0, 0x54, 0xf9, 0xf0, 0x90, 0x12, 0x04,
+ 0xe0, 0x44, 0x04, 0xf0, 0x75, 0x38, 0x01, 0x75, 0x55, 0x02, 0xe4,
+ 0xf5, 0x51, 0x80, 0x09, 0xe5, 0x50, 0x70, 0x05, 0x75, 0x38, 0x03,
+ 0xf5, 0x51, 0xe5, 0x38, 0x60, 0x15, 0xc2, 0x01, 0xe4, 0xf5, 0x51,
+ 0xc2, 0x59, 0xad, 0x38, 0xaf, 0x40, 0x12, 0x02, 0xc8, 0xe5, 0x38,
+ 0xb4, 0x03, 0x02, 0xd2, 0x03, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0x30,
+ 0x01, 0x0e, 0xe4, 0xf5, 0x51, 0xc2, 0x59, 0xc2, 0x01, 0x7d, 0x02,
+ 0xaf, 0x40, 0x12, 0x02, 0xc8, 0xe5, 0x52, 0x14, 0x60, 0x17, 0x04,
+ 0x70, 0x5d, 0x90, 0x12, 0x04, 0xe0, 0x54, 0xfb, 0xf0, 0x7f, 0x20,
+ 0x12, 0x15, 0x43, 0x75, 0x52, 0x01, 0x75, 0x55, 0x03, 0x80, 0x49,
+ 0xe5, 0x54, 0x70, 0x45, 0x90, 0x04, 0x01, 0xe0, 0x44, 0x0e, 0xf0,
+ 0xe0, 0x54, 0xef, 0xf0, 0x90, 0x13, 0x28, 0xe0, 0x44, 0x0f, 0xf0,
+ 0xa3, 0xe0, 0x44, 0x0f, 0xf0, 0xa3, 0xe0, 0x44, 0x05, 0xf0, 0x90,
+ 0x12, 0x04, 0x74, 0x03, 0xf0, 0x90, 0x02, 0x08, 0xe0, 0x44, 0x05,
+ 0xf0, 0x90, 0x10, 0x04, 0xe0, 0x44, 0x0c, 0xf0, 0xe4, 0xf5, 0x52,
+ 0xf5, 0x55, 0x30, 0x02, 0x0b, 0xc2, 0x02, 0x7d, 0x01, 0xaf, 0x41,
+ 0x12, 0x02, 0xc8, 0x80, 0x02, 0xc2, 0x03, 0xd2, 0xaf, 0x22, 0x22,
+ 0x22, 0xc2, 0xaf, 0xe5, 0x6a, 0x70, 0x04, 0xe5, 0x77, 0x60, 0x03,
+ 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x28, 0x90, 0x70, 0x2a, 0xe0, 0x30,
+ 0xe1, 0x2c, 0xe0, 0x13, 0x92, 0x29, 0x90, 0x70, 0x29, 0xe0, 0xff,
+ 0x90, 0x70, 0x28, 0xe0, 0xfd, 0xa2, 0x28, 0x92, 0x2a, 0xa2, 0x29,
+ 0x92, 0x2b, 0x12, 0x16, 0xe1, 0x50, 0x03, 0x20, 0x28, 0x05, 0x12,
+ 0x17, 0x61, 0x80, 0x07, 0x90, 0x70, 0x2a, 0xe0, 0x54, 0xfd, 0xf0,
+ 0xc2, 0x05, 0xd2, 0xaf, 0x22, 0x30, 0x2b, 0x49, 0x30, 0x2a, 0x46,
+ 0xbf, 0x41, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x32, 0xf0, 0x80, 0x39,
+ 0xbf, 0x42, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x33, 0xf0, 0x80, 0x2e,
+ 0xbf, 0x45, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x35, 0xf0, 0x80, 0x23,
+ 0xbf, 0x46, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x30, 0xf0, 0x80, 0x18,
+ 0xbf, 0x49, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x31, 0xf0, 0x80, 0x0d,
+ 0xbf, 0x71, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x2f, 0xf0, 0x80, 0x02,
+ 0xc3, 0x22, 0xd3, 0x22, 0x20, 0x2b, 0x2f, 0xbf, 0x41, 0x04, 0x8d,
+ 0x32, 0x80, 0x25, 0xbf, 0x42, 0x04, 0x8d, 0x33, 0x80, 0x1e, 0xbf,
+ 0x45, 0x04, 0x8d, 0x35, 0x80, 0x17, 0xbf, 0x46, 0x04, 0x8d, 0x30,
+ 0x80, 0x10, 0xbf, 0x49, 0x04, 0x8d, 0x31, 0x80, 0x09, 0xbf, 0x71,
+ 0x04, 0x8d, 0x2f, 0x80, 0x02, 0xc3, 0x22, 0xa2, 0x2a, 0x22, 0xc3,
+ 0x22, 0x90, 0x70, 0x28, 0xe0, 0x90, 0x10, 0x1c, 0xf0, 0x90, 0x70,
+ 0x29, 0xe0, 0x90, 0x10, 0x1d, 0xf0, 0x90, 0x70, 0x2a, 0xe0, 0x90,
+ 0x10, 0x1e, 0xf0, 0x90, 0x10, 0x1c, 0xe0, 0xf5, 0x38, 0x90, 0x10,
+ 0x1e, 0xe0, 0x20, 0xe1, 0xf3, 0x90, 0x10, 0x1c, 0xe0, 0x90, 0x70,
+ 0x28, 0xf0, 0x90, 0x10, 0x1d, 0xe0, 0x90, 0x70, 0x29, 0xf0, 0x90,
+ 0x10, 0x1e, 0xe0, 0x90, 0x70, 0x2a, 0xf0, 0x30, 0x4a, 0x0d, 0x90,
+ 0x70, 0x24, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x02, 0x2c, 0x74, 0xff,
+ 0xf0, 0x22, 0xc2, 0xaf, 0x90, 0x10, 0x1c, 0xed, 0xf0, 0xa3, 0xef,
+ 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x90, 0x10, 0x1c, 0xe0, 0xf5, 0x3a,
+ 0x90, 0x10, 0x1e, 0xe0, 0x20, 0xe1, 0xf3, 0xd2, 0xaf, 0x22, 0xc2,
+ 0xaf, 0x90, 0x10, 0x1d, 0xef, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x90,
+ 0x10, 0x1c, 0xe0, 0xff, 0x90, 0x10, 0x1e, 0xe0, 0x20, 0xe1, 0xf4,
+ 0xd2, 0xaf, 0x22, 0x90, 0x13, 0x44, 0xe0, 0xf5, 0x34, 0xe4, 0xf0,
+ 0x90, 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x44, 0x02, 0xf0,
+ 0x7d, 0x1d, 0x7f, 0x41, 0x12, 0x17, 0xaf, 0x7d, 0x60, 0x0f, 0x12,
+ 0x17, 0xaf, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef, 0x44, 0x20, 0xfd,
+ 0x7f, 0x71, 0x02, 0x17, 0xaf, 0xe4, 0x90, 0x02, 0x29, 0xf0, 0x90,
+ 0x02, 0x28, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x13, 0x44, 0xe0, 0xf5,
+ 0x34, 0xe4, 0xf0, 0x7f, 0x04, 0x12, 0x17, 0xcd, 0xef, 0x54, 0x18,
+ 0x60, 0x1d, 0xe5, 0x78, 0x24, 0x02, 0xf8, 0xe6, 0x60, 0x07, 0x90,
+ 0x70, 0x2c, 0xe0, 0xfd, 0x80, 0x05, 0x90, 0x70, 0x2d, 0xe0, 0xfd,
+ 0x7f, 0x42, 0x12, 0x17, 0xaf, 0x7d, 0x30, 0x80, 0x1b, 0xe5, 0x78,
+ 0x24, 0x02, 0xf8, 0xe6, 0x60, 0x07, 0x90, 0x70, 0x2e, 0xe0, 0xfd,
+ 0x80, 0x05, 0x90, 0x70, 0x2f, 0xe0, 0xfd, 0x7f, 0x42, 0x12, 0x17,
+ 0xaf, 0x7d, 0x28, 0x7f, 0x45, 0x12, 0x17, 0xaf, 0x0f, 0x12, 0x17,
+ 0xaf, 0x7d, 0x08, 0x7f, 0x49, 0x12, 0x17, 0xaf, 0x22, 0xe4, 0x90,
+ 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x54, 0xfd, 0xf0, 0x90,
+ 0x13, 0x44, 0xe5, 0x34, 0xf0, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef,
+ 0x54, 0xdf, 0xfd, 0x7f, 0x71, 0x12, 0x17, 0xaf, 0xad, 0x32, 0x7f,
+ 0x41, 0x12, 0x17, 0xaf, 0xad, 0x33, 0x0f, 0x12, 0x17, 0xaf, 0xad,
+ 0x35, 0x7f, 0x45, 0x12, 0x17, 0xaf, 0xad, 0x30, 0x0f, 0x12, 0x17,
+ 0xaf, 0xad, 0x31, 0x7f, 0x49, 0x02, 0x17, 0xaf, 0xe5, 0x6a, 0x14,
+ 0x60, 0x16, 0x04, 0x70, 0x31, 0x90, 0x70, 0x6c, 0xe0, 0x04, 0xf0,
+ 0x7f, 0x06, 0x12, 0x1b, 0x95, 0x50, 0x24, 0x75, 0x6a, 0x01, 0x75,
+ 0x78, 0x62, 0x90, 0x70, 0x6d, 0xe0, 0x04, 0xf0, 0x12, 0x19, 0x60,
+ 0x40, 0x13, 0x90, 0x70, 0x24, 0xe0, 0x44, 0x04, 0xf0, 0x90, 0x02,
+ 0x2c, 0x74, 0xff, 0xf0, 0xe4, 0xf5, 0x6a, 0x12, 0x1b, 0xae, 0xc2,
+ 0xaf, 0x53, 0x20, 0xbf, 0xd2, 0xaf, 0x22, 0xe5, 0x77, 0x14, 0x60,
+ 0x10, 0x04, 0x70, 0x52, 0x7f, 0x07, 0x12, 0x1b, 0x95, 0x50, 0x4b,
+ 0x75, 0x77, 0x01, 0x75, 0x78, 0x6e, 0x12, 0x19, 0x60, 0x40, 0x40,
+ 0xe5, 0x79, 0x24, 0x05, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xfe,
+ 0x24, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x70, 0xf5, 0x83, 0xe0, 0xfd,
+ 0xef, 0x54, 0x07, 0xff, 0x74, 0x01, 0xc8, 0xef, 0xc8, 0x08, 0x80,
+ 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4d, 0xff, 0x74, 0x24, 0x2e, 0xf5,
+ 0x82, 0xe4, 0x34, 0x70, 0xf5, 0x83, 0xef, 0xf0, 0x90, 0x02, 0x2c,
+ 0x74, 0xff, 0xf0, 0xe4, 0xf5, 0x77, 0x12, 0x1b, 0xae, 0xc2, 0xaf,
+ 0x53, 0x20, 0x7f, 0xd2, 0xaf, 0x22, 0xe5, 0x78, 0x24, 0x04, 0xf8,
+ 0xe6, 0xf5, 0x38, 0x90, 0x70, 0x68, 0xf0, 0xf8, 0xe6, 0xff, 0xa3,
+ 0xf0, 0x90, 0x70, 0x6f, 0xe0, 0x04, 0xf0, 0xe5, 0x7b, 0x24, 0xfe,
+ 0x70, 0x03, 0x02, 0x1a, 0x26, 0x14, 0x70, 0x03, 0x02, 0x1a, 0x78,
+ 0x24, 0x02, 0x60, 0x03, 0x02, 0x1b, 0x94, 0xe4, 0xf5, 0x79, 0xef,
+ 0x20, 0xe0, 0x03, 0x02, 0x1a, 0x24, 0xa8, 0x38, 0xe6, 0x54, 0xfe,
+ 0xf6, 0xe5, 0x78, 0x04, 0xf8, 0xe6, 0x60, 0x4d, 0xc2, 0xaf, 0x75,
+ 0x7a, 0x05, 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2, 0xaf, 0xe5,
+ 0x78, 0x04, 0xf8, 0xe6, 0x64, 0x02, 0x70, 0x1c, 0x90, 0x77, 0x85,
+ 0xf0, 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0x90, 0x04, 0x10, 0xf0,
+ 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0xff, 0x90, 0x04, 0x10, 0xe0,
+ 0x5f, 0x70, 0xf2, 0x90, 0x77, 0x85, 0x74, 0xfe, 0xf0, 0xa8, 0x38,
+ 0xe6, 0x54, 0xfb, 0xf6, 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0x90,
+ 0x04, 0x10, 0xf0, 0x75, 0x7b, 0x02, 0x80, 0x33, 0xc2, 0xaf, 0xa8,
+ 0x78, 0xe6, 0xf5, 0x7a, 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2,
+ 0xaf, 0x90, 0x10, 0x04, 0xe0, 0x54, 0xfb, 0xf0, 0xe5, 0x6a, 0x60,
+ 0x06, 0x90, 0x17, 0x04, 0xe0, 0xf5, 0x39, 0xe5, 0x78, 0x24, 0x06,
+ 0xf8, 0xe6, 0xfe, 0x08, 0xe6, 0xca, 0xee, 0xca, 0xf9, 0x12, 0x02,
+ 0x75, 0x75, 0x7b, 0x03, 0xd3, 0x22, 0xa8, 0x38, 0xe6, 0x30, 0xe2,
+ 0x3b, 0x54, 0xfb, 0xf6, 0xc2, 0xaf, 0xa8, 0x78, 0xe6, 0xf5, 0x7a,
+ 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2, 0xaf, 0x90, 0x10, 0x04,
+ 0xe0, 0x54, 0xfb, 0xf0, 0xe5, 0x6a, 0x60, 0x06, 0x90, 0x17, 0x04,
+ 0xe0, 0xf5, 0x39, 0xe5, 0x78, 0x24, 0x06, 0xf8, 0xe6, 0xfe, 0x08,
+ 0xe6, 0xca, 0xee, 0xca, 0xf9, 0x12, 0x02, 0x75, 0xe4, 0xf5, 0x7d,
+ 0x75, 0x7b, 0x03, 0xd3, 0x22, 0xa8, 0x38, 0xe6, 0x30, 0xe1, 0x09,
+ 0x54, 0xfd, 0xf6, 0xe4, 0xf5, 0x79, 0x02, 0x1b, 0x8e, 0xd3, 0x22,
+ 0xe5, 0x77, 0x70, 0x03, 0x02, 0x1b, 0x6e, 0xa8, 0x38, 0xe6, 0x20,
+ 0xe3, 0x03, 0x02, 0x1b, 0x6e, 0x54, 0xf7, 0xf6, 0x7f, 0x73, 0x12,
+ 0x17, 0xcd, 0xef, 0x64, 0x02, 0x60, 0x03, 0x02, 0x1b, 0x28, 0x90,
+ 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x44, 0x08, 0xf0, 0x7f,
+ 0x76, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x75, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x74, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x02, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x73, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x03, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x71, 0x12, 0x17, 0xcd, 0xef, 0x54, 0xdf, 0xfd, 0x7f, 0x71, 0x12,
+ 0x17, 0xaf, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef, 0x44, 0x20, 0xfd,
+ 0x7f, 0x71, 0x12, 0x17, 0xaf, 0x75, 0x79, 0xff, 0xe4, 0x90, 0x02,
+ 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x44,
+ 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24, 0x00, 0xf5, 0x82, 0xe4,
+ 0x34, 0x71, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x7d, 0x25, 0xe0, 0x25,
+ 0xe0, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xe4,
+ 0xf0, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24, 0x02, 0xf5, 0x82,
+ 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x7d, 0x25, 0xe0,
+ 0x25, 0xe0, 0x24, 0x03, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83,
+ 0xe4, 0xf0, 0x05, 0x7d, 0xa8, 0x38, 0xe6, 0xff, 0x30, 0xe1, 0x1e,
+ 0x54, 0xfd, 0xf6, 0xe5, 0x79, 0xf4, 0x60, 0x07, 0x7f, 0x73, 0x12,
+ 0x17, 0xcd, 0x8f, 0x79, 0x12, 0x18, 0x7c, 0x90, 0x10, 0x04, 0xe0,
+ 0x44, 0x04, 0xf0, 0x75, 0x7b, 0x01, 0xc3, 0x22, 0xd3, 0x22, 0xa2,
+ 0x2e, 0xe4, 0x33, 0x90, 0x70, 0x6a, 0xf0, 0xc2, 0xaf, 0x30, 0x2e,
+ 0x06, 0xc2, 0x2e, 0xd2, 0xaf, 0xd3, 0x22, 0x8f, 0x3b, 0xd2, 0xaf,
+ 0xc3, 0x22, 0xc2, 0xaf, 0xe5, 0x3b, 0x60, 0x12, 0xff, 0x74, 0x01,
+ 0xc8, 0xef, 0xc8, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42,
+ 0x20, 0xe4, 0xf5, 0x3b, 0xd2, 0x2e, 0xd2, 0xaf, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x65, 0x30
+};
diff --git a/sys/contrib/dev/ral/rt2860.fw.uu b/sys/contrib/dev/ral/rt2860.fw.uu
new file mode 100644
index 0000000..14b3f7b
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2860.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2860.fw
+M`@#B`@+L(B+___\"`;W______P(`'O______`@%NP.#`\,"#P(+`T'70&,*O
+M,$4#$A`)D`06X##C#70(\.558`9D`V`"T@.0!!3@(.<#`@#5=(#PD'`2X/4V
+MD`0$X"3/8#`48$(DXF!'%&!5)"%P8.55)/Y@!Q1@""0"<`A]`8`H?0*`))!P
+M$.#U4(4V0-(!@#[E560#8`3E57`$?0*`"84V0=("@"FM5:\V$@+(@""0<!#@
+M]4>0<!'@]402$"6`!I!P$.#U1>3]KS82`LC2!)!P$^3PTJ_0T-""T(/0\-#@
+M,GA_Y/;8_76!?0(!*0(""^23H_CDDZ-``_:``?((W_2`*>23H_A4!R0,R,,S
+MQ%0/1"#(@T`$]%:``4;VW^2`"P$"!`@0($"`D`.:Y'X!DV"\H_]4/S#E"50?
+M_N23HV`!#L]4P"7@8*A`N.23H_KDDZ/XY).CR,6"R,K%@\KPH\C%@LC*Q8/*
+MW^G>YX"^P.#`\,"#P(+`T.C`X.G`X.K`X.O`X.S`X.W`X.[`X._`X,*O,$4#
+M$A`2TJ_0X/_0X/[0X/W0X/S0X/O0X/K0X/G0X/C0T-""T(/0\-#@,L#@P/#`
+M@\""P-!UT!#"KS!%`Q(0##!8"N548`055(`"PE@P60KE4&`$%5"``L)9U5,'
+M,&`$%4;2!#!%`Q(0#\*-TJ_0T-""T(/0\-#@,A(##C!%`Q(0`S`!!B`)`Q(0
+M'#`"!B`*`Q(0'S`#!B`+`Q(0'S`$!B`,`Q(0(B`3"2`1!N4K12Q@`].``<.2
+MJ1(#/H"_T(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3:&#OHZ.C@-^*
+M@XF"Y'/([\CF^@CF2F`,R._(".86&'`!%L,B[23__>PT_\COR/8(QNW&TR+"
+M0])%Y/4@]2'U4_5&]2OU+,)"]5'U4O55D`08=(#PD`0:=`CP(N_T8!_D_A(#
+M?>"T_Q(2`WWO\'0<+O6"Y#1P]8/M\"(.O@3C(L#@P/#`@\""P-!UT`C"KS!%
+M`Q(0!M*OT-#0@M"#T/#0X#+"KQ(`!A("HA(#)^3U(O5'D`0`=(#PTJ\B=8D"
+MY/6,]8KUB/6X]>AUD!C2C'6H!2(P10,2$!7E('`#(!`#,!$#0X<!(L[OSNY@
+M"'__$@.3'H#U(LCOR.9@`Q;#(NT4]M,BR._(YF`&%N8D_[,BPR)T%"[U@N0T
+M</6#(N^0`Y&3D`0`<PH8[V`#'X#Z(@$[`,&N`/______________________
+M____________________________________________________________
+M_____________________________________________\`F=`3`X,""P(-U
+M)@HBP"9T!,#@P(+`@W4F&"+_____________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M_____P(0*`(01`(010(2J0(2J@(310(31L,B__\"%4@"%AX"$_T"$T<P!08@
+M#0,2%ILP!@8@#@,2&+TP!P8@#P,2&/XB(I`$%.`@YP,"$IR0<!+@]5:0!`3@
+M$@)/$(<Q$+)1$+U2$+U3$+U4$7%@$/YA$;]B$/YC$>AP$A)Q$CQR$FZ````2
+MG"`"`S`#'7T"KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/A59!T@("$IR0
+M<!'@)/^21P(2G)`$!.`EX"1=]5>0<!#@_W1')5?XQN_&D'`1X/]T2"57^,;O
+MQN3]KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/D'`1X%0?]6+@5(#U9)!P
+M$.#_?@"0!`3@M&$$?0"``GT'[\CMR`B`!<,SSC/.V/GU88Y@D'`1X%1@)/^2
+M+>!48,035`<4]6-U98!U9B-U9P9U:!AU:16M5Z]6$@+(D`04=(#PY)!P$_#E
+M5O1P`P(2G`(2CY!P$>!4'_5ND'`0X/5KD'`1X%1@)/^2+.!48,035`<4]6]U
+M<4!U<B1U<P5U=!=U=>>M5Z]6$@+(D`04=(#PY)!P$_#E5O1P`P(2G`(2CY!P
+M$.!@!-(:@`+2(JU7KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/D'`0X/Z0
+M<!'@_>WXYO57_:]6$@+(D`04=(#PY)!P$_#E5O1P`P(2G(!]D'`0X/Z0<!'@
+M_>WU@HZ#X/57_:]6$@+(D`04=(#PY)!P$_#E5O1@8H!3Y/5Z=7L!]6K"+?4C
+M]3O2+O5WPBSU)'5V&*U7KU82`LB0!!1T@/#DD'`3\.56]&`P@"&0<!#@)/^2
+M2M(%K5>O5A("R)`$%'2`\.20<!/PY5;T8`V0<"3@1!#PD`(L=/_PY2,D_Y(&
+MY20D_Y('(B+E=F`$%7:``W5V&.5V<$,P+0U_;*UAK&`2`GM0`M(8,"P+?WRM
+M:Q(#85`"TB#E=V`)?WH2`V]0`M(AY6I@"7]Z$@-O4`+2&>5W8`?E>[0#`M(C
+MY2,D_Y(&Y20D_Y('D'!@Y7OPH^5J\*/E(/"CY2/PY6RC\*/E;?"B+>0SH_"C
+MY6+PD'!KY7;PY5-P#N5/14Y@".5/%4]P`A5.(B(BPDO"3.5$$@)/$VD`$\0$
+M$\`($Z@0$W0@$X9@$Y&@```3QH5(0X5*0H5,7H!2Y4C$5`_U0^5*Q%0/]4+E
+M3(`;A4E#A4M"A4U>@#7E2<14#_5#Y4O$5`_U0N5-Q%0/]5Z`'N5'M`0&4U[[
+M=4()Y4>T!0Y#7@1U0@F`!M)+@`+23.3U3O5/]2?E0L14\/_E0U0/3_5?D'!$
+M\*/E7O"CY4KPH^5(\*/E3/"CY43PH^5"\*/E0_#28"*0<$#@!/"0<$+E1_#E
+M1V`0),!P`Q(5*!(4'L*OP@32KR+"KY`$%.!4#F`4Y4>T`@OE1+0@!G5."'5/
+M`-(H@`CE3D5/)/^2*-*OD`04X*+DDBET'O#E7U0/]2WE)W`3,"@%Y5\@Y0LP
+M*1GE7U0P_[\P$>4G<`5U)PR``A4GTFS2;8`/Y5\PY@;";-)M@`32;,)MY4=D
+M`W`A,$L&PFS2;8`8Y2=P`S!,$<),Y2=P!74G!X`"%2?2;-)MD'!&Y2WP(&D'
+MY5X@X`*R:"!K!^5>(.$"LFH@;0?E7B#B`K)LD'!'Y2WP=2Y`(&D$HFB`"N5&
+M,&@$HN.``3.2<Y)R(&L$HFJ`"N5&,&H$HN.``3.2=9)T(&T$HFR`"N5&,&P$
+MHN.``3.2<9)PD!`OY2[P(N20`BGP,$<$KT6`!.5%]/^0`BCO\"*/4-)9(H]4
+MTE@BY/4XPJ_E411@111@820"8`,"%@+26755`9`"".!4_O#@(.$BD`0TX+0"
+M&Z/@M`(6H^"T`A%_(!(5/I`0!.!4\_!U40&`=.50<`5U.`.`:Y`2`.!4`W`1
+M?R`2%3Z0`@C@5/OP=5$"@%+E4'`"@$>0`@C@(.,\D`0WX&0B<#20$@1T"O"0
+M$RC@5/#PH^!4\/"CX%3Z\)`$`>!$$/#@5/GPD!($X$0$\'4X`755`N3U48`)
+MY5!P!74X`_51Y3A@%<(!Y/51PEFM.*]`$@+(Y3BT`P+2`]*O(L*O,`$.Y/51
+MPEG"`7T"KT`2`LCE4A1@%P1P79`2!.!4^_!_(!(50W52`755`X!)Y51P19`$
+M`>!$#O#@5._PD!,HX$0/\*/@1`_PH^!$!?"0$@1T`_"0`@C@1`7PD!`$X$0,
+M\.3U4O55,`(+P@)]`:]!$@+(@`+"`]*O(B(BPJ_E:G`$Y7=@`].``<.2*)!P
+M*N`PX2S@$Y(ID'`IX/^0<"C@_:(HDBJB*9(K$A;A4`,@*`42%V&`!Y!P*N!4
+M_?#"!=*O(C`K23`J1K]!")!P*.4R\(`YOT((D'`HY3/P@"Z_10B0<"CE-?"`
+M([]&")!P*.4P\(`8OTD(D'`HY3'P@`V_<0B0<"CE+_"``L,BTR(@*R^_002-
+M,H`EOT($C3.`'K]%!(TU@!>_1@2-,(`0OTD$C3&`";]Q!(TO@`+#(J(J(L,B
+MD'`HX)`0'/"0<"G@D!`=\)!P*N"0$![PD!`<X/4XD!`>X"#A\Y`0'."0<"CP
+MD!`=X)!P*?"0$![@D'`J\#!*#9!P).!$`?"0`BQT__`BPJ^0$!SM\*/O\*-T
+M"O"0$!S@]3J0$![@(.'STJ\BPJ^0$!WO\*-T"_"0$!S@_Y`0'N`@X?32KR*0
+M$T3@]33D\)`"*?"0`BC@1`+P?1U_01(7KWU@#Q(7KW]Q$A?-[T0@_7]Q`A>O
+MY)`"*?"0`BC@1`+PD!-$X/4TY/!_!!(7S>]4&&`=Y7@D`OCF8`>0<"S@_8`%
+MD'`MX/U_0A(7KWTP@!OE>"0"^.9@!Y!P+N#]@`60<"_@_7]"$A>O?2A_11(7
+MKP\2%Z]]"'])$A>O(N20`BGPD`(HX%3]\)`31.4T\']Q$A?-[U3?_7]Q$A>O
+MK3)_01(7KZTS#Q(7KZTU?T42%Z^M,`\2%Z^M,7])`A>OY6H48!8$<#&0<&S@
+M!/!_!A(;E5`D=6H!=7ABD'!MX`3P$AE@0!.0<"3@1`3PD`(L=/_PY/5J$ANN
+MPJ]3(+_2KR+E=Q1@$`1P4G\'$AN54$MU=P%U>&X2&6!`0.5Y)`7_$Q,35!_^
+M)"3U@N0T</6#X/WO5`?_=`'([\@(@`+#,]C\3?]T)"[U@N0T</6#[_"0`BQT
+M__#D]7<2&Z["KU,@?]*O(N5X)`3XYO4XD'!H\/CF_Z/PD'!OX`3PY7LD_G`#
+M`AHF%'`#`AIX)`)@`P(;E.3U>>\@X`,"&B2H..94_O;E>`3XYF!-PJ]U>@6H
+M..94^_;2K^5X!/CF9`)P')!WA?#E>"0#^.:0!!#PY7@D`_CF_Y`$$.!?</*0
+M=X5T_O"H..94^_;E>"0#^.:0!!#P=7L"@#/"KZAXYO5ZJ#CF5/OVTJ^0$`3@
+M5/OPY6I@!I`7!.#U.>5X)`;XYOX(YLKNROD2`G5U>P/3(J@XYC#B.U3[]L*O
+MJ'CF]7JH..94^_;2KY`0!.!4^_#E:F`&D!<$X/4YY7@D!OCF_@CFRN[*^1("
+M=>3U?75[`],BJ#CF,.$)5/WVY/5Y`AN.TR+E=W`#`AMNJ#CF(.,#`AMN5/?V
+M?W,2%\WO9`)@`P(;*)`"*?"0`BC@1`CP?W82%\WE?27@)>`D`/6"Y#1Q]8/O
+M\']U$A?-Y7TEX"7@)`'U@N0T<?6#[_!_=!(7S>5])>`EX"0"]8+D-''U@^_P
+M?W,2%\WE?27@)>`D`_6"Y#1Q]8/O\']Q$A?-[U3?_7]Q$A>O?W$2%\WO1"#]
+M?W$2%Z]U>?_DD`(I\)`"*.!4]_"`1.5])>`EX"0`]8+D-''U@^3PY7TEX"7@
+M)`'U@N0T<?6#Y/#E?27@)>`D`O6"Y#1Q]8/D\.5])>`EX"0#]8+D-''U@^3P
+M!7VH..;_,.$>5/WVY7GT8`=_<Q(7S8]Y$AA\D!`$X$0$\'5[`<,BTR*B+N0S
+MD'!J\,*O,"X&PB[2K],BCSO2K\,BPJ_E.V`2_W0!R._("(`"PS/8_$(@Y/4[
+MTB[2KR(`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````#]
+"93``
+`
+end
diff --git a/sys/dev/ath/ah_osdep.c b/sys/dev/ath/ah_osdep.c
index dcc5765..249c260 100644
--- a/sys/dev/ath/ah_osdep.c
+++ b/sys/dev/ath/ah_osdep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/ah_osdep.h b/sys/dev/ath/ah_osdep.h
index a82bd02..2e2a561 100644
--- a/sys/dev/ath/ah_osdep.h
+++ b/sys/dev/ath/ah_osdep.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/ath_rate/amrr/amrr.c b/sys/dev/ath/ath_rate/amrr/amrr.c
index 00ae568..c230bdf 100644
--- a/sys/dev/ath/ath_rate/amrr/amrr.c
+++ b/sys/dev/ath/ath_rate/amrr/amrr.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -65,7 +66,6 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
-#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
@@ -80,21 +80,10 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/amrr/amrr.h>
#include <contrib/dev/ath/ah_desc.h>
-#define AMRR_DEBUG
-#ifdef AMRR_DEBUG
-#define DPRINTF(sc, _fmt, ...) do { \
- if (sc->sc_debug & 0x10) \
- printf(_fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, _fmt, ...)
-#endif
-
static int ath_rateinterval = 1000; /* rate ctl interval (ms) */
static int ath_rate_max_success_threshold = 10;
static int ath_rate_min_success_threshold = 1;
-static void ath_ratectl(void *);
static void ath_rate_update(struct ath_softc *, struct ieee80211_node *,
int rate);
static void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
@@ -104,7 +93,6 @@ void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
/* NB: assumed to be zero'd by caller */
- ath_rate_update(sc, &an->an_node, 0);
}
void
@@ -166,6 +154,11 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
amn->amn_tx_try3_cnt++;
amn->amn_tx_failure_cnt++;
}
+ if (amn->amn_interval != 0 &&
+ ticks - amn->amn_ticks > amn->amn_interval) {
+ ath_rate_ctl(sc, &an->an_node);
+ amn->amn_ticks = ticks;
+ }
}
void
@@ -176,7 +169,7 @@ ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
}
static void
-node_reset (struct amrr_node *amn)
+node_reset(struct amrr_node *amn)
{
amn->amn_tx_try0_cnt = 0;
amn->amn_tx_try1_cnt = 0;
@@ -200,17 +193,18 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
{
struct ath_node *an = ATH_NODE(ni);
struct amrr_node *amn = ATH_NODE_AMRR(an);
+ struct ieee80211vap *vap = ni->ni_vap;
const HAL_RATE_TABLE *rt = sc->sc_currates;
u_int8_t rix;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
- DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
- __func__, ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: set xmit rate to %dM", __func__,
ni->ni_rates.rs_nrates > 0 ?
(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
- ni->ni_txrate = rate;
+ amn->amn_rix = rate;
/*
* Before associating a node has no rate set setup
* so we can't calculate any transmit codes to use.
@@ -219,8 +213,8 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
* lowest hardware rate.
*/
if (ni->ni_rates.rs_nrates > 0) {
- amn->amn_tx_rix0 = sc->sc_rixmap[
- ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+ ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL;
+ amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate];
amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode;
amn->amn_tx_rate0sp = amn->amn_tx_rate0 |
rt->info[amn->amn_tx_rix0].shortPreamble;
@@ -268,7 +262,12 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0;
}
}
- node_reset (amn);
+ node_reset(amn);
+
+ amn->amn_interval = ath_rateinterval;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ amn->amn_interval /= 2;
+ amn->amn_interval = (amn->amn_interval * hz) / 1000;
}
/*
@@ -278,11 +277,12 @@ static void
ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
int srate;
KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
/*
* No fixed rate is requested. For 11b start with
* the highest negotiated rate; otherwise, for 11g
@@ -308,7 +308,7 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
}
/*
@@ -333,22 +333,20 @@ ath_rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
- if (state == IEEE80211_S_INIT) {
- callout_stop(&asc->timer);
+ if (state == IEEE80211_S_INIT)
return;
- }
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* Reset local xmit state; this is really only
* meaningful when operating in station mode.
*/
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
if (state == IEEE80211_S_RUN) {
ath_rate_ctl_start(sc, ni);
} else {
@@ -362,20 +360,7 @@ ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
* tx rate state of each node.
*/
ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, sc);
- ath_rate_update(sc, ic->ic_bss, 0);
- }
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
- state == IEEE80211_S_RUN) {
- int interval;
- /*
- * Start the background rate control thread if we
- * are not configured to use a fixed xmit rate.
- */
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&asc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
+ ath_rate_update(sc, vap->iv_bss, 0);
}
}
@@ -387,7 +372,7 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
{
struct ath_softc *sc = arg;
struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni));
- int old_rate;
+ int rix;
#define is_success(amn) \
(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10))
@@ -395,52 +380,53 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
(amn->amn_tx_try0_cnt > 10)
#define is_failure(amn) \
(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3))
-#define is_max_rate(ni) \
-((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)
-#define is_min_rate(ni) \
-(ni->ni_txrate == 0)
- old_rate = ni->ni_txrate;
+ rix = amn->amn_rix;
- DPRINTF (sc, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d\n",
- amn->amn_tx_try0_cnt,
- amn->amn_tx_try1_cnt,
- amn->amn_tx_try2_cnt,
- amn->amn_tx_try3_cnt,
- amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d",
+ amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt,
+ amn->amn_tx_try3_cnt, amn->amn_success_threshold);
if (is_success (amn) && is_enough (amn)) {
amn->amn_success++;
if (amn->amn_success == amn->amn_success_threshold &&
- !is_max_rate (ni)) {
+ rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
- ni->ni_txrate++;
- DPRINTF (sc, "increase rate to %d\n", ni->ni_txrate);
+ rix++;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "increase rate to %d", rix);
} else {
amn->amn_recovery = 0;
}
} else if (is_failure (amn)) {
amn->amn_success = 0;
- if (!is_min_rate (ni)) {
+ if (rix > 0) {
if (amn->amn_recovery) {
/* recovery failure. */
amn->amn_success_threshold *= 2;
amn->amn_success_threshold = min (amn->amn_success_threshold,
(u_int)ath_rate_max_success_threshold);
- DPRINTF (sc, "decrease rate recovery thr: %d\n", amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_RATECTL, ni,
+ "decrease rate recovery thr: %d",
+ amn->amn_success_threshold);
} else {
/* simple failure. */
amn->amn_success_threshold = ath_rate_min_success_threshold;
- DPRINTF (sc, "decrease rate normal thr: %d\n", amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_RATECTL, ni,
+ "decrease rate normal thr: %d",
+ amn->amn_success_threshold);
}
amn->amn_recovery = 0;
- ni->ni_txrate--;
+ rix--;
} else {
amn->amn_recovery = 0;
}
}
- if (is_enough (amn) || old_rate != ni->ni_txrate) {
+ if (is_enough (amn) || rix != amn->amn_rix) {
/* reset counters. */
amn->amn_tx_try0_cnt = 0;
amn->amn_tx_try1_cnt = 0;
@@ -448,33 +434,9 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
amn->amn_tx_try3_cnt = 0;
amn->amn_tx_failure_cnt = 0;
}
- if (old_rate != ni->ni_txrate) {
- ath_rate_update(sc, ni, ni->ni_txrate);
- }
-}
-
-static void
-ath_ratectl(void *arg)
-{
- struct ifnet *ifp = arg;
- struct ath_softc *sc = ifp->if_softc;
- struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
- int interval;
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_stats.ast_rate_calls++;
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- ath_rate_ctl(sc, ic->ic_bss); /* NB: no reference */
- else
- ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
+ if (rix != amn->amn_rix) {
+ ath_rate_update(sc, ni, rix);
}
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&asc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
}
static void
@@ -504,7 +466,6 @@ ath_rate_attach(struct ath_softc *sc)
if (asc == NULL)
return NULL;
asc->arc.arc_space = sizeof(struct amrr_node);
- callout_init(&asc->timer, CALLOUT_MPSAFE);
ath_rate_sysctlattach(sc);
return &asc->arc;
@@ -515,7 +476,6 @@ ath_rate_detach(struct ath_ratectrl *arc)
{
struct amrr_softc *asc = (struct amrr_softc *) arc;
- callout_drain(&asc->timer);
free(asc, M_DEVBUF);
}
diff --git a/sys/dev/ath/ath_rate/amrr/amrr.h b/sys/dev/ath/ath_rate/amrr/amrr.h
index cb5d135..c97a007 100644
--- a/sys/dev/ath/ath_rate/amrr/amrr.h
+++ b/sys/dev/ath/ath_rate/amrr/amrr.h
@@ -43,11 +43,13 @@
/* per-device state */
struct amrr_softc {
struct ath_ratectrl arc; /* base state */
- struct callout timer; /* periodic timer */
};
/* per-node state */
struct amrr_node {
+ int amn_rix; /* current rate index */
+ int amn_ticks; /* time of last update */
+ int amn_interval; /* update interval (ticks) */
/* AMRR statistics for this node */
u_int amn_tx_try0_cnt;
u_int amn_tx_try1_cnt;
diff --git a/sys/dev/ath/ath_rate/onoe/onoe.c b/sys/dev/ath/ath_rate/onoe/onoe.c
index eb5759e..25bac09 100644
--- a/sys/dev/ath/ath_rate/onoe/onoe.c
+++ b/sys/dev/ath/ath_rate/onoe/onoe.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
* Atsushi Onoe's rate control algorithm.
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -68,19 +69,6 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/onoe/onoe.h>
#include <contrib/dev/ath/ah_desc.h>
-#define ONOE_DEBUG
-#ifdef ONOE_DEBUG
-enum {
- ATH_DEBUG_RATE = 0x00000010, /* rate control */
-};
-#define DPRINTF(sc, _fmt, ...) do { \
- if (sc->sc_debug & ATH_DEBUG_RATE) \
- printf(_fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, _fmt, ...)
-#endif
-
/*
* Default parameters for the rate control algorithm. These are
* all tunable with sysctls. The rate controller runs periodically
@@ -104,7 +92,6 @@ static int ath_rateinterval = 1000; /* rate ctl interval (ms) */
static int ath_rate_raise = 10; /* add credit threshold */
static int ath_rate_raise_threshold = 10; /* rate ctl raise threshold */
-static void ath_ratectl(void *);
static void ath_rate_update(struct ath_softc *, struct ieee80211_node *,
int rate);
static void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
@@ -114,7 +101,6 @@ void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
/* NB: assumed to be zero'd by caller */
- ath_rate_update(sc, &an->an_node, 0);
}
void
@@ -163,6 +149,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
on->on_tx_err++;
on->on_tx_retr += ts->ts_shortretry
+ ts->ts_longretry;
+ if (on->on_interval != 0 && ticks - on->on_ticks > on->on_interval) {
+ ath_rate_ctl(sc, &an->an_node);
+ on->on_ticks = ticks;
+ }
}
void
@@ -177,17 +167,17 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
{
struct ath_node *an = ATH_NODE(ni);
struct onoe_node *on = ATH_NODE_ONOE(an);
+ struct ieee80211vap *vap = ni->ni_vap;
const HAL_RATE_TABLE *rt = sc->sc_currates;
u_int8_t rix;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
- DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
- __func__, ether_sprintf(ni->ni_macaddr),
- ni->ni_rates.rs_nrates > 0 ?
+ IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: set xmit rate to %dM", __func__,
+ ni->ni_rates.rs_nrates > 0 ?
(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
- ni->ni_txrate = rate;
/*
* Before associating a node has no rate set setup
* so we can't calculate any transmit codes to use.
@@ -197,8 +187,9 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
*/
if (ni->ni_rates.rs_nrates == 0)
goto done;
- on->on_tx_rix0 = sc->sc_rixmap[
- ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+ on->on_rix = rate;
+ ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL;
+ on->on_tx_rix0 = sc->sc_rixmap[ni->ni_txrate];
on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode;
on->on_tx_rate0sp = on->on_tx_rate0 |
@@ -246,6 +237,11 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
}
done:
on->on_tx_ok = on->on_tx_err = on->on_tx_retr = on->on_tx_upper = 0;
+
+ on->on_interval = ath_rateinterval;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ on->on_interval /= 2;
+ on->on_interval = (on->on_interval * hz) / 1000;
}
/*
@@ -255,11 +251,12 @@ static void
ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
int srate;
KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
/*
* No fixed rate is requested. For 11b start with
* the highest negotiated rate; otherwise, for 11g
@@ -285,7 +282,7 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
}
/*
@@ -310,22 +307,20 @@ ath_rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
- if (state == IEEE80211_S_INIT) {
- callout_stop(&osc->timer);
+ if (state == IEEE80211_S_INIT)
return;
- }
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* Reset local xmit state; this is really only
* meaningful when operating in station mode.
*/
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
if (state == IEEE80211_S_RUN) {
ath_rate_ctl_start(sc, ni);
} else {
@@ -339,20 +334,7 @@ ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
* tx rate state of each node.
*/
ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, sc);
- ath_rate_update(sc, ic->ic_bss, 0);
- }
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
- state == IEEE80211_S_RUN) {
- int interval;
- /*
- * Start the background rate control thread if we
- * are not configured to use a fixed xmit rate.
- */
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&osc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
+ ath_rate_update(sc, vap->iv_bss, 0);
}
}
@@ -386,12 +368,11 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)
dir = 1;
- DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n",
- ether_sprintf(ni->ni_macaddr),
- on->on_tx_ok, on->on_tx_err, on->on_tx_retr,
- on->on_tx_upper, dir);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "ok %d err %d retr %d upper %d dir %d",
+ on->on_tx_ok, on->on_tx_err, on->on_tx_retr, on->on_tx_upper, dir);
- nrate = ni->ni_txrate;
+ nrate = on->on_rix;
switch (dir) {
case 0:
if (enough && on->on_tx_upper > 0)
@@ -416,10 +397,10 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
break;
}
- if (nrate != ni->ni_txrate) {
- DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
- __func__,
- (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,
+ if (nrate != on->on_rix) {
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: %dM -> %dM (%d ok, %d err, %d retr)", __func__,
+ ni->ni_txrate / 2,
(rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,
on->on_tx_ok, on->on_tx_err, on->on_tx_retr);
ath_rate_update(sc, ni, nrate);
@@ -428,30 +409,6 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
}
static void
-ath_ratectl(void *arg)
-{
- struct ifnet *ifp = arg;
- struct ath_softc *sc = ifp->if_softc;
- struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
- int interval;
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_stats.ast_rate_calls++;
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- ath_rate_ctl(sc, ic->ic_bss); /* NB: no reference */
- else
- ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
- }
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&osc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
-}
-
-static void
ath_rate_sysctlattach(struct ath_softc *sc)
{
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
@@ -478,7 +435,6 @@ ath_rate_attach(struct ath_softc *sc)
if (osc == NULL)
return NULL;
osc->arc.arc_space = sizeof(struct onoe_node);
- callout_init(&osc->timer, CALLOUT_MPSAFE);
ath_rate_sysctlattach(sc);
return &osc->arc;
@@ -489,7 +445,6 @@ ath_rate_detach(struct ath_ratectrl *arc)
{
struct onoe_softc *osc = (struct onoe_softc *) arc;
- callout_drain(&osc->timer);
free(osc, M_DEVBUF);
}
diff --git a/sys/dev/ath/ath_rate/onoe/onoe.h b/sys/dev/ath/ath_rate/onoe/onoe.h
index 4eef862..27bbe94 100644
--- a/sys/dev/ath/ath_rate/onoe/onoe.h
+++ b/sys/dev/ath/ath_rate/onoe/onoe.h
@@ -38,11 +38,14 @@
/* per-device state */
struct onoe_softc {
struct ath_ratectrl arc; /* base state */
- struct callout timer; /* periodic timer */
};
/* per-node state */
struct onoe_node {
+ int on_rix; /* current rate index */
+ int on_ticks; /* time of last update */
+ int on_interval; /* update interval (ticks) */
+
u_int on_tx_ok; /* tx ok pkt */
u_int on_tx_err; /* tx !ok pkt */
u_int on_tx_retr; /* tx retry count */
diff --git a/sys/dev/ath/ath_rate/sample/sample.c b/sys/dev/ath/ath_rate/sample/sample.c
index 180ef82..60a81ed 100644
--- a/sys/dev/ath/ath_rate/sample/sample.c
+++ b/sys/dev/ath/ath_rate/sample/sample.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
* John Bicket's SampleRate control algorithm.
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -61,7 +62,6 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
-#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
@@ -76,23 +76,6 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/sample/sample.h>
#include <contrib/dev/ath/ah_desc.h>
-#define SAMPLE_DEBUG
-#ifdef SAMPLE_DEBUG
-enum {
- ATH_DEBUG_NODE = 0x00080000, /* node management */
- ATH_DEBUG_RATE = 0x00000010, /* rate control */
- ATH_DEBUG_ANY = 0xffffffff
-};
-#define DPRINTF(sc, m, fmt, ...) do { \
- if (sc->sc_debug & (m)) \
- printf(fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, m, fmt, ...) do { \
- (void) sc; \
-} while (0)
-#endif
-
/*
* This file is an implementation of the SampleRate algorithm
* in "Bit-rate Selection in Wireless Networks"
@@ -152,14 +135,12 @@ rate_to_ndx(struct sample_node *sn, int rate) {
void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s:\n", __func__);
/* NB: assumed to be zero'd by caller */
}
void
ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s:\n", __func__);
}
@@ -258,7 +239,8 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
{
struct sample_node *sn = ATH_NODE_SAMPLE(an);
struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int ndx, size_bin, mrr, best_ndx, change_rates;
unsigned average_tx_time;
@@ -323,10 +305,11 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
if (change_rates) {
if (best_ndx != sn->current_rate[size_bin]) {
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d\n",
+ IEEE80211_NOTE(an->an_node.ni_vap,
+ IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
__func__,
- ether_sprintf(an->an_node.ni_macaddr),
packet_size_bins[size_bin],
sn->rates[sn->current_rate[size_bin]].rate,
sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time,
@@ -340,16 +323,13 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
sn->packets_since_switch[size_bin] = 0;
sn->current_rate[size_bin] = best_ndx;
sn->ticks_since_switch[size_bin] = ticks;
- }
- ndx = sn->current_rate[size_bin];
- sn->packets_since_switch[size_bin]++;
- if (size_bin == 0) {
/*
- * set the visible txrate for this node
- * to the rate of small packets
+ * Set the visible txrate for this node.
*/
- an->an_node.ni_txrate = ndx;
+ an->an_node.ni_txrate = sn->rates[best_ndx].rate;
}
+ ndx = sn->current_rate[size_bin];
+ sn->packets_since_switch[size_bin]++;
}
}
@@ -494,9 +474,10 @@ update_stats(struct ath_softc *sc, struct ath_node *an,
if (ndx0 == sn->current_sample_ndx[size_bin]) {
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)",
+ __func__,
size,
status ? "FAIL" : "OK",
rate, short_tries, tries, tt,
@@ -511,7 +492,8 @@ void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
const struct ath_buf *bf)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct sample_node *sn = ATH_NODE_SAMPLE(an);
const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
const struct ath_desc *ds0 = &bf->bf_desc[0];
@@ -526,9 +508,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
frame_size = 1500;
if (sn->num_rates <= 0) {
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s size %d %s rate/try %d/%d no rates yet\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+ "%s: size %d %s rate/try %d/%d no rates yet",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
ts->ts_status ? "FAIL" : "OK",
short_tries, long_tries);
@@ -541,9 +524,9 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
/*
* Only one rate was used; optimize work.
*/
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s size %d %s rate/try %d/%d/%d\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node, "%s: size %d %s rate/try %d/%d/%d",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
ts->ts_status ? "FAIL" : "OK",
final_rate, short_tries, long_tries);
@@ -591,9 +574,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
tries3 = MS(ds0->ds_ctl2, AR_XmitDataTries3);
ndx3 = rate_to_ndx(sn, rate3);
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
finalTSIdx,
long_tries,
@@ -658,8 +642,6 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
void
ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s: %s isnew %d\n", __func__,
- ether_sprintf(an->an_node.ni_macaddr), isnew);
if (isnew)
ath_rate_ctl_reset(sc, &an->an_node);
}
@@ -671,15 +653,15 @@ static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
struct sample_node *sn = ATH_NODE_SAMPLE(an);
const HAL_RATE_TABLE *rt = sc->sc_currates;
int x, y, srate;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
sn->static_rate_ndx = -1;
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+ if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
/*
* A fixed rate is to be used; ic_fixed_rate is the
* IEEE code for this rate (sans basic bit). Convert this
@@ -688,7 +670,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
/*
* The fixed rate may not be available due to races
@@ -700,32 +682,34 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->static_rate_ndx = srate;
}
- DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s size 1600 rate/tt",
- __func__, ether_sprintf(ni->ni_macaddr));
-
sn->num_rates = ni->ni_rates.rs_nrates;
for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
if (sn->rates[x].rix == 0xff) {
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: ignore bogus rix at %d\n", __func__, x);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: ignore bogus rix at %d", __func__, x);
continue;
}
sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
sn->rates[x].shortPreambleRateCode =
rt->info[sn->rates[x].rix].rateCode |
rt->info[sn->rates[x].rix].shortPreamble;
-
- DPRINTF(sc, ATH_DEBUG_RATE, " %d/%d", sn->rates[x].rate,
- calc_usecs_unicast_packet(sc, 1600, sn->rates[x].rix, 0,0));
}
- DPRINTF(sc, ATH_DEBUG_RATE, "%s\n", "");
-
- /* set the visible bit-rate to the lowest one available */
- ni->ni_txrate = 0;
- sn->num_rates = ni->ni_rates.rs_nrates;
-
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) {
+ ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt",
+ __func__, ni->ni_macaddr, ":");
+ for (x = 0; x < sn->num_rates; x++) {
+ if (sn->rates[x].rix == 0xff)
+ continue;
+ printf(" %d/%d", sn->rates[x].rate,
+ calc_usecs_unicast_packet(sc, 1600,
+ sn->rates[x].rix, 0,0));
+ }
+ printf("\n");
+ }
+#endif
for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
int size = bin_to_size(y);
int ndx = 0;
@@ -756,9 +740,8 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->current_rate[y] = ndx;
}
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s %d rates %d%sMbps (%dus)- %d%sMbps (%dus)\n",
- __func__, ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: %d rates %d%sMbps (%dus)- %d%sMbps (%dus)", __func__,
sn->num_rates,
sn->rates[0].rate/2, sn->rates[0].rate % 0x1 ? ".5" : "",
sn->stats[1][0].perfect_tx_time,
@@ -767,10 +750,11 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->stats[1][sn->num_rates-1].perfect_tx_time
);
- if (sn->static_rate_ndx != -1)
- ni->ni_txrate = sn->static_rate_ndx;
+ /* set the visible bit-rate */
+ if (sn->static_rate_ndx != -1)
+ ni->ni_txrate = sn->rates[sn->static_rate_ndx].rate;
else
- ni->ni_txrate = sn->current_rate[0];
+ ni->ni_txrate = sn->rates[0].rate;
#undef RATE
}
@@ -786,18 +770,19 @@ rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
if (state == IEEE80211_S_RUN) {
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Sync rates for associated stations and neighbors.
*/
ieee80211_iterate_nodes(&ic->ic_sta, rate_cb, sc);
}
- ath_rate_newassoc(sc, ATH_NODE(ic->ic_bss), 1);
+ ath_rate_newassoc(sc, ATH_NODE(vap->iv_bss), 1);
}
}
@@ -822,7 +807,6 @@ ath_rate_attach(struct ath_softc *sc)
{
struct sample_softc *osc;
- DPRINTF(sc, ATH_DEBUG_ANY, "%s:\n", __func__);
osc = malloc(sizeof(struct sample_softc), M_DEVBUF, M_NOWAIT|M_ZERO);
if (osc == NULL)
return NULL;
diff --git a/sys/dev/ath/ath_rate/sample/sample.h b/sys/dev/ath/ath_rate/sample/sample.h
index 1e9377e..1ea96c4 100644
--- a/sys/dev/ath/ath_rate/sample/sample.h
+++ b/sys/dev/ath/ath_rate/sample/sample.h
@@ -155,12 +155,13 @@ static unsigned calc_usecs_unicast_packet(struct ath_softc *sc,
int length,
int rix, int short_retries, int long_retries) {
const HAL_RATE_TABLE *rt = sc->sc_currates;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int rts, cts;
unsigned t_slot = 20;
unsigned t_difs = 50;
unsigned t_sifs = 10;
- struct ieee80211com *ic = &sc->sc_ic;
int tt = 0;
int x = 0;
int cw = WIFI_CW_MIN;
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index fd02770..0d5ef91 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -84,6 +84,22 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
+/*
+ * ATH_BCBUF determines the number of vap's that can transmit
+ * beacons and also (currently) the number of vap's that can
+ * have unique mac addresses/bssid. When staggering beacons
+ * 4 is probably a good max as otherwise the beacons become
+ * very closely spaced and there is limited time for cab q traffic
+ * to go out. You can burst beacons instead but that is not good
+ * for stations in power save and at some point you really want
+ * another radio (and channel).
+ *
+ * The limit on the number of mac addresses is tied to our use of
+ * the U/L bit and tracking addresses in a byte; it would be
+ * worthwhile to allow more for applications like proxy sta.
+ */
+CTASSERT(ATH_BCBUF <= 8);
+
/* unaligned little endian access */
#define LE_READ_2(p) \
((u_int16_t) \
@@ -99,49 +115,59 @@ enum {
ATH_LED_POLL,
};
+static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ath_vap_delete(struct ieee80211vap *);
static void ath_init(void *);
static void ath_stop_locked(struct ifnet *);
static void ath_stop(struct ifnet *);
static void ath_start(struct ifnet *);
static int ath_reset(struct ifnet *);
+static int ath_reset_vap(struct ieee80211vap *, u_long);
static int ath_media_change(struct ifnet *);
static void ath_watchdog(struct ifnet *);
static int ath_ioctl(struct ifnet *, u_long, caddr_t);
static void ath_fatal_proc(void *, int);
static void ath_rxorn_proc(void *, int);
+static void ath_bmiss_vap(struct ieee80211vap *);
static void ath_bmiss_proc(void *, int);
-static int ath_key_alloc(struct ieee80211com *,
+static int ath_key_alloc(struct ieee80211vap *,
const struct ieee80211_key *,
ieee80211_keyix *, ieee80211_keyix *);
-static int ath_key_delete(struct ieee80211com *,
+static int ath_key_delete(struct ieee80211vap *,
const struct ieee80211_key *);
-static int ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
+static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *,
const u_int8_t mac[IEEE80211_ADDR_LEN]);
-static void ath_key_update_begin(struct ieee80211com *);
-static void ath_key_update_end(struct ieee80211com *);
+static void ath_key_update_begin(struct ieee80211vap *);
+static void ath_key_update_end(struct ieee80211vap *);
+static void ath_update_mcast(struct ifnet *);
+static void ath_update_promisc(struct ifnet *);
static void ath_mode_init(struct ath_softc *);
static void ath_setslottime(struct ath_softc *);
static void ath_updateslot(struct ifnet *);
static int ath_beaconq_setup(struct ath_hal *);
static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
-static void ath_beacon_update(struct ieee80211com *, int item);
+static void ath_beacon_update(struct ieee80211vap *, int item);
static void ath_beacon_setup(struct ath_softc *, struct ath_buf *);
static void ath_beacon_proc(void *, int);
+static struct ath_buf *ath_beacon_generate(struct ath_softc *,
+ struct ieee80211vap *);
static void ath_bstuck_proc(void *, int);
+static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
static void ath_beacon_free(struct ath_softc *);
-static void ath_beacon_config(struct ath_softc *);
+static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
static void ath_descdma_cleanup(struct ath_softc *sc,
struct ath_descdma *, ath_bufhead *);
static int ath_desc_alloc(struct ath_softc *);
static void ath_desc_free(struct ath_softc *);
static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
static void ath_node_free(struct ieee80211_node *);
-static int8_t ath_node_getrssi(const struct ieee80211_node *);
static void ath_node_getsignal(const struct ieee80211_node *,
int8_t *, int8_t *);
static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
-static void ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni,
+static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, int rssi, int noise, u_int32_t rstamp);
static void ath_setdefantenna(struct ath_softc *, u_int);
static void ath_rx_proc(void *, int);
@@ -157,6 +183,7 @@ static int ath_tx_start(struct ath_softc *, struct ieee80211_node *,
static void ath_tx_proc_q0(void *, int);
static void ath_tx_proc_q0123(void *, int);
static void ath_tx_proc(void *, int);
+static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
static void ath_draintxq(struct ath_softc *);
static void ath_stoprecv(struct ath_softc *);
@@ -166,13 +193,16 @@ static void ath_scan_start(struct ieee80211com *);
static void ath_scan_end(struct ieee80211com *);
static void ath_set_channel(struct ieee80211com *);
static void ath_calibrate(void *);
-static int ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void ath_setup_stationkey(struct ieee80211_node *);
static void ath_newassoc(struct ieee80211_node *, int);
-static int ath_getchannels(struct ath_softc *,
- HAL_REG_DOMAIN, HAL_CTRY_CODE, HAL_BOOL, HAL_BOOL);
+static int ath_setregdomain(struct ieee80211com *,
+ struct ieee80211_regdomain *, int,
+ struct ieee80211_channel []);
+static void ath_getradiocaps(struct ieee80211com *, int *,
+ struct ieee80211_channel []);
+static int ath_getchannels(struct ath_softc *);
static void ath_led_event(struct ath_softc *, int);
-static void ath_update_txpow(struct ath_softc *);
static int ath_rate_setup(struct ath_softc *, u_int mode);
static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
@@ -189,21 +219,6 @@ SYSCTL_DECL(_hw_ath);
static int ath_calinterval = 30; /* calibrate every 30 secs */
SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
0, "chip calibration interval (secs)");
-static int ath_outdoor = AH_TRUE; /* outdoor operation */
-SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RW, &ath_outdoor,
- 0, "outdoor operation");
-TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
-static int ath_xchanmode = AH_TRUE; /* extended channel use */
-SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RW, &ath_xchanmode,
- 0, "extended channel mode");
-TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
-static int ath_countrycode = CTRY_DEFAULT; /* country code */
-SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RW, &ath_countrycode,
- 0, "country code");
-TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
-static int ath_regdomain = 0; /* regulatory domain */
-SYSCTL_INT(_hw_ath, OID_AUTO, regdomain, CTLFLAG_RD, &ath_regdomain,
- 0, "regulatory domain");
static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf,
@@ -273,19 +288,20 @@ int
ath_attach(u_int16_t devid, struct ath_softc *sc)
{
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ath_hal *ah = NULL;
HAL_STATUS status;
int error = 0, i;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
error = ENOSPC;
goto bad;
}
+ ic = ifp->if_l2com;
/* set these up early for if_printf use */
if_initname(ifp, device_get_name(sc->sc_dev),
@@ -342,13 +358,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
ath_hal_keyreset(ah, i);
/*
- * Collect the channel list using the default country
- * code and including outdoor channels. The 802.11 layer
- * is resposible for filtering this list based on settings
- * like the phy mode.
+ * Collect the default channel list.
*/
- error = ath_getchannels(sc, ath_regdomain, ath_countrycode,
- ath_outdoor != 0, ath_xchanmode != 0);
+ error = ath_getchannels(sc);
if (error != 0)
goto bad;
@@ -378,7 +390,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
goto bad;
}
callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE);
- callout_init(&sc->sc_dfs_ch, CALLOUT_MPSAFE);
ATH_TXBUF_LOCK_INIT(sc);
@@ -412,8 +423,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
error = EIO;
goto bad2;
}
- /* NB: s/w q, qnum used only by WITNESS */
- ath_txq_init(sc, &sc->sc_mcastq, HAL_NUM_TX_QUEUES+1);
/* NB: insure BK queue is the lowest priority h/w queue */
if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
@@ -497,10 +506,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
- ic->ic_reset = ath_reset;
- ic->ic_newassoc = ath_newassoc;
- ic->ic_updateslot = ath_updateslot;
- ic->ic_wme.wme_update = ath_wme_update;
/* XXX not right but it's not used anywhere important */
ic->ic_phytype = IEEE80211_T_OFDM;
ic->ic_opmode = IEEE80211_M_STA;
@@ -509,6 +514,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
| IEEE80211_C_HOSTAP /* hostap mode */
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
@@ -519,22 +525,22 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
* Query the hal to figure out h/w crypto support.
*/
if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
- ic->ic_caps |= IEEE80211_C_AES;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
- ic->ic_caps |= IEEE80211_C_AES_CCM;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
- ic->ic_caps |= IEEE80211_C_CKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
- ic->ic_caps |= IEEE80211_C_TKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP;
/*
* Check if h/w does the MIC and/or whether the
* separate key cache entries are required to
* handle both tx+rx MIC keys.
*/
if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
- ic->ic_caps |= IEEE80211_C_TKIPMIC;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC;
/*
* If the h/w supports storing tx+rx MIC keys
* in one cache slot automatically enable use.
@@ -542,6 +548,13 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
if (ath_hal_hastkipsplit(ah) ||
!ath_hal_settkipsplit(ah, AH_FALSE))
sc->sc_splitmic = 1;
+ /*
+ * If the h/w can do TKIP MIC together with WME then
+ * we use it; otherwise we force the MIC to be done
+ * in software by the net80211 layer.
+ */
+ if (ath_hal_haswmetkipmic(ah))
+ sc->sc_wmetkipmic = 1;
}
sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
@@ -578,9 +591,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
*/
if (ath_hal_hasbursting(ah))
ic->ic_caps |= IEEE80211_C_BURST;
+ sc->sc_hasbmask = ath_hal_hasbssidmask(ah);
+ sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
if (ath_hal_hasfastframes(ah))
ic->ic_caps |= IEEE80211_C_FF;
- if (ath_hal_getwirelessmodes(ah, ath_countrycode) & (HAL_MODE_108G|HAL_MODE_TURBO))
+ if (ath_hal_getwirelessmodes(ah, ic->ic_regdomain.country) & (HAL_MODE_108G|HAL_MODE_TURBO))
ic->ic_caps |= IEEE80211_C_TURBOP;
/*
@@ -602,33 +617,33 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
/* get mac address from hardware */
ath_hal_getmac(ah, ic->ic_myaddr);
+ if (sc->sc_hasbmask)
+ ath_hal_getbssidmask(ah, sc->sc_hwbssidmask);
+ /* NB: used to size node table key mapping array */
+ ic->ic_max_keyix = sc->sc_keymax;
/* call MI attach routine. */
ieee80211_ifattach(ic);
- sc->sc_opmode = ic->ic_opmode;
+ ic->ic_setregdomain = ath_setregdomain;
+ ic->ic_getradiocaps = ath_getradiocaps;
+ sc->sc_opmode = HAL_M_STA;
+
/* override default methods */
+ ic->ic_newassoc = ath_newassoc;
+ ic->ic_updateslot = ath_updateslot;
+ ic->ic_wme.wme_update = ath_wme_update;
+ ic->ic_vap_create = ath_vap_create;
+ ic->ic_vap_delete = ath_vap_delete;
+ ic->ic_raw_xmit = ath_raw_xmit;
+ ic->ic_update_mcast = ath_update_mcast;
+ ic->ic_update_promisc = ath_update_promisc;
ic->ic_node_alloc = ath_node_alloc;
sc->sc_node_free = ic->ic_node_free;
ic->ic_node_free = ath_node_free;
- ic->ic_node_getrssi = ath_node_getrssi;
ic->ic_node_getsignal = ath_node_getsignal;
- sc->sc_recv_mgmt = ic->ic_recv_mgmt;
- ic->ic_recv_mgmt = ath_recv_mgmt;
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ath_newstate;
ic->ic_scan_start = ath_scan_start;
ic->ic_scan_end = ath_scan_end;
ic->ic_set_channel = ath_set_channel;
- ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
- ic->ic_crypto.cs_key_alloc = ath_key_alloc;
- ic->ic_crypto.cs_key_delete = ath_key_delete;
- ic->ic_crypto.cs_key_set = ath_key_set;
- ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
- ic->ic_crypto.cs_key_update_end = ath_key_update_end;
- ic->ic_raw_xmit = ath_raw_xmit;
- ic->ic_update_beacon = ath_beacon_update;
- /* complete initialization */
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
ath_bpfattach(sc);
/*
@@ -675,7 +690,7 @@ ath_detach(struct ath_softc *sc)
* it last
* Other than that, it's straightforward...
*/
- ieee80211_ifdetach(&sc->sc_ic);
+ ieee80211_ifdetach(ifp->if_l2com);
#ifdef ATH_TX99_DIAG
if (sc->sc_tx99 != NULL)
sc->sc_tx99->detach(sc->sc_tx99);
@@ -690,6 +705,343 @@ ath_detach(struct ath_softc *sc)
return 0;
}
+/*
+ * MAC address handling for multiple BSS on the same radio.
+ * The first vap uses the MAC address from the EEPROM. For
+ * subsequent vap's we set the U/L bit (bit 1) in the MAC
+ * address and use the next six bits as an index.
+ */
+static void
+assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone)
+{
+ int i;
+
+ if (clone && sc->sc_hasbmask) {
+ /* NB: we only do this if h/w supports multiple bssid */
+ for (i = 0; i < 8; i++)
+ if ((sc->sc_bssidmask & (1<<i)) == 0)
+ break;
+ if (i != 0)
+ mac[0] |= (i << 2)|0x2;
+ } else
+ i = 0;
+ sc->sc_bssidmask |= 1<<i;
+ sc->sc_hwbssidmask[0] &= ~mac[0];
+ if (i == 0)
+ sc->sc_nbssid0++;
+}
+
+static void
+reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ int i = mac[0] >> 2;
+ uint8_t mask;
+
+ if (i != 0 || --sc->sc_nbssid0 == 0) {
+ sc->sc_bssidmask &= ~(1<<i);
+ /* recalculate bssid mask from remaining addresses */
+ mask = 0xff;
+ for (i = 1; i < 8; i++)
+ if (sc->sc_bssidmask & (1<<i))
+ mask &= ~((i<<2)|0x2);
+ sc->sc_hwbssidmask[0] |= mask;
+ }
+}
+
+/*
+ * Assign a beacon xmit slot. We try to space out
+ * assignments so when beacons are staggered the
+ * traffic coming out of the cab q has maximal time
+ * to go out before the next beacon is scheduled.
+ */
+static int
+assign_bslot(struct ath_softc *sc)
+{
+ u_int slot, free;
+
+ free = 0;
+ for (slot = 0; slot < ATH_BCBUF; slot++)
+ if (sc->sc_bslot[slot] == NULL) {
+ if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL &&
+ sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL)
+ return slot;
+ free = slot;
+ /* NB: keep looking for a double slot */
+ }
+ return free;
+}
+
+static struct ieee80211vap *
+ath_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac0[IEEE80211_ADDR_LEN])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_vap *avp;
+ struct ieee80211vap *vap;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+ int ic_opmode, needbeacon, error;
+
+ avp = (struct ath_vap *) malloc(sizeof(struct ath_vap),
+ M_80211_VAP, M_WAITOK | M_ZERO);
+ needbeacon = 0;
+ IEEE80211_ADDR_COPY(mac, mac0);
+
+ ATH_LOCK(sc);
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ if (sc->sc_nstavaps != 0) { /* XXX only 1 sta for now */
+ device_printf(sc->sc_dev, "only 1 sta vap supported\n");
+ goto bad;
+ }
+ if (sc->sc_nvaps) {
+ /*
+ * When there are multiple vaps we must fall
+ * back to s/w beacon miss handling.
+ */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ }
+ if (flags & IEEE80211_CLONE_NOBEACONS) {
+ sc->sc_swbmiss = 1;
+ ic_opmode = IEEE80211_M_HOSTAP;
+ } else
+ ic_opmode = opmode;
+ break;
+ case IEEE80211_M_IBSS:
+ if (sc->sc_nvaps != 0) { /* XXX only 1 for now */
+ device_printf(sc->sc_dev,
+ "only 1 ibss vap supported\n");
+ goto bad;
+ }
+ ic_opmode = opmode;
+ needbeacon = 1;
+ break;
+ case IEEE80211_M_AHDEMO:
+ /* fall thru... */
+ case IEEE80211_M_MONITOR:
+ if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) {
+ /* XXX not right for monitor mode */
+ ic_opmode = ic->ic_opmode;
+ } else
+ ic_opmode = opmode;
+ break;
+ case IEEE80211_M_HOSTAP:
+ needbeacon = 1;
+ /* fall thru... */
+ case IEEE80211_M_WDS:
+ if (sc->sc_nvaps && ic->ic_opmode == IEEE80211_M_STA) {
+ device_printf(sc->sc_dev,
+ "wds not supported in sta mode\n");
+ goto bad;
+ }
+ if (opmode == IEEE80211_M_WDS) {
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ }
+ ic_opmode = IEEE80211_M_HOSTAP;
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
+ goto bad;
+ }
+ /*
+ * Check that a beacon buffer is available; the code below assumes it.
+ */
+ if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) {
+ device_printf(sc->sc_dev, "no beacon buffer available\n");
+ goto bad;
+ }
+
+ /* STA, AHDEMO? */
+ if (opmode == IEEE80211_M_HOSTAP) {
+ assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
+ ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
+ }
+
+ vap = &avp->av_vap;
+ /* XXX can't hold mutex across if_alloc */
+ ATH_UNLOCK(sc);
+ error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags,
+ bssid, mac);
+ ATH_LOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "%s: error %d creating vap\n",
+ __func__, error);
+ goto bad2;
+ }
+
+ /* h/w crypto support */
+ vap->iv_key_alloc = ath_key_alloc;
+ vap->iv_key_delete = ath_key_delete;
+ vap->iv_key_set = ath_key_set;
+ vap->iv_key_update_begin = ath_key_update_begin;
+ vap->iv_key_update_end = ath_key_update_end;
+
+ /* override various methods */
+ avp->av_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = ath_recv_mgmt;
+ vap->iv_reset = ath_reset_vap;
+ vap->iv_update_beacon = ath_beacon_update;
+ avp->av_newstate = vap->iv_newstate;
+ vap->iv_newstate = ath_newstate;
+ avp->av_bmiss = vap->iv_bmiss;
+ vap->iv_bmiss = ath_bmiss_vap;
+
+ avp->av_bslot = -1;
+ if (needbeacon) {
+ /*
+ * Allocate beacon state and setup the q for buffered
+ * multicast frames. We know a beacon buffer is
+ * available because we checked above.
+ */
+ avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf);
+ STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list);
+ if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) {
+ /*
+ * Assign the vap to a beacon xmit slot. As above
+ * this cannot fail to find a free one.
+ */
+ avp->av_bslot = assign_bslot(sc);
+ KASSERT(sc->sc_bslot[avp->av_bslot] == NULL,
+ ("beacon slot %u not empty", avp->av_bslot));
+ sc->sc_bslot[avp->av_bslot] = vap;
+ sc->sc_nbcnvaps++;
+ }
+ if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) {
+ /*
+ * Multple vaps are to transmit beacons and we
+ * have h/w support for TSF adjusting; enable
+ * use of staggered beacons.
+ */
+ sc->sc_stagbeacons = 1;
+ }
+ ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ);
+ }
+
+ ic->ic_opmode = ic_opmode;
+ if (opmode != IEEE80211_M_WDS) {
+ sc->sc_nvaps++;
+ if (opmode == IEEE80211_M_STA)
+ sc->sc_nstavaps++;
+ }
+ switch (ic_opmode) {
+ case IEEE80211_M_IBSS:
+ sc->sc_opmode = HAL_M_IBSS;
+ break;
+ case IEEE80211_M_STA:
+ sc->sc_opmode = HAL_M_STA;
+ break;
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ sc->sc_opmode = HAL_M_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ sc->sc_opmode = HAL_M_MONITOR;
+ break;
+ default:
+ /* XXX should not happen */
+ break;
+ }
+ if (sc->sc_hastsfadd) {
+ /*
+ * Configure whether or not TSF adjust should be done.
+ */
+ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons);
+ }
+ ATH_UNLOCK(sc);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status);
+ return vap;
+bad2:
+ reclaim_address(sc, mac);
+ ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
+bad:
+ free(avp, M_80211_VAP);
+ ATH_UNLOCK(sc);
+ return NULL;
+}
+
+static void
+ath_vap_delete(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ath_softc *sc = ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_vap *avp = ATH_VAP(vap);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Quiesce the hardware while we remove the vap. In
+ * particular we need to reclaim all references to
+ * the vap state by any frames pending on the tx queues.
+ */
+ ath_hal_intrset(ah, 0); /* disable interrupts */
+ ath_draintxq(sc); /* stop xmit side */
+ ath_stoprecv(sc); /* stop recv side */
+ }
+
+ ieee80211_vap_detach(vap);
+ ATH_LOCK(sc);
+ /*
+ * Reclaim beacon state. Note this must be done before
+ * the vap instance is reclaimed as we may have a reference
+ * to it in the buffer for the beacon frame.
+ */
+ if (avp->av_bcbuf != NULL) {
+ if (avp->av_bslot != -1) {
+ sc->sc_bslot[avp->av_bslot] = NULL;
+ sc->sc_nbcnvaps--;
+ }
+ ath_beacon_return(sc, avp->av_bcbuf);
+ avp->av_bcbuf = NULL;
+ if (sc->sc_nbcnvaps == 0) {
+ sc->sc_stagbeacons = 0;
+ if (sc->sc_hastsfadd)
+ ath_hal_settsfadjust(sc->sc_ah, 0);
+ }
+ /*
+ * Reclaim any pending mcast frames for the vap.
+ */
+ ath_tx_draintxq(sc, &avp->av_mcastq);
+ ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq);
+ }
+ /*
+ * Update bookkeeping.
+ */
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ sc->sc_nstavaps--;
+ if (sc->sc_nstavaps == 0 && sc->sc_swbmiss)
+ sc->sc_swbmiss = 0;
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ reclaim_address(sc, vap->iv_myaddr);
+ ath_hal_setbssidmask(ah, sc->sc_hwbssidmask);
+ }
+ if (vap->iv_opmode != IEEE80211_M_WDS)
+ sc->sc_nvaps--;
+ ATH_UNLOCK(sc);
+ free(avp, M_80211_VAP);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Restart rx+tx machines if still running (RUNNING will
+ * be reset if we just destroyed the last vap).
+ */
+ if (ath_startrecv(sc) != 0)
+ if_printf(ifp, "%s: unable to restart recv logic\n",
+ __func__);
+ if (sc->sc_beacons)
+ ath_beacon_config(sc, NULL);
+ ath_hal_intrset(ah, sc->sc_imask);
+ }
+}
+
void
ath_suspend(struct ath_softc *sc)
{
@@ -864,37 +1216,40 @@ ath_rxorn_proc(void *arg, int pending)
}
static void
+ath_bmiss_vap(struct ieee80211vap *vap)
+{
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ u_int64_t lastrx = sc->sc_lastrx;
+ u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
+ u_int bmisstimeout =
+ vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024;
+
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
+ __func__, (unsigned long long) tsf,
+ (unsigned long long)(tsf - lastrx),
+ (unsigned long long) lastrx, bmisstimeout);
+ /*
+ * Workaround phantom bmiss interrupts by sanity-checking
+ * the time of our last rx'd frame. If it is within the
+ * beacon miss interval then ignore the interrupt. If it's
+ * truly a bmiss we'll get another interrupt soon and that'll
+ * be dispatched up for processing.
+ */
+ if (tsf - lastrx > bmisstimeout)
+ ATH_VAP(vap)->av_bmiss(vap);
+ else
+ sc->sc_stats.ast_bmiss_phantom++;
+}
+
+static void
ath_bmiss_proc(void *arg, int pending)
{
struct ath_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending);
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("unexpect operating mode %u", ic->ic_opmode));
- if (ic->ic_state == IEEE80211_S_RUN) {
- u_int64_t lastrx = sc->sc_lastrx;
- u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
- u_int bmisstimeout =
- ic->ic_bmissthreshold * ic->ic_bss->ni_intval * 1024;
-
- DPRINTF(sc, ATH_DEBUG_BEACON,
- "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
- __func__, (unsigned long long) tsf,
- (unsigned long long)(tsf - lastrx),
- (unsigned long long) lastrx, bmisstimeout);
- /*
- * Workaround phantom bmiss interrupts by sanity-checking
- * the time of our last rx'd frame. If it is within the
- * beacon miss interval then ignore the interrupt. If it's
- * truly a bmiss we'll get another interrupt soon and that'll
- * be dispatched up for processing.
- */
- if (tsf - lastrx > bmisstimeout)
- ieee80211_beacon_miss(ic);
- else
- sc->sc_stats.ast_bmiss_phantom++;
- }
+ ieee80211_beacon_miss(ifp->if_l2com);
}
/*
@@ -939,12 +1294,35 @@ ath_mapchan(HAL_CHANNEL *hc, const struct ieee80211_channel *chan)
#undef N
}
+/*
+ * Handle TKIP MIC setup to deal hardware that doesn't do MIC
+ * calcs together with WME. If necessary disable the crypto
+ * hardware and mark the 802.11 state so keys will be setup
+ * with the MIC work done in software.
+ */
+static void
+ath_settkipmic(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) {
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ ath_hal_settkipmic(sc->sc_ah, AH_FALSE);
+ ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC;
+ } else {
+ ath_hal_settkipmic(sc->sc_ah, AH_TRUE);
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC;
+ }
+ }
+}
+
static void
ath_init(void *arg)
{
struct ath_softc *sc = (struct ath_softc *) arg;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_STATUS status;
@@ -966,18 +1344,16 @@ ath_init(void *arg)
* and then setup of the interrupt mask.
*/
ath_mapchan(&sc->sc_curchan, ic->ic_curchan);
+ ath_settkipmic(sc);
if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
if_printf(ifp, "unable to reset hardware; hal status %u\n",
status);
- goto done;
+ ATH_UNLOCK(sc);
+ return;
}
+ ath_chan_change(sc, ic->ic_curchan);
/*
- * This is needed only to setup initial state
- * but it's best done after a reset.
- */
- ath_update_txpow(sc);
- /*
* Likewise this is set during reset so update
* state cached in the driver.
*/
@@ -994,7 +1370,8 @@ ath_init(void *arg)
*/
if (ath_startrecv(sc) != 0) {
if_printf(ifp, "unable to start recv logic\n");
- goto done;
+ ATH_UNLOCK(sc);
+ return;
}
/*
@@ -1009,36 +1386,24 @@ ath_init(void *arg)
*/
if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
sc->sc_imask |= HAL_INT_MIB;
- ath_hal_intrset(ah, sc->sc_imask);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ic->ic_state = IEEE80211_S_INIT;
+ ath_hal_intrset(ah, sc->sc_imask);
+
+ ATH_UNLOCK(sc);
- /*
- * The hardware should be ready to go now so it's safe
- * to kick the 802.11 state machine as it's likely to
- * immediately call back to us to send mgmt frames.
- */
- ath_chan_change(sc, ic->ic_curchan);
#ifdef ATH_TX99_DIAG
if (sc->sc_tx99 != NULL)
sc->sc_tx99->start(sc->sc_tx99);
else
#endif
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-done:
- ATH_UNLOCK(sc);
+ ieee80211_start_all(ic); /* start all vap's */
}
static void
ath_stop_locked(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ath_hal *ah = sc->sc_ah;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
@@ -1065,7 +1430,6 @@ ath_stop_locked(struct ifnet *ifp)
if (sc->sc_tx99 != NULL)
sc->sc_tx99->stop(sc->sc_tx99);
#endif
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
ifp->if_timer = 0;
if (!sc->sc_invalid) {
@@ -1083,8 +1447,7 @@ ath_stop_locked(struct ifnet *ifp)
ath_hal_phydisable(ah);
} else
sc->sc_rxlink = NULL;
- IFQ_DRV_PURGE(&ifp->if_snd);
- ath_beacon_free(sc);
+ ath_beacon_free(sc); /* XXX not needed */
}
}
@@ -1121,7 +1484,7 @@ static int
ath_reset(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_STATUS status;
@@ -1134,11 +1497,11 @@ ath_reset(struct ifnet *ifp)
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* stop xmit side */
ath_stoprecv(sc); /* stop recv side */
+ ath_settkipmic(sc); /* configure TKIP MIC handling */
/* NB: indicate channel change so we do a full reset */
if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_TRUE, &status))
if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
__func__, status);
- ath_update_txpow(sc); /* update tx power state */
sc->sc_diversity = ath_hal_getdiversity(ah);
sc->sc_calinterval = 1;
sc->sc_caltries = 0;
@@ -1150,14 +1513,20 @@ ath_reset(struct ifnet *ifp)
* might change as a result.
*/
ath_chan_change(sc, ic->ic_curchan);
- if (ic->ic_state == IEEE80211_S_RUN)
- ath_beacon_config(sc); /* restart beacons */
+ if (sc->sc_beacons)
+ ath_beacon_config(sc, NULL); /* restart beacons */
ath_hal_intrset(ah, sc->sc_imask);
ath_start(ifp); /* restart xmit */
return 0;
}
+static int
+ath_reset_vap(struct ieee80211vap *vap, u_long cmd)
+{
+ return ath_reset(vap->iv_ic->ic_ifp);
+}
+
static int
ath_ff_always(struct ath_txq *txq, struct ath_buf *bf)
{
@@ -1210,7 +1579,7 @@ ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq,
sc->sc_stats.ast_ff_flush++;
/* encap and xmit */
- bf->bf_m = ieee80211_encap(&sc->sc_ic, bf->bf_m, ni);
+ bf->bf_m = ieee80211_encap(ni, bf->bf_m);
if (bf->bf_m == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
"%s: discard, encapsulation failure\n",
@@ -1243,6 +1612,7 @@ ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq,
static __inline u_int32_t
ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m)
{
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
u_int32_t framelen;
struct ath_buf *bf;
@@ -1256,7 +1626,7 @@ ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m)
* - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd)
*/
framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14;
- if (sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY)
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
framelen += 24;
bf = an->an_ff_buf[M_WME_GETAC(m)];
if (bf != NULL)
@@ -1280,7 +1650,7 @@ static __inline int
ath_ff_can_aggregate(struct ath_softc *sc,
struct ath_node *an, struct mbuf *m, int *flushq)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_txq *txq;
u_int32_t txoplimit;
u_int pri;
@@ -1330,7 +1700,6 @@ static struct mbuf *
ath_ff_check(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
struct ath_node *an = ATH_NODE(ni);
struct ath_buf *bfstaged;
int ff_flush, pri;
@@ -1423,7 +1792,7 @@ ath_ff_check(struct ath_softc *sc, struct ath_txq *txq,
ether_sprintf(an->an_node.ni_macaddr));
/* encap and xmit */
- bfstaged->bf_m = ieee80211_encap(ic, bfstaged->bf_m, ni);
+ bfstaged->bf_m = ieee80211_encap(ni, bfstaged->bf_m);
if (bfstaged->bf_m == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
"%s: discard, encap failure\n", __func__);
@@ -1525,13 +1894,10 @@ static void
ath_start(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_node *ni;
struct ath_buf *bf;
struct mbuf *m, *next;
- struct ieee80211_frame *wh;
- struct ether_header *eh;
struct ath_txq *txq;
ath_bufhead frags;
int pri;
@@ -1554,154 +1920,63 @@ ath_start(struct ifnet *ifp)
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
- /*
- * Poll the management queue for frames; they
- * have priority over normal data frames.
- */
- IF_DEQUEUE(&ic->ic_mgtq, m);
- if (m == NULL) {
- /*
- * No data frames go out unless we're associated.
- */
- if (ic->ic_state != IEEE80211_S_RUN) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: discard data packet, state %s\n",
- __func__,
- ieee80211_state_name[ic->ic_state]);
- sc->sc_stats.ast_tx_discard++;
- ATH_TXBUF_LOCK(sc);
- STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
- ATH_TXBUF_UNLOCK(sc);
- break;
- }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */
- if (m == NULL) {
- ATH_TXBUF_LOCK(sc);
- STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
- ATH_TXBUF_UNLOCK(sc);
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
- STAILQ_INIT(&frags);
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL) {
+ ATH_TXBUF_LOCK(sc);
+ STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+ ATH_TXBUF_UNLOCK(sc);
+ break;
+ }
+ STAILQ_INIT(&frags);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ pri = M_WME_GETAC(m);
+ txq = sc->sc_ac2q[pri];
+ if (ni->ni_ath_flags & IEEE80211_NODE_FF) {
/*
- * Find the node for the destination so we can do
- * things like power save and fast frames aggregation.
+ * Check queue length; if too deep drop this
+ * frame (tail drop considered good).
*/
- if (m->m_len < sizeof(struct ether_header) &&
- (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ni = NULL;
- goto bad;
- }
- eh = mtod(m, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- /* NB: ieee80211_find_txnode does stat+msg */
+ if (txq->axq_depth >= sc->sc_fftxqmax) {
+ DPRINTF(sc, ATH_DEBUG_FF,
+ "[%s] tail drop on q %u depth %u\n",
+ ether_sprintf(ni->ni_macaddr),
+ txq->axq_qnum, txq->axq_depth);
+ sc->sc_stats.ast_tx_qfull++;
m_freem(m);
- goto bad;
- }
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m);
goto reclaim;
}
- /* calculate priority so we can find the tx queue */
- if (ieee80211_classify(ic, m, ni)) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: discard, classification failure\n",
- __func__);
- m_freem(m);
- goto bad;
- }
- pri = M_WME_GETAC(m);
- txq = sc->sc_ac2q[pri];
- if (ni->ni_ath_flags & IEEE80211_NODE_FF) {
- /*
- * Check queue length; if too deep drop this
- * frame (tail drop considered good).
- */
- if (txq->axq_depth >= sc->sc_fftxqmax) {
- DPRINTF(sc, ATH_DEBUG_FF,
- "[%s] tail drop on q %u depth %u\n",
- ether_sprintf(ni->ni_macaddr),
- txq->axq_qnum, txq->axq_depth);
- sc->sc_stats.ast_tx_qfull++;
- m_freem(m);
- goto reclaim;
- }
- m = ath_ff_check(sc, txq, bf, m, ni);
- if (m == NULL) {
- /* NB: ni ref & bf held on stageq */
- continue;
- }
- }
- ifp->if_opackets++;
- BPF_MTAP(ifp, m);
- /*
- * Encapsulate the packet in prep for transmission.
- */
- m = ieee80211_encap(ic, m, ni);
+ m = ath_ff_check(sc, txq, bf, m, ni);
if (m == NULL) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: encapsulation failure\n",
- __func__);
- sc->sc_stats.ast_tx_encap++;
- goto bad;
+ /* NB: ni ref & bf held on stageq */
+ continue;
}
- /*
- * Check for fragmentation. If this frame
- * has been broken up verify we have enough
- * buffers to send all the fragments so all
- * go out or none...
- */
- if ((m->m_flags & M_FRAG) &&
- !ath_txfrag_setup(sc, &frags, m, ni)) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: out of txfrag buffers\n", __func__);
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ath_freetx(m);
- goto bad;
- }
- } else {
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
- m->m_pkthdr.rcvif = NULL;
-
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
- /* fill time stamp */
- u_int64_t tsf;
- u_int32_t *tstamp;
-
- tsf = ath_hal_gettsf64(ah);
- /* XXX: adjust 100us delay to xmit */
- tsf += 100;
- tstamp = (u_int32_t *)&wh[1];
- tstamp[0] = htole32(tsf & 0xffffffff);
- tstamp[1] = htole32(tsf >> 32);
- }
- sc->sc_stats.ast_tx_mgmt++;
}
-
+ ifp->if_opackets++;
+ /*
+ * Encapsulate the packet in prep for transmission.
+ */
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: encapsulation failure\n", __func__);
+ sc->sc_stats.ast_tx_encap++;
+ goto bad;
+ }
+ /*
+ * Check for fragmentation. If this frame
+ * has been broken up verify we have enough
+ * buffers to send all the fragments so all
+ * go out or none...
+ */
+ if ((m->m_flags & M_FRAG) &&
+ !ath_txfrag_setup(sc, &frags, m, ni)) {
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: out of txfrag buffers\n", __func__);
+ ic->ic_stats.is_tx_nobuf++; /* XXX */
+ ath_freetx(m);
+ goto bad;
+ }
nextfrag:
/*
* Pass the frame to the h/w for transmission.
@@ -1735,11 +2010,11 @@ ath_start(struct ifnet *ifp)
* Beware of state changing between frags.
* XXX check sta power-save state?
*/
- if (ic->ic_state != IEEE80211_S_RUN) {
+ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: flush fragmented packet, state %s\n",
__func__,
- ieee80211_state_name[ic->ic_state]);
+ ieee80211_state_name[ni->ni_vap->iv_state]);
ath_freetx(next);
goto reclaim;
}
@@ -1751,7 +2026,6 @@ ath_start(struct ifnet *ifp)
}
ifp->if_timer = 5;
- ic->ic_lastdata = ticks;
#if 0
/*
* Flush stale frames from the fast-frame staging queue.
@@ -1765,30 +2039,9 @@ ath_start(struct ifnet *ifp)
static int
ath_media_change(struct ifnet *ifp)
{
-#define IS_UP(ifp) \
- ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- /*
- * Adhoc demo mode is just ibss mode w/o beacons
- * (mostly). The hal knows nothing about it;
- * tell it we're operating in ibss mode.
- */
- sc->sc_opmode = HAL_M_IBSS;
- } else
- sc->sc_opmode = ic->ic_opmode;
- if (IS_UP(ifp))
- ath_init(sc); /* XXX lose error */
- error = 0;
- }
- return error;
-#undef IS_UP
+ int error = ieee80211_media_change(ifp);
+ /* NB: only the fixed rate can change and that doesn't need a reset */
+ return (error == ENETRESET ? 0 : error);
}
#ifdef ATH_DEBUG
@@ -1860,7 +2113,7 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
/*
* Room for both TX+RX MIC keys in one key cache
* slot, just set key at the first index; the hal
- * will handle the reset.
+ * will handle the rest.
*/
memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
#if HAL_ABI_VERSION > 0x06052200
@@ -1869,13 +2122,16 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
KEYPRINTF(sc, k->wk_keyix, hk, mac);
return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
}
- } else if (k->wk_flags & IEEE80211_KEY_XR) {
- /*
- * TX/RX key goes at first index.
- * The hal handles the MIC keys are index+64.
- */
- memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
- k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
+ } else if (k->wk_flags & IEEE80211_KEY_XMIT) {
+#if HAL_ABI_VERSION > 0x06052200
+ memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic));
+#else
+ memcpy(hk->kv_mic, k->wk_mic, sizeof(hk->kv_mic));
+#endif
+ KEYPRINTF(sc, k->wk_keyix, hk, mac);
+ return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+ } else if (k->wk_flags & IEEE80211_KEY_RECV) {
+ memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
KEYPRINTF(sc, k->wk_keyix, hk, mac);
return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
}
@@ -2091,10 +2347,10 @@ key_alloc_single(struct ath_softc *sc,
* 64 entries.
*/
static int
-ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+ath_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
/*
* Group key allocation must be handled specially for
@@ -2108,8 +2364,8 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* multi-station operation.
*/
if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
- if (!(&ic->ic_nw_keys[0] <= k &&
- k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/* should not happen */
DPRINTF(sc, ATH_DEBUG_KEYCACHE,
"%s: bogus group key\n", __func__);
@@ -2119,7 +2375,7 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* XXX we pre-allocate the global keys so
* have no way to check if they've already been allocated.
*/
- *keyix = *rxkeyix = k - ic->ic_nw_keys;
+ *keyix = *rxkeyix = k - vap->iv_nw_keys;
return 1;
}
@@ -2148,9 +2404,9 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* Delete an entry in the key cache allocated by ath_key_alloc.
*/
static int
-ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
struct ath_hal *ah = sc->sc_ah;
const struct ieee80211_cipher *cip = k->wk_cipher;
u_int keyix = k->wk_keyix;
@@ -2188,12 +2444,12 @@ ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
* slot(s) must already have been allocated by ath_key_alloc.
*/
static int
-ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const u_int8_t mac[IEEE80211_ADDR_LEN])
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
- return ath_keyset(sc, k, mac, ic->ic_bss);
+ return ath_keyset(sc, k, mac, vap->iv_bss);
}
/*
@@ -2203,29 +2459,25 @@ ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
* uses that originate in the driver.
*/
static void
-ath_key_update_begin(struct ieee80211com *ic)
+ath_key_update_begin(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ath_softc *sc = ifp->if_softc;
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
-#if 0
- tasklet_disable(&sc->sc_rxtq);
-#endif
+ taskqueue_block(sc->sc_tq);
IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */
}
static void
-ath_key_update_end(struct ieee80211com *ic)
+ath_key_update_end(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ath_softc *sc = ifp->if_softc;
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
IF_UNLOCK(&ifp->if_snd);
-#if 0
- tasklet_enable(&sc->sc_rxtq);
-#endif
+ taskqueue_unblock(sc->sc_tq);
}
/*
@@ -2233,80 +2485,101 @@ ath_key_update_end(struct ieee80211com *ic)
* operating mode and state:
*
* o always accept unicast, broadcast, and multicast traffic
- * o maintain current state of phy error reception (the hal
- * may enable phy error frames for noise immunity work)
+ * o accept PHY error frames when hardware doesn't have MIB support
+ * to count and we need them for ANI (sta mode only at the moment)
+ * and we are not scanning (ANI is disabled)
+ * NB: only with recent hal's; older hal's add rx filter bits out
+ * of sight and we need to blindly preserve them
* o probe request frames are accepted only when operating in
* hostap, adhoc, or monitor modes
- * o enable promiscuous mode according to the interface state
+ * o enable promiscuous mode
+ * - when in monitor mode
+ * - if interface marked PROMISC (assumes bridge setting is filtered)
* o accept beacons:
- * - when operating in adhoc mode so the 802.11 layer creates
- * node table entries for peers,
* - when operating in station mode for collecting rssi data when
* the station is otherwise quiet, or
+ * - when operating in adhoc mode so the 802.11 layer creates
+ * node table entries for peers,
* - when scanning
+ * - when doing s/w beacon miss (e.g. for ap+sta)
+ * - when operating in ap mode in 11g to detect overlapping bss that
+ * require protection
* o accept control frames:
* - when in monitor mode
+ * XXX BAR frames for 11n
+ * XXX HT protection for 11n
*/
static u_int32_t
ath_calcrxfilter(struct ath_softc *sc)
{
-#define RX_FILTER_PRESERVE (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
u_int32_t rfilt;
- rfilt = (ath_hal_getrxfilter(ah) & RX_FILTER_PRESERVE)
+#if HAL_ABI_VERSION < 0x08011600
+ rfilt = (ath_hal_getrxfilter(sc->sc_ah) &
+ (HAL_RX_FILTER_PHYRADAR | HAL_RX_FILTER_PHYERR))
| HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+#else
+ rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ !sc->sc_needmib && !sc->sc_scanning)
+ rfilt |= HAL_RX_FILTER_PHYERR;
+#endif
if (ic->ic_opmode != IEEE80211_M_STA)
rfilt |= HAL_RX_FILTER_PROBEREQ;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- (ifp->if_flags & IFF_PROMISC))
+ if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC))
rfilt |= HAL_RX_FILTER_PROM;
if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- sc->sc_scanning)
+ sc->sc_opmode == HAL_M_IBSS ||
+ sc->sc_swbmiss || sc->sc_scanning)
+ rfilt |= HAL_RX_FILTER_BEACON;
+ /*
+ * NB: We don't recalculate the rx filter when
+ * ic_protmode changes; otherwise we could do
+ * this only when ic_protmode != NONE.
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
rfilt |= HAL_RX_FILTER_BEACON;
if (ic->ic_opmode == IEEE80211_M_MONITOR)
rfilt |= HAL_RX_FILTER_CONTROL;
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n",
+ __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags);
return rfilt;
-#undef RX_FILTER_PRESERVE
}
static void
-ath_mode_init(struct ath_softc *sc)
+ath_update_promisc(struct ifnet *ifp)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- struct ifnet *ifp = sc->sc_ifp;
- u_int32_t rfilt, mfilt[2], val;
- u_int8_t pos;
- struct ifmultiaddr *ifma;
+ struct ath_softc *sc = ifp->if_softc;
+ u_int32_t rfilt;
/* configure rx filter */
rfilt = ath_calcrxfilter(sc);
- ath_hal_setrxfilter(ah, rfilt);
+ ath_hal_setrxfilter(sc->sc_ah, rfilt);
- /* configure operational mode */
- ath_hal_setopmode(ah);
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt);
+}
- /*
- * Handle any link-level address change. Note that we only
- * need to force ic_myaddr; any other addresses are handled
- * as a byproduct of the ifnet code marking the interface
- * down then up.
- *
- * XXX should get from lladdr instead of arpcom but that's more work
- */
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- ath_hal_setmac(ah, ic->ic_myaddr);
+static void
+ath_update_mcast(struct ifnet *ifp)
+{
+ struct ath_softc *sc = ifp->if_softc;
+ u_int32_t mfilt[2];
/* calculate and install multicast filter */
if ((ifp->if_flags & IFF_ALLMULTI) == 0) {
+ struct ifmultiaddr *ifma;
+ /*
+ * Merge multicast addresses to form the hardware filter.
+ */
mfilt[0] = mfilt[1] = 0;
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_LOCK(ifp); /* XXX need some fiddling to remove? */
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
caddr_t dl;
+ u_int32_t val;
+ u_int8_t pos;
/* calculate XOR of eight 6bit values */
dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
@@ -2318,12 +2591,41 @@ ath_mode_init(struct ath_softc *sc)
mfilt[pos / 32] |= (1 << (pos % 32));
}
IF_ADDR_UNLOCK(ifp);
- } else {
+ } else
mfilt[0] = mfilt[1] = ~0;
- }
- ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
- DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n",
- __func__, rfilt, mfilt[0], mfilt[1]);
+ ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]);
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n",
+ __func__, mfilt[0], mfilt[1]);
+}
+
+static void
+ath_mode_init(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt;
+
+ /* configure rx filter */
+ rfilt = ath_calcrxfilter(sc);
+ ath_hal_setrxfilter(ah, rfilt);
+
+ /* configure operational mode */
+ ath_hal_setopmode(ah);
+
+ /*
+ * Handle any link-level address change. Note that we only
+ * need to force ic_myaddr; any other addresses are handled
+ * as a byproduct of the ifnet code marking the interface
+ * down then up.
+ *
+ * XXX should get from lladdr instead of arpcom but that's more work
+ */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ ath_hal_setmac(ah, ic->ic_myaddr);
+
+ /* calculate and install multicast filter */
+ ath_update_mcast(ifp);
}
/*
@@ -2332,7 +2634,7 @@ ath_mode_init(struct ath_softc *sc)
static void
ath_setslottime(struct ath_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
u_int usec;
@@ -2367,7 +2669,7 @@ static void
ath_updateslot(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
/*
* When not coordinating the BSS, change the hardware
@@ -2404,7 +2706,7 @@ static int
ath_beaconq_config(struct ath_softc *sc)
{
#define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_TXQ_INFO qi;
@@ -2444,38 +2746,81 @@ ath_beaconq_config(struct ath_softc *sc)
static int
ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_buf *bf;
struct mbuf *m;
int error;
- bf = STAILQ_FIRST(&sc->sc_bbuf);
- if (bf == NULL) {
- DPRINTF(sc, ATH_DEBUG_BEACON, "%s: no dma buffers\n", __func__);
- sc->sc_stats.ast_be_nombuf++; /* XXX */
- return ENOMEM; /* XXX */
+ bf = avp->av_bcbuf;
+ if (bf->bf_m != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ }
+ if (bf->bf_node != NULL) {
+ ieee80211_free_node(bf->bf_node);
+ bf->bf_node = NULL;
}
+
/*
* NB: the beacon data buffer must be 32-bit aligned;
* we assume the mbuf routines will return us something
* with this alignment (perhaps should assert).
*/
- m = ieee80211_beacon_alloc(ni, &sc->sc_boff);
+ m = ieee80211_beacon_alloc(ni, &avp->av_boff);
if (m == NULL) {
- DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get mbuf\n",
- __func__);
+ device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__);
sc->sc_stats.ast_be_nombuf++;
return ENOMEM;
}
error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
bf->bf_segs, &bf->bf_nseg,
BUS_DMA_NOWAIT);
- if (error == 0) {
- bf->bf_m = m;
- bf->bf_node = ieee80211_ref_node(ni);
- } else {
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n",
+ __func__, error);
m_freem(m);
+ return error;
}
- return error;
+
+ /*
+ * Calculate a TSF adjustment factor required for staggered
+ * beacons. Note that we assume the format of the beacon
+ * frame leaves the tstamp field immediately following the
+ * header.
+ */
+ if (sc->sc_stagbeacons && avp->av_bslot > 0) {
+ uint64_t tsfadjust;
+ struct ieee80211_frame *wh;
+
+ /*
+ * The beacon interval is in TU's; the TSF is in usecs.
+ * We figure out how many TU's to add to align the timestamp
+ * then convert to TSF units and handle byte swapping before
+ * inserting it in the frame. The hardware will then add this
+ * each time a beacon frame is sent. Note that we align vap's
+ * 1..N and leave vap 0 untouched. This means vap 0 has a
+ * timestamp in one beacon interval while the others get a
+ * timstamp aligned to the next interval.
+ */
+ tsfadjust = ni->ni_intval *
+ (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF;
+ tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */
+
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: %s beacons bslot %d intval %u tsfadjust %llu\n",
+ __func__, sc->sc_stagbeacons ? "stagger" : "burst",
+ avp->av_bslot, ni->ni_intval, le64toh(tsfadjust));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
+ }
+ bf->bf_m = m;
+ bf->bf_node = ieee80211_ref_node(ni);
+
+ return 0;
}
/*
@@ -2516,8 +2861,12 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
* Switch antenna every 4 beacons.
* XXX assumes two antenna
*/
- antenna = sc->sc_txantenna != 0 ? sc->sc_txantenna
- : (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
+ if (sc->sc_txantenna != 0)
+ antenna = sc->sc_txantenna;
+ else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0)
+ antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1);
+ else
+ antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
}
KASSERT(bf->bf_nseg == 1,
@@ -2527,7 +2876,7 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
* Calculate rate code.
* XXX everything at min xmit rate
*/
- rix = sc->sc_minrateix;
+ rix = 0;
rt = sc->sc_currates;
rate = rt->info[rix].rateCode;
if (USE_SHPREAMBLE(ic))
@@ -2551,14 +2900,16 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
, AH_TRUE /* last segment */
, ds /* first descriptor */
);
+#if 0
+ ath_desc_swap(ds);
+#endif
#undef USE_SHPREAMBLE
}
static void
-ath_beacon_update(struct ieee80211com *ic, int item)
+ath_beacon_update(struct ieee80211vap *vap, int item)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
- struct ieee80211_beacon_offsets *bo = &sc->sc_boff;
+ struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff;
setbit(bo->bo_flags, item);
}
@@ -2586,24 +2937,14 @@ static void
ath_beacon_proc(void *arg, int pending)
{
struct ath_softc *sc = arg;
- struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
- struct ieee80211_node *ni = bf->bf_node;
- struct ieee80211com *ic = ni->ni_ic;
struct ath_hal *ah = sc->sc_ah;
- struct ath_txq *cabq = sc->sc_cabq;
- struct mbuf *m;
- int ncabq, nmcastq, error, otherant;
+ struct ieee80211vap *vap;
+ struct ath_buf *bf;
+ int slot, otherant;
+ uint32_t bfaddr;
DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
__func__, pending);
-
- if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_opmode == IEEE80211_M_MONITOR ||
- bf == NULL || bf->bf_m == NULL) {
- DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n",
- __func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL);
- return;
- }
/*
* Check if the previous beacon has gone out. If
* not don't try to post another, skip this period
@@ -2627,39 +2968,34 @@ ath_beacon_proc(void *arg, int pending)
sc->sc_bmisscount = 0;
}
- /*
- * Update dynamic beacon contents. If this returns
- * non-zero then we need to remap the memory because
- * the beacon frame changed size (probably because
- * of the TIM bitmap).
- */
- m = bf->bf_m;
- nmcastq = sc->sc_mcastq.axq_depth;
- ncabq = ath_hal_numtxpending(ah, cabq->axq_qnum);
- if (ieee80211_beacon_update(bf->bf_node, &sc->sc_boff, m, ncabq+nmcastq)) {
- /* XXX too conservative? */
- bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
- error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
- bf->bf_segs, &bf->bf_nseg,
- BUS_DMA_NOWAIT);
- if (error != 0) {
- if_printf(ic->ic_ifp,
- "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
- __func__, error);
- return;
+ if (sc->sc_stagbeacons) { /* staggered beacons */
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ uint32_t tsftu;
+
+ tsftu = ath_hal_gettsf32(ah) >> 10;
+ /* XXX lintval */
+ slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval;
+ vap = sc->sc_bslot[(slot+1) % ATH_BCBUF];
+ bfaddr = 0;
+ if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) {
+ bf = ath_beacon_generate(sc, vap);
+ if (bf != NULL)
+ bfaddr = bf->bf_daddr;
}
- }
- if (ncabq && (sc->sc_boff.bo_tim[4] & 1)) {
- /*
- * CABQ traffic from the previous DTIM is still pending.
- * This is ok for now but when there are multiple vap's
- * and we are using staggered beacons we'll want to drain
- * the cabq before loading frames for the different vap.
- */
- DPRINTF(sc, ATH_DEBUG_BEACON,
- "%s: cabq did not drain, mcastq %u cabq %u/%u\n",
- __func__, nmcastq, ncabq, cabq->axq_depth);
- sc->sc_stats.ast_cabq_busy++;
+ } else { /* burst'd beacons */
+ uint32_t *bflink = &bfaddr;
+
+ for (slot = 0; slot < ATH_BCBUF; slot++) {
+ vap = sc->sc_bslot[slot];
+ if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) {
+ bf = ath_beacon_generate(sc, vap);
+ if (bf != NULL) {
+ *bflink = bf->bf_daddr;
+ bflink = &bf->bf_desc->ds_link;
+ }
+ }
+ }
+ *bflink = 0; /* terminate list */
}
/*
@@ -2670,9 +3006,10 @@ ath_beacon_proc(void *arg, int pending)
* beacon interval to note the state change.
*/
/* XXX locking */
- if (sc->sc_updateslot == UPDATE)
+ if (sc->sc_updateslot == UPDATE) {
sc->sc_updateslot = COMMIT; /* commit next beacon */
- else if (sc->sc_updateslot == COMMIT)
+ sc->sc_slotupdate = slot;
+ } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
ath_setslottime(sc); /* commit change to h/w */
/*
@@ -2681,64 +3018,159 @@ ath_beacon_proc(void *arg, int pending)
* on the non-default antenna.
* XXX assumes 2 anntenae
*/
- otherant = sc->sc_defant & 1 ? 2 : 1;
- if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
- ath_setdefantenna(sc, otherant);
- sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+ if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) {
+ otherant = sc->sc_defant & 1 ? 2 : 1;
+ if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
+ ath_setdefantenna(sc, otherant);
+ sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+ }
- /*
- * Construct tx descriptor.
- */
- ath_beacon_setup(sc, bf);
+ if (bfaddr != 0) {
+ /*
+ * Stop any current dma and put the new frame on the queue.
+ * This should never fail since we check above that no frames
+ * are still pending on the queue.
+ */
+ if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+ DPRINTF(sc, ATH_DEBUG_ANY,
+ "%s: beacon queue %u did not stop?\n",
+ __func__, sc->sc_bhalq);
+ }
+ /* NB: cabq traffic should already be queued and primed */
+ ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr);
+ ath_hal_txstart(ah, sc->sc_bhalq);
+
+ sc->sc_stats.ast_be_xmit++;
+ }
+}
+
+static struct ath_buf *
+ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+ struct ath_vap *avp = ATH_VAP(vap);
+ struct ath_txq *cabq = sc->sc_cabq;
+ struct ath_buf *bf;
+ struct mbuf *m;
+ int nmcastq, error;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ ("not running, state %d", vap->iv_state));
+ KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
/*
- * Stop any current dma and put the new frame on the queue.
- * This should never fail since we check above that no frames
- * are still pending on the queue.
+ * Update dynamic beacon contents. If this returns
+ * non-zero then we need to remap the memory because
+ * the beacon frame changed size (probably because
+ * of the TIM bitmap).
*/
- if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
- DPRINTF(sc, ATH_DEBUG_ANY,
- "%s: beacon queue %u did not stop?\n",
- __func__, sc->sc_bhalq);
+ bf = avp->av_bcbuf;
+ m = bf->bf_m;
+ nmcastq = avp->av_mcastq.axq_depth;
+ if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) {
+ /* XXX too conservative? */
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(vap->iv_ifp,
+ "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+ __func__, error);
+ return NULL;
+ }
}
+ if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) {
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: cabq did not drain, mcastq %u cabq %u\n",
+ __func__, nmcastq, cabq->axq_depth);
+ sc->sc_stats.ast_cabq_busy++;
+ if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) {
+ /*
+ * CABQ traffic from a previous vap is still pending.
+ * We must drain the q before this beacon frame goes
+ * out as otherwise this vap's stations will get cab
+ * frames from a different vap.
+ * XXX could be slow causing us to miss DBA
+ */
+ ath_tx_draintxq(sc, cabq);
+ }
+ }
+ ath_beacon_setup(sc, bf);
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
/*
* Enable the CAB queue before the beacon queue to
* insure cab frames are triggered by this beacon.
*/
- if (sc->sc_boff.bo_tim_len && (sc->sc_boff.bo_tim[4] & 1)) {
+ if (avp->av_boff.bo_tim[4] & 1) {
+ struct ath_hal *ah = sc->sc_ah;
+
/* NB: only at DTIM */
ATH_TXQ_LOCK(cabq);
- ATH_TXQ_LOCK(&sc->sc_mcastq);
+ ATH_TXQ_LOCK(&avp->av_mcastq);
if (nmcastq) {
struct ath_buf *bfm;
/*
* Move frames from the s/w mcast q to the h/w cab q.
+ * XXX MORE_DATA bit
*/
- bfm = STAILQ_FIRST(&sc->sc_mcastq.axq_q);
+ bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q);
if (cabq->axq_link != NULL) {
*cabq->axq_link = bfm->bf_daddr;
} else
ath_hal_puttxbuf(ah, cabq->axq_qnum,
bfm->bf_daddr);
- ath_txqmove(cabq, &sc->sc_mcastq);
+ ath_txqmove(cabq, &avp->av_mcastq);
sc->sc_stats.ast_cabq_xmit += nmcastq;
}
/* NB: gated by beacon so safe to start here */
ath_hal_txstart(ah, cabq->axq_qnum);
ATH_TXQ_UNLOCK(cabq);
- ATH_TXQ_UNLOCK(&sc->sc_mcastq);
+ ATH_TXQ_UNLOCK(&avp->av_mcastq);
}
+ return bf;
+}
+
+static void
+ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+ struct ath_vap *avp = ATH_VAP(vap);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_buf *bf;
+ struct mbuf *m;
+ int error;
+
+ KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
+
+ /*
+ * Update dynamic beacon contents. If this returns
+ * non-zero then we need to remap the memory because
+ * the beacon frame changed size (probably because
+ * of the TIM bitmap).
+ */
+ bf = avp->av_bcbuf;
+ m = bf->bf_m;
+ if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) {
+ /* XXX too conservative? */
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(vap->iv_ifp,
+ "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+ __func__, error);
+ return;
+ }
+ }
+ ath_beacon_setup(sc, bf);
+ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+ /* NB: caller is known to have already stopped tx dma */
ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
ath_hal_txstart(ah, sc->sc_bhalq);
- DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
- "%s: TXDP[%u] = %p (%p)\n", __func__,
- sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
-
- sc->sc_stats.ast_be_xmit++;
}
/*
@@ -2756,6 +3188,25 @@ ath_bstuck_proc(void *arg, int pending)
}
/*
+ * Reclaim beacon resources and return buffer to the pool.
+ */
+static void
+ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+ if (bf->bf_m != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ }
+ if (bf->bf_node != NULL) {
+ ieee80211_free_node(bf->bf_node);
+ bf->bf_node = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
+}
+
+/*
* Reclaim beacon resources.
*/
static void
@@ -2792,29 +3243,46 @@ ath_beacon_free(struct ath_softc *sc)
* we've associated with.
*/
static void
-ath_beacon_config(struct ath_softc *sc)
+ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
{
#define TSF_TO_TU(_h,_l) \
((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
#define FUDGE 2
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ struct ieee80211_node *ni;
u_int32_t nexttbtt, intval, tsftu;
u_int64_t tsf;
+ if (vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
+ ni = vap->iv_bss;
+
/* extract tstamp from last beacon and convert to TU */
nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
LE_READ_4(ni->ni_tstamp.data));
- /* NB: the beacon interval is kept internally in TU's */
- intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * For multi-bss ap support beacons are either staggered
+ * evenly over N slots or burst together. For the former
+ * arrange for the SWBA to be delivered for each slot.
+ * Slots that are not occupied will generate nothing.
+ */
+ /* NB: the beacon interval is kept internally in TU's */
+ intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ if (sc->sc_stagbeacons)
+ intval /= ATH_BCBUF;
+ } else {
+ /* NB: the beacon interval is kept internally in TU's */
+ intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ }
if (nexttbtt == 0) /* e.g. for ap mode */
nexttbtt = intval;
else if (intval) /* NB: can be 0 for monitor mode */
nexttbtt = roundup(nexttbtt, intval);
DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
__func__, nexttbtt, intval, ni->ni_intval);
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) {
HAL_BEACON_STATE bs;
int dtimperiod, dtimcount;
int cfpperiod, cfpcount;
@@ -2870,7 +3338,7 @@ ath_beacon_config(struct ath_softc *sc)
* before taking a BMISS interrupt.
* Note that we clamp the result to at most 10 beacons.
*/
- bs.bs_bmissthreshold = ic->ic_bmissthreshold;
+ bs.bs_bmissthreshold = vap->iv_bmissthreshold;
if (bs.bs_bmissthreshold > 10)
bs.bs_bmissthreshold = 10;
else if (bs.bs_bmissthreshold <= 0)
@@ -2953,7 +3421,7 @@ ath_beacon_config(struct ath_softc *sc)
* ibss mode load it once here.
*/
if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
- ath_beacon_proc(sc, 0);
+ ath_beacon_start_adhoc(sc, vap);
}
sc->sc_syncbeacon = 0;
#undef FUDGE
@@ -3130,7 +3598,7 @@ ath_desc_alloc(struct ath_softc *sc)
}
error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
- "beacon", 1, 1);
+ "beacon", ATH_BCBUF, 1);
if (error != 0) {
ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
@@ -3164,7 +3632,6 @@ ath_node_alloc(struct ieee80211_node_table *nt)
/* XXX stat+msg */
return NULL;
}
- an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
ath_rate_node_init(sc, an);
DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
@@ -3183,26 +3650,6 @@ ath_node_free(struct ieee80211_node *ni)
sc->sc_node_free(ni);
}
-static int8_t
-ath_node_getrssi(const struct ieee80211_node *ni)
-{
-#define HAL_EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
- u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
- int32_t rssi;
-
- /*
- * When only one frame is received there will be no state in
- * avgrssi so fallback on the value recorded by the 802.11 layer.
- */
- if (avgrssi != ATH_RSSI_DUMMY_MARKER)
- rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
- else
- rssi = ni->ni_rssi;
- return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
-#undef HAL_EP_RND
-}
-
static void
ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
{
@@ -3211,7 +3658,7 @@ ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
struct ath_hal *ah = sc->sc_ah;
HAL_CHANNEL hchan;
- *rssi = ath_node_getrssi(ni);
+ *rssi = ic->ic_node_getrssi(ni);
if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
ath_mapchan(&hchan, ni->ni_chan);
*noise = ath_hal_getchannoise(ah, &hchan);
@@ -3309,33 +3756,33 @@ ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf)
* and to do ibss merges.
*/
static void
-ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni,
+ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, int rssi, int noise, u_int32_t rstamp)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
/*
* Call up first so subsequent work can use information
* potentially stored in the node (e.g. for ibss merge).
*/
- sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, noise, rstamp);
+ ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
/* update rssi statistics for use by the hal */
ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
if (sc->sc_syncbeacon &&
- ni == ic->ic_bss && ic->ic_state == IEEE80211_S_RUN) {
+ ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
/*
* Resync beacon timers using the tsf of the beacon
* frame we just received.
*/
- ath_beacon_config(sc);
+ ath_beacon_config(sc, vap);
}
/* fall thru... */
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- ic->ic_state == IEEE80211_S_RUN) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ vap->iv_state == IEEE80211_S_RUN) {
u_int64_t tsf = ath_extend_tsf(rstamp,
ath_hal_gettsf64(sc->sc_ah));
/*
@@ -3377,14 +3824,13 @@ ath_setdefantenna(struct ath_softc *sc, u_int antenna)
}
static int
-ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
+ath_rx_tap(struct ifnet *ifp, struct mbuf *m,
const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
{
#define CHANNEL_HT (CHANNEL_HT20|CHANNEL_HT40PLUS|CHANNEL_HT40MINUS)
+ struct ath_softc *sc = ifp->if_softc;
u_int8_t rix;
- KASSERT(sc->sc_drvbpf != NULL, ("no tap"));
-
/*
* Discard anything shorter than an ack or cts.
*/
@@ -3425,13 +3871,28 @@ ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
sc->sc_rx_th.wr_antnoise = nf;
sc->sc_rx_th.wr_antenna = rs->rs_antenna;
- bpf_mtap2(sc->sc_drvbpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
return 1;
#undef CHANNEL_HT
}
static void
+ath_handle_micerror(struct ieee80211com *ic,
+ struct ieee80211_frame *wh, int keyix)
+{
+ struct ieee80211_node *ni;
+
+ /* XXX recheck MIC to deal w/ chips that lie */
+ /* XXX discard MIC errors on !data frames */
+ ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
+ if (ni != NULL) {
+ ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix);
+ ieee80211_free_node(ni);
+ }
+}
+
+static void
ath_rx_proc(void *arg, int npending)
{
#define PA2DESC(_sc, _pa) \
@@ -3439,21 +3900,19 @@ ath_rx_proc(void *arg, int npending)
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_softc *sc = arg;
struct ath_buf *bf;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
struct ath_desc *ds;
struct ath_rx_status *rs;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ath_node *an;
int len, type, ngood;
u_int phyerr;
HAL_STATUS status;
int16_t nf;
u_int64_t tsf;
-
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
ngood = 0;
nf = ath_hal_getchannoise(ah, &sc->sc_curchan);
@@ -3540,11 +3999,10 @@ ath_rx_proc(void *arg, int npending)
bus_dmamap_sync(sc->sc_dmat,
bf->bf_dmamap,
BUS_DMASYNC_POSTREAD);
- ieee80211_notify_michael_failure(ic,
+ ath_handle_micerror(ic,
mtod(m, struct ieee80211_frame *),
sc->sc_splitmic ?
- rs->rs_keyix-32 : rs->rs_keyix
- );
+ rs->rs_keyix-32 : rs->rs_keyix);
}
}
ifp->if_ierrors++;
@@ -3562,14 +4020,14 @@ rx_error:
* pass decrypt+mic errors but others may be
* interesting (e.g. crc).
*/
- if (bpf_peers_present(sc->sc_drvbpf) &&
+ if (bpf_peers_present(ifp->if_bpf) &&
(rs->rs_status & sc->sc_monpass)) {
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
BUS_DMASYNC_POSTREAD);
/* NB: bpf needs the mbuf length setup */
len = rs->rs_datalen;
m->m_pkthdr.len = m->m_len = len;
- (void) ath_rx_tap(sc, m, rs, tsf, nf);
+ (void) ath_rx_tap(ifp, m, rs, tsf, nf);
}
/* XXX pass MIC errors up for s/w reclaculation */
goto rx_next;
@@ -3624,10 +4082,11 @@ rx_accept:
m->m_pkthdr.len = len;
}
+ ifp->if_ipackets++;
sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
- if (bpf_peers_present(sc->sc_drvbpf) &&
- !ath_rx_tap(sc, m, rs, tsf, nf)) {
+ if (bpf_peers_present(ifp->if_bpf) &&
+ !ath_rx_tap(ifp, m, rs, tsf, nf)) {
m_freem(m); /* XXX reclaim */
goto rx_next;
}
@@ -3661,18 +4120,30 @@ rx_accept:
mtod(m, const struct ieee80211_frame_min *),
rs->rs_keyix == HAL_RXKEYIX_INVALID ?
IEEE80211_KEYIX_NONE : rs->rs_keyix);
+ if (ni != NULL) {
+ /*
+ * Sending station is known, dispatch directly.
+ */
+ type = ieee80211_input(ni, m,
+ rs->rs_rssi, nf, rs->rs_tstamp);
+ ieee80211_free_node(ni);
+ /*
+ * Arrange to update the last rx timestamp only for
+ * frames from our ap when operating in station mode.
+ * This assumes the rx key is always setup when
+ * associated.
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ rs->rs_keyix != HAL_RXKEYIX_INVALID)
+ ngood++;
+ } else {
+ type = ieee80211_input_all(ic, m,
+ rs->rs_rssi, nf, rs->rs_tstamp);
+ }
/*
* Track rx rssi and do any rx antenna management.
*/
- an = ATH_NODE(ni);
- ATH_RSSI_LPF(an->an_avgrssi, rs->rs_rssi);
ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
- /*
- * Send frame up for processing.
- */
- type = ieee80211_input(ic, m, ni,
- rs->rs_rssi, nf, rs->rs_tstamp);
- ieee80211_free_node(ni);
if (sc->sc_diversity) {
/*
* When using fast diversity, change the default rx
@@ -3698,14 +4169,6 @@ rx_accept:
} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
ath_led_event(sc, ATH_LED_POLL);
}
- /*
- * Arrange to update the last rx timestamp only for
- * frames from our ap when operating in station mode.
- * This assumes the rx key is always setup when associated.
- */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- rs->rs_keyix != HAL_RXKEYIX_INVALID)
- ngood++;
rx_next:
STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
} while (ath_rxbuf_init(sc, bf) == 0);
@@ -3715,7 +4178,6 @@ rx_next:
if (ngood)
sc->sc_lastrx = tsf;
- /* NB: may want to check mgtq too */
if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
!IFQ_IS_EMPTY(&ifp->if_snd))
ath_start(ifp);
@@ -3825,7 +4287,8 @@ ath_txq_update(struct ath_softc *sc, int ac)
{
#define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1)
#define ATH_TXOP_TO_US(v) (v<<5)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_txq *txq = sc->sc_ac2q[ac];
struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
struct ath_hal *ah = sc->sc_ah;
@@ -3838,7 +4301,7 @@ ath_txq_update(struct ath_softc *sc, int ac)
qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
- device_printf(sc->sc_dev, "unable to update hardware queue "
+ if_printf(ifp, "unable to update hardware queue "
"parameters for %s traffic!\n",
ieee80211_wme_acnames[ac]);
return 0;
@@ -3888,7 +4351,6 @@ ath_tx_cleanup(struct ath_softc *sc)
for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->sc_txq[i]);
- ATH_TXQ_LOCK_DESTROY(&sc->sc_mcastq);
}
/*
@@ -4016,8 +4478,8 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
* to avoid possible races.
*/
ATH_TXQ_LOCK(txq);
- ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
- if (txq != &sc->sc_mcastq) {
+ if (txq->axq_qnum != ATH_TXQ_SWQ) {
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
if (txq->axq_link == NULL) {
ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
DPRINTF(sc, ATH_DEBUG_XMIT,
@@ -4034,8 +4496,20 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
ath_hal_txstart(ah, txq->axq_qnum);
} else {
- if (txq->axq_link != NULL)
+ if (txq->axq_link != NULL) {
+ struct ath_buf *last = ATH_TXQ_LAST(txq);
+ struct ieee80211_frame *wh;
+
+ /* mark previous frame */
+ wh = mtod(last->bf_m, struct ieee80211_frame *);
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ /* link descriptor */
*txq->axq_link = bf->bf_daddr;
+ }
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
}
ATH_TXQ_UNLOCK(txq);
@@ -4045,9 +4519,11 @@ static int
ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
struct mbuf *m0)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_hal *ah = sc->sc_ah;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
int error, iswep, ismcast, isfrag, ismrr;
int keyix, hdrlen, pktlen, try0;
@@ -4083,7 +4559,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* frame. The only reason this can fail is because of an
* unknown or unsupported cipher/key type.
*/
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
/*
* This can happen when the key is yanked after the
@@ -4156,6 +4632,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
an = ATH_NODE(ni);
flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
ismrr = 0; /* default no multi-rate retry*/
+ pri = M_WME_GETAC(m0); /* honor classification */
/*
* Calculate Atheros packet type from IEEE80211 packet header,
* setup for rate calculations, and select h/w transmit queue.
@@ -4171,32 +4648,20 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
atype = HAL_PKT_TYPE_ATIM;
else
atype = HAL_PKT_TYPE_NORMAL; /* XXX */
- rix = sc->sc_minrateix;
+ rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
- /* NB: force all management frames to highest queue */
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all management frames to highest queue */
- pri = WME_AC_VO;
- } else
- pri = WME_AC_BE;
flags |= HAL_TXDESC_INTREQ; /* force interrupt */
break;
case IEEE80211_FC0_TYPE_CTL:
atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */
- rix = sc->sc_minrateix;
+ rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
- /* NB: force all ctl frames to highest queue */
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all ctl frames to highest queue */
- pri = WME_AC_VO;
- } else
- pri = WME_AC_BE;
flags |= HAL_TXDESC_INTREQ; /* force interrupt */
break;
case IEEE80211_FC0_TYPE_DATA:
@@ -4207,16 +4672,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* rate to use.
*/
if (ismcast) {
- /*
- * Check mcast rate setting in case it's changed.
- * XXX move out of fastpath
- */
- if (ic->ic_mcast_rate != sc->sc_mcastrate) {
- sc->sc_mcastrix =
- ath_tx_findrix(rt, ic->ic_mcast_rate);
- sc->sc_mcastrate = ic->ic_mcast_rate;
- }
- rix = sc->sc_mcastrix;
+ rix = an->an_mcastrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
@@ -4229,7 +4685,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
if (try0 != ATH_TXMAXTRY)
ismrr = 1;
}
- pri = M_WME_GETAC(m0);
if (cap->cap_wmeParams[pri].wmep_noackPolicy)
flags |= HAL_TXDESC_NOACK;
break;
@@ -4248,17 +4703,15 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* queue (to prevent out of order delivery) multicast
* frames must be buffered until after the beacon.
*/
- if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth)) {
- txq = &sc->sc_mcastq;
- /* XXX? more bit in 802.11 frame header */
- }
+ if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth))
+ txq = &avp->av_mcastq;
/*
* Calculate miscellaneous flags.
*/
if (ismcast) {
flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
- } else if (pktlen > ic->ic_rtsthreshold &&
+ } else if (pktlen > vap->iv_rtsthreshold &&
(ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
cix = rt->info[rix].controlRate;
@@ -4386,9 +4839,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
sc->sc_hwmap[txrate].ieeerate, -1);
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
u_int64_t tsf = ath_hal_gettsf64(ah);
sc->sc_tx_th.wt_tsf = htole64(tsf);
@@ -4401,8 +4852,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
sc->sc_tx_th.wt_txpower = ni->ni_txpower;
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
/*
@@ -4465,7 +4915,8 @@ static int
ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_buf *bf;
struct ath_desc *ds, *ds0;
struct ath_tx_status *ts;
@@ -4706,10 +5157,12 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
ATH_TXQ_UNLOCK(txq);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RESET) {
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+
ath_printtxbuf(bf, txq->axq_qnum, ix,
ath_hal_txprocdesc(ah, bf->bf_desc,
&bf->bf_status.ds_txstat) == HAL_OK);
- ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+ ieee80211_dump_pkt(ic, mtod(bf->bf_m, caddr_t),
bf->bf_m->m_len, 0, -1);
}
#endif /* ATH_DEBUG */
@@ -4770,7 +5223,6 @@ ath_draintxq(struct ath_softc *sc)
for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_draintxq(sc, &sc->sc_txq[i]);
- ath_tx_draintxq(sc, &sc->sc_mcastq);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RESET) {
struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
@@ -4778,7 +5230,7 @@ ath_draintxq(struct ath_softc *sc)
ath_printtxbuf(bf, sc->sc_bhalq, 0,
ath_hal_txprocdesc(ah, bf->bf_desc,
&bf->bf_status.ds_txstat) == HAL_OK);
- ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+ ieee80211_dump_pkt(ifp->if_l2com, mtod(bf->bf_m, caddr_t),
bf->bf_m->m_len, 0, -1);
}
}
@@ -4890,42 +5342,6 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
}
/*
- * Poll for a channel clear indication; this is required
- * for channels requiring DFS and not previously visited
- * and/or with a recent radar detection.
- */
-static void
-ath_dfswait(void *arg)
-{
- struct ath_softc *sc = arg;
- struct ath_hal *ah = sc->sc_ah;
- HAL_CHANNEL hchan;
-
- ath_hal_radar_wait(ah, &hchan);
- DPRINTF(sc, ATH_DEBUG_DFS, "%s: radar_wait %u/%x/%x\n",
- __func__, hchan.channel, hchan.channelFlags, hchan.privFlags);
-
- if (hchan.privFlags & CHANNEL_INTERFERENCE) {
- if_printf(sc->sc_ifp,
- "channel %u/0x%x/0x%x has interference\n",
- hchan.channel, hchan.channelFlags, hchan.privFlags);
- return;
- }
- if ((hchan.privFlags & CHANNEL_DFS) == 0) {
- /* XXX should not happen */
- return;
- }
- if (hchan.privFlags & CHANNEL_DFS_CLEAR) {
- sc->sc_curchan.privFlags |= CHANNEL_DFS_CLEAR;
- sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- if_printf(sc->sc_ifp,
- "channel %u/0x%x/0x%x marked clear\n",
- hchan.channel, hchan.channelFlags, hchan.privFlags);
- } else
- callout_reset(&sc->sc_dfs_ch, 2 * hz, ath_dfswait, sc);
-}
-
-/*
* Set/change channels. If the channel is really being changed,
* it's done by reseting the chip. To accomplish this we must
* first cleanup any pending DMA, then restart stuff after a la
@@ -4934,8 +5350,9 @@ ath_dfswait(void *arg)
static int
ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
HAL_CHANNEL hchan;
/*
@@ -4967,7 +5384,7 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
ath_draintxq(sc); /* clear pending tx frames */
ath_stoprecv(sc); /* turn off frame recv */
if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) {
- if_printf(ic->ic_ifp, "%s: unable to reset "
+ if_printf(ifp, "%s: unable to reset "
"channel %u (%u Mhz, flags 0x%x hal flags 0x%x), "
"hal status %u\n", __func__,
ieee80211_chan2ieee(ic, chan), chan->ic_freq,
@@ -4975,7 +5392,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
return EIO;
}
sc->sc_curchan = hchan;
- ath_update_txpow(sc); /* update tx power state */
sc->sc_diversity = ath_hal_getdiversity(ah);
sc->sc_calinterval = 1;
sc->sc_caltries = 0;
@@ -4984,8 +5400,8 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
* Re-enable rx framework.
*/
if (ath_startrecv(sc) != 0) {
- if_printf(ic->ic_ifp,
- "%s: unable to restart recv logic\n", __func__);
+ if_printf(ifp, "%s: unable to restart recv logic\n",
+ __func__);
return EIO;
}
@@ -4996,25 +5412,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
ath_chan_change(sc, chan);
/*
- * Handle DFS required waiting period to determine
- * if channel is clear of radar traffic.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-#define DFS_AND_NOT_CLEAR(_c) \
- (((_c)->privFlags & (CHANNEL_DFS | CHANNEL_DFS_CLEAR)) == CHANNEL_DFS)
- if (DFS_AND_NOT_CLEAR(&sc->sc_curchan)) {
- if_printf(sc->sc_ifp,
- "wait for DFS clear channel signal\n");
- /* XXX stop sndq */
- sc->sc_ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- callout_reset(&sc->sc_dfs_ch,
- 2 * hz, ath_dfswait, sc);
- } else
- callout_stop(&sc->sc_dfs_ch);
-#undef DFS_NOT_CLEAR
- }
-
- /*
* Re-enable interrupts.
*/
ath_hal_intrset(ah, sc->sc_imask);
@@ -5138,13 +5535,32 @@ ath_set_channel(struct ieee80211com *ic)
sc->sc_syncbeacon = 1;
}
+/*
+ * Walk the vap list and check if there any vap's in RUN state.
+ */
static int
-ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ath_isanyrunningvaps(struct ieee80211vap *this)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = this->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap != this && vap->iv_state == IEEE80211_S_RUN)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211_node *ni;
+ struct ieee80211_node *ni = NULL;
int i, error, stamode;
u_int32_t rfilt;
static const HAL_LED_STATE leds[] = {
@@ -5159,75 +5575,70 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
};
DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
callout_stop(&sc->sc_cal_ch);
- callout_stop(&sc->sc_dfs_ch);
ath_hal_setledstate(ah, leds[nstate]); /* set LED */
- if (nstate == IEEE80211_S_INIT) {
+ if (nstate == IEEE80211_S_SCAN) {
/*
- * Shutdown host/driver operation:
- * o disable interrupts so we don't rx frames
- * o clean any pending items on the task q
- * o notify the rate control algorithm
+ * Scanning: turn off beacon miss and don't beacon.
+ * Mark beacon state so when we reach RUN state we'll
+ * [re]setup beacons. Unblock the task q thread so
+ * deferred interrupt processing is done.
*/
+ ath_hal_intrset(ah,
+ sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
- ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
-#if 0
- /* XXX can't use taskqueue_drain 'cuz we're holding sc_mtx */
- taskqueue_drain(sc->sc_tq, &sc->sc_rxtask);
- taskqueue_drain(sc->sc_tq, &sc->sc_rxorntask);
- taskqueue_drain(sc->sc_tq, &sc->sc_bmisstask);
- taskqueue_drain(sc->sc_tq, &sc->sc_bstucktask);
-#endif
- ath_rate_newstate(sc, nstate);
- goto done;
+ sc->sc_beacons = 0;
+ taskqueue_unblock(sc->sc_tq);
}
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
rfilt = ath_calcrxfilter(sc);
- stamode = (sc->sc_opmode == HAL_M_STA || sc->sc_opmode == HAL_M_IBSS);
+ stamode = (vap->iv_opmode == IEEE80211_M_STA ||
+ vap->iv_opmode == IEEE80211_M_IBSS);
if (stamode && nstate == IEEE80211_S_RUN) {
sc->sc_curaid = ni->ni_associd;
IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid);
- } else
- sc->sc_curaid = 0;
-
+ ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid);
+ }
DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n",
- __func__, rfilt, ether_sprintf(sc->sc_curbssid),
- sc->sc_curaid);
-
+ __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid);
ath_hal_setrxfilter(ah, rfilt);
- if (stamode)
- ath_hal_setassocid(ah, sc->sc_curbssid, ni->ni_associd);
- if (ic->ic_opmode != IEEE80211_M_STA &&
- (ic->ic_flags & IEEE80211_F_PRIVACY)) {
+ /* XXX is this to restore keycache on resume? */
+ if (vap->iv_opmode != IEEE80211_M_STA &&
+ (vap->iv_flags & IEEE80211_F_PRIVACY)) {
for (i = 0; i < IEEE80211_WEP_NKID; i++)
if (ath_hal_keyisvalid(ah, i))
ath_hal_keysetmac(ah, i, ni->ni_bssid);
}
-
/*
* Notify the rate control algorithm so rates
* are setup should ath_beacon_alloc be called.
*/
- ath_rate_newstate(sc, nstate);
+ ath_rate_newstate(vap, nstate);
+
+ /*
+ * Invoke the parent method to do net80211 work.
+ */
+ error = avp->av_newstate(vap, nstate, arg);
+ if (error != 0)
+ goto bad;
if (nstate == IEEE80211_S_RUN) {
+ /* NB: collect bss node again, it may have changed */
+ ni = vap->iv_bss;
+
DPRINTF(sc, ATH_DEBUG_STATE,
- "%s(RUN): ic_flags=0x%08x iv=%d bssid=%s "
- "capinfo=0x%04x chan=%d\n"
- , __func__
- , ic->ic_flags
- , ni->ni_intval
- , ether_sprintf(ni->ni_bssid)
- , ni->ni_capinfo
- , ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- switch (ic->ic_opmode) {
+ "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
+ "capinfo 0x%04x chan %d\n", __func__,
+ vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid),
+ ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+ switch (vap->iv_opmode) {
case IEEE80211_M_HOSTAP:
case IEEE80211_M_IBSS:
/*
@@ -5240,7 +5651,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* be called with beacon transmission active.
*/
ath_hal_stoptxdma(ah, sc->sc_bhalq);
- ath_beacon_free(sc);
+
error = ath_beacon_alloc(sc, ni);
if (error != 0)
goto bad;
@@ -5248,22 +5659,23 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* If joining an adhoc network defer beacon timer
* configuration to the next beacon frame so we
* have a current TSF to use. Otherwise we're
- * starting an ibss/bss so there's no need to delay.
+ * starting an ibss/bss so there's no need to delay;
+ * if this is the first vap moving to RUN state, then
+ * beacon state needs to be [re]configured.
*/
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- ic->ic_bss->ni_tstamp.tsf != 0)
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ ni->ni_tstamp.tsf != 0) {
sc->sc_syncbeacon = 1;
- else
- ath_beacon_config(sc);
+ } else if (!sc->sc_beacons) {
+ ath_beacon_config(sc, vap);
+ sc->sc_beacons = 1;
+ }
break;
case IEEE80211_M_STA:
/*
- * Allocate a key cache slot to the station.
+ * Fakeup since we're not called by net80211.
*/
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0 &&
- sc->sc_hasclrkey &&
- ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
- ath_setup_stationkey(ni);
+ ath_newassoc(ni, 1);
/*
* Defer beacon timer configuration to the next
* beacon frame so we have a current TSF to use
@@ -5271,6 +5683,16 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
*/
sc->sc_syncbeacon = 1;
break;
+ case IEEE80211_M_MONITOR:
+ /*
+ * Monitor mode vaps have only INIT->RUN and RUN->RUN
+ * transitions so we must re-enable interrupts here to
+ * handle the case of a single monitor mode vap.
+ */
+ ath_hal_intrset(ah, sc->sc_imask);
+ break;
+ case IEEE80211_M_WDS:
+ break;
default:
break;
}
@@ -5285,23 +5707,31 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
- } else {
- ath_hal_intrset(ah,
- sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
- sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
- }
-done:
- /*
- * Invoke the parent method to complete the work.
- */
- error = sc->sc_newstate(ic, nstate, arg);
- /*
- * Finally, start any timers.
- */
- if (nstate == IEEE80211_S_RUN) {
- /* start periodic recalibration timer */
- callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
- ath_calibrate, sc);
+ /*
+ * Finally, start any timers and the task q thread
+ * (in case we didn't go through SCAN state).
+ */
+ if (sc->sc_calinterval != 0) {
+ /* start periodic recalibration timer */
+ callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
+ ath_calibrate, sc);
+ }
+ taskqueue_unblock(sc->sc_tq);
+ } else if (nstate == IEEE80211_S_INIT) {
+ /*
+ * If there are no vaps left in RUN state then
+ * shutdown host/driver operation:
+ * o disable interrupts
+ * o disable the task queue thread
+ * o mark beacon processing as stopped
+ */
+ if (!ath_isanyrunningvaps(vap)) {
+ sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+ /* disable interrupts */
+ ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
+ taskqueue_block(sc->sc_tq);
+ sc->sc_beacons = 0;
+ }
}
bad:
return error;
@@ -5318,11 +5748,11 @@ bad:
static void
ath_setup_stationkey(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
ieee80211_keyix keyix, rxkeyix;
- if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
+ if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
/*
* Key cache is full; we'll fall back to doing
* the more expensive lookup in software. Note
@@ -5334,7 +5764,7 @@ ath_setup_stationkey(struct ieee80211_node *ni)
ni->ni_ucastkey.wk_keyix = keyix;
ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
/* NB: this will create a pass-thru key entry */
- ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
+ ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, vap->iv_bss);
}
}
@@ -5346,58 +5776,78 @@ ath_setup_stationkey(struct ieee80211_node *ni)
static void
ath_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_node *an = ATH_NODE(ni);
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_phymode mode;
- ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
- if (isnew &&
- (ic->ic_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey) {
- KASSERT(ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE,
- ("new assoc with a unicast key already setup (keyix %u)",
- ni->ni_ucastkey.wk_keyix));
+ /*
+ * Deduce netband of station to simplify setting up xmit
+ * parameters. Note this allows us to assign different
+ * parameters to each station in a mixed bss (b/g, n/[abg]).
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ mode = IEEE80211_MODE_11NA;
+ else
+ mode = IEEE80211_MODE_11NG;
+ } else if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+ mode = IEEE80211_MODE_11A;
+ else if (ni->ni_flags & IEEE80211_NODE_ERP)
+ mode = IEEE80211_MODE_11G;
+ else
+ mode = IEEE80211_MODE_11B;
+ tp = &vap->iv_txparms[mode];
+ an->an_tp = tp;
+ an->an_mcastrix = ath_tx_findrix(sc->sc_rates[mode], tp->mcastrate);
+ an->an_mgmtrix = ath_tx_findrix(sc->sc_rates[mode], tp->mgmtrate);
+
+ ath_rate_newassoc(sc, an, isnew);
+ if (isnew &&
+ (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey &&
+ ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
ath_setup_stationkey(ni);
- }
}
static int
-ath_getchannels(struct ath_softc *sc,
- HAL_REG_DOMAIN rd, HAL_CTRY_CODE cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
+getchannels(struct ath_softc *sc, int *nchans, struct ieee80211_channel chans[],
+ int cc, int ecm, int outdoor)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = sc->sc_ifp;
struct ath_hal *ah = sc->sc_ah;
- HAL_CHANNEL *chans;
- int i, nchan;
- u_int32_t regdomain;
-
- chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
- M_TEMP, M_NOWAIT);
- if (chans == NULL) {
- if_printf(ifp, "unable to allocate channel table\n");
+ HAL_CHANNEL *halchans;
+ int i, nhalchans, error;
+
+ halchans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
+ M_TEMP, M_NOWAIT | M_ZERO);
+ if (halchans == NULL) {
+ device_printf(sc->sc_dev,
+ "%s: unable to allocate channel table\n", __func__);
return ENOMEM;
}
- if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
- NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
- (void) ath_hal_getregdomain(ah, &regdomain);
- if_printf(ifp, "unable to collect channel list from hal; "
- "regdomain likely %u country code %u\n", regdomain, cc);
- free(chans, M_TEMP);
- return EINVAL;
+ error = 0;
+ if (!ath_hal_init_channels(ah, halchans, IEEE80211_CHAN_MAX, &nhalchans,
+ NULL, 0, NULL, CTRY_DEFAULT, HAL_MODE_ALL, AH_FALSE, AH_TRUE)) {
+ error = EINVAL;
+ goto done;
}
+ if (nchans == NULL) /* no table requested */
+ goto done;
/*
* Convert HAL channels to ieee80211 ones.
*/
- memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
- for (i = 0; i < nchan; i++) {
- HAL_CHANNEL *c = &chans[i];
- struct ieee80211_channel *ichan = &ic->ic_channels[i];
+ for (i = 0; i < nhalchans; i++) {
+ HAL_CHANNEL *c = &halchans[i];
+ struct ieee80211_channel *ichan = &chans[i];
ichan->ic_ieee = ath_hal_mhz2ieee(ah, c->channel,
c->channelFlags);
if (bootverbose)
- if_printf(ifp, "hal channel %u/%x -> %u\n",
- c->channel, c->channelFlags, ichan->ic_ieee);
+ device_printf(sc->sc_dev, "hal channel %u/%x -> %u "
+ "maxpow %d minpow %d maxreg %d\n",
+ c->channel, c->channelFlags, ichan->ic_ieee,
+ c->maxTxPower, c->minTxPower, c->maxRegTxPower);
ichan->ic_freq = c->channel;
if ((c->channelFlags & CHANNEL_PUREG) == CHANNEL_PUREG) {
@@ -5419,15 +5869,98 @@ ath_getchannels(struct ath_softc *sc,
ichan->ic_flags);
}
ichan->ic_maxregpower = c->maxRegTxPower; /* dBm */
- ichan->ic_maxpower = c->maxTxPower; /* 1/2 dBm */
+ /* XXX: old hal's don't provide maxTxPower for some parts */
+ ichan->ic_maxpower = (c->maxTxPower != 0) ?
+ c->maxTxPower : 2*c->maxRegTxPower; /* 1/2 dBm */
ichan->ic_minpower = c->minTxPower; /* 1/2 dBm */
}
- ic->ic_nchans = nchan;
- free(chans, M_TEMP);
- (void) ath_hal_getregdomain(ah, &sc->sc_regdomain);
- ath_hal_getcountrycode(ah, &sc->sc_countrycode);
- sc->sc_xchanmode = xchanmode;
- sc->sc_outdoor = outdoor;
+ *nchans = nhalchans;
+done:
+ free(halchans, M_TEMP);
+ return error;
+}
+
+static int
+ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
+ int nchans, struct ieee80211_channel chans[])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t ord;
+ int error;
+
+ (void) ath_hal_getregdomain(ah, &ord);
+ /* XXX map sku->rd */
+ ath_hal_setregdomain(ah, rd->regdomain);
+ error = getchannels(sc, &nchans, chans, rd->country,
+ rd->ecm ? AH_TRUE : AH_FALSE,
+ rd->location == 'O' ? AH_TRUE : AH_FALSE);
+ if (error != 0) {
+ /*
+ * Restore previous state.
+ */
+ ath_hal_setregdomain(ah, ord);
+ (void) getchannels(sc, NULL, NULL, ic->ic_regdomain.country,
+ ic->ic_regdomain.ecm ? AH_TRUE : AH_FALSE,
+ ic->ic_regdomain.location == 'O' ? AH_TRUE : AH_FALSE);
+ return error;
+ }
+ return 0;
+}
+
+static void
+ath_getradiocaps(struct ieee80211com *ic,
+ int *nchans, struct ieee80211_channel chans[])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t ord;
+
+ (void) ath_hal_getregdomain(ah, &ord);
+ ath_hal_setregdomain(ah, 0);
+ /* XXX not quite right but close enough for now */
+ getchannels(sc, nchans, chans, CTRY_DEBUG, AH_TRUE, AH_FALSE);
+ ath_hal_setregdomain(ah, ord);
+}
+
+static int
+ath_mapregdomain(struct ath_softc *sc, u_int32_t rd)
+{
+ /* map Atheros rd's to SKU's */
+ return rd;
+}
+
+static int
+ath_getchannels(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rd, cc;
+ int error;
+
+ /*
+ * Convert HAL channels to ieee80211 ones.
+ */
+ error = getchannels(sc, &ic->ic_nchans, ic->ic_channels,
+ CTRY_DEFAULT, AH_TRUE, AH_FALSE);
+ (void) ath_hal_getregdomain(ah, &rd);
+ ath_hal_getcountrycode(ah, &cc); /* NB: cannot fail */
+ if (error) {
+ if_printf(ifp, "%s: unable to collect channel list from hal, "
+ "error %d\n", __func__, error);
+ if (error == EINVAL) {
+ if_printf(ifp, "%s: regdomain likely %u country code %u\n",
+ __func__, rd, cc);
+ }
+ return error;
+ }
+ ic->ic_regdomain.regdomain = ath_mapregdomain(sc, rd);
+ ic->ic_regdomain.country = cc;
+ ic->ic_regdomain.ecm = 1;
+ ic->ic_regdomain.location = 'I';
+ ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */
+ ic->ic_regdomain.isocc[1] = ' ';
return 0;
}
@@ -5488,26 +6021,6 @@ ath_led_event(struct ath_softc *sc, int event)
}
}
-static void
-ath_update_txpow(struct ath_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- u_int32_t txpow;
-
- if (sc->sc_curtxpow != ic->ic_txpowlimit) {
- ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
- /* read back in case value is clamped */
- if (ath_hal_gettxpowlimit(ah, &txpow))
- ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
- }
- /*
- * Fetch max tx power level for status requests.
- */
- if (ath_hal_getmaxtxpow(sc->sc_ah, &txpow))
- ic->ic_bss->ni_txpower = txpow;
-}
-
static int
ath_rate_setup(struct ath_softc *sc, u_int mode)
{
@@ -5630,14 +6143,6 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
sc->sc_protrix = ath_tx_findrix(rt, 2*2);
else
sc->sc_protrix = ath_tx_findrix(rt, 2*1);
- /* rate index used to send management frames */
- sc->sc_minrateix = 0;
- /*
- * Setup multicast rate state.
- */
- /* XXX layering violation */
- sc->sc_mcastrix = ath_tx_findrix(rt, sc->sc_ic.ic_mcast_rate);
- sc->sc_mcastrate = sc->sc_ic.ic_mcast_rate;
/* NB: caller is responsible for reseting rate control state */
#undef N
}
@@ -5763,7 +6268,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
#define IS_RUNNING(ifp) \
((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
@@ -5787,7 +6292,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* torn down much of our state. There's
* probably a better way to deal with this.
*/
- if (!sc->sc_invalid && ic->ic_bss != NULL)
+ if (!sc->sc_invalid)
ath_init(sc); /* XXX lose error */
} else
ath_stop_locked(ifp);
@@ -5802,12 +6307,18 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ath_mode_init(sc);
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
case SIOCGATHSTATS:
/* NB: embed these numbers to get a consistent view */
sc->sc_stats.ast_tx_packets = ifp->if_opackets;
sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
+#if 0
ieee80211_getsignal(ic, &sc->sc_stats.ast_rx_rssi,
&sc->sc_stats.ast_rx_noise);
+#endif
sc->sc_stats.ast_tx_rate = sc->sc_hwmap[sc->sc_txrate].ieeerate;
ATH_UNLOCK(sc);
/*
@@ -5826,15 +6337,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
#endif
default:
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (IS_RUNNING(ifp) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ath_init(sc); /* XXX lose error */
- error = 0;
- }
- if (error == ERESTART)
- error = IS_RUNNING(ifp) ? ath_reset(ifp) : 0;
+ error = ether_ioctl(ifp, cmd, data);
break;
}
ATH_UNLOCK(sc);
@@ -6059,48 +6562,6 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
}
static int
-ath_sysctl_countrycode(SYSCTL_HANDLER_ARGS)
-{
- struct ath_softc *sc = arg1;
- u_int32_t cc = sc->sc_countrycode;
- struct ieee80211com *ic = &sc->sc_ic;
- int error;
-
- error = sysctl_handle_int(oidp, &cc, 0, req);
- if (error || !req->newptr)
- return error;
- error = ath_getchannels(sc, sc->sc_regdomain, cc,
- sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
- if (error != 0)
- return error;
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
- /* setcurmode? */
- return 0;
-}
-
-static int
-ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
-{
- struct ath_softc *sc = arg1;
- u_int32_t rd = sc->sc_regdomain;
- struct ieee80211com *ic = &sc->sc_ic;
- int error;
-
- error = sysctl_handle_int(oidp, &rd, 0, req);
- if (error || !req->newptr)
- return error;
- if (!ath_hal_setregdomain(sc->sc_ah, rd))
- return EINVAL;
- error = ath_getchannels(sc, rd, sc->sc_countrycode,
- sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
- if (error != 0)
- return error;
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
- /* setcurmode? */
- return 0;
-}
-
-static int
ath_sysctl_tpack(SYSCTL_HANDLER_ARGS)
{
struct ath_softc *sc = arg1;
@@ -6135,12 +6596,6 @@ ath_sysctlattach(struct ath_softc *sc)
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
struct ath_hal *ah = sc->sc_ah;
- SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
- "countrycode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
- ath_sysctl_countrycode, "I", "country code");
- SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
- "regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
- ath_sysctl_regdomain, "I", "EEPROM regdomain code");
#ifdef ATH_DEBUG
sc->sc_debug = ath_debug;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
@@ -6228,9 +6683,8 @@ ath_bpfattach(struct ath_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th));
/*
* Initialize constant fields.
* XXX make header lengths a multiple of 32-bits so subsequent
@@ -6254,12 +6708,12 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
int error, ismcast, ismrr;
int hdrlen, pktlen, try0, txantenna;
u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
- struct ath_txq *txq;
struct ieee80211_frame *wh;
u_int flags, ctsduration;
HAL_PKT_TYPE atype;
@@ -6340,9 +6794,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
sc->sc_hwmap[txrate].ieeerate, -1);
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
u_int64_t tsf = ath_hal_gettsf64(ah);
sc->sc_tx_th.wt_tsf = htole64(tsf);
@@ -6353,8 +6805,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
sc->sc_tx_th.wt_txpower = ni->ni_txpower;
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
/*
@@ -6402,16 +6853,8 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
);
}
- /*
- * When servicing one or more stations in power-save mode
- * (or) if there is some mcast data waiting on the mcast
- * queue (to prevent out of order delivery) multicast
- * frames must be buffered until after the beacon.
- */
- txq = sc->sc_ac2q[pri];
- if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth))
- txq = &sc->sc_mcastq;
- ath_tx_handoff(sc, txq, bf);
+ /* NB: no buffered multicast in power save support */
+ ath_tx_handoff(sc, sc->sc_ac2q[pri], bf);
return 0;
}
diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c
index d775966..ed35447 100644
--- a/sys/dev/ath/if_ath_pci.c
+++ b/sys/dev/ath/if_ath_pci.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h
index ccd7220..1c737a4 100644
--- a/sys/dev/ath/if_athioctl.h
+++ b/sys/dev/ath/if_athioctl.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/if_athrate.h b/sys/dev/ath/if_athrate.h
index a6aaf6f..3ad287a 100644
--- a/sys/dev/ath/if_athrate.h
+++ b/sys/dev/ath/if_athrate.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* Copyright (c) 2004 Video54 Technologies, Inc.
* All rights reserved.
*
@@ -102,7 +102,7 @@ void ath_rate_newassoc(struct ath_softc *, struct ath_node *,
* Important mostly as the analog to ath_rate_newassoc when operating
* in station mode.
*/
-void ath_rate_newstate(struct ath_softc *, enum ieee80211_state);
+void ath_rate_newstate(struct ieee80211vap *, enum ieee80211_state);
/*
* Transmit handling.
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index af1045d..257c6ee 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
#ifndef ATH_TXBUF
#define ATH_TXBUF 200 /* number of TX buffers */
#endif
+#define ATH_BCBUF 4 /* number of beacon buffers */
+
#define ATH_TXDESC 10 /* number of descriptors per buffer */
#define ATH_TXMAXTRY 11 /* max number of transmit attempts */
#define ATH_TXMGTTRY 4 /* xmit attempts for mgt/ctl frames */
@@ -80,7 +82,9 @@ struct ath_buf;
/* driver-specific node state */
struct ath_node {
struct ieee80211_node an_node; /* base class */
- u_int32_t an_avgrssi; /* average rssi over all rx frames */
+ const struct ieee80211_txparam *an_tp;
+ u_int8_t an_mgmtrix; /* min h/w rate index */
+ u_int8_t an_mcastrix; /* mcast h/w rate index */
struct ath_buf *an_ff_buf[WME_NUM_AC]; /* ff staging area */
/* variable-length rate control state follows */
};
@@ -141,6 +145,7 @@ struct ath_descdma {
*/
struct ath_txq {
u_int axq_qnum; /* hardware q number */
+#define ATH_TXQ_SWQ (HAL_NUM_TX_QUEUES+1) /* qnum for s/w only queue */
u_int axq_depth; /* queue depth (stat only) */
u_int axq_intrcnt; /* interrupt count */
u_int32_t *axq_link; /* link ptr in last TX desc */
@@ -175,6 +180,25 @@ struct ath_txq {
STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
(_tq)->axq_depth--; \
} while (0)
+/* NB: this does not do the "head empty check" that STAILQ_LAST does */
+#define ATH_TXQ_LAST(_tq) \
+ ((struct ath_buf *)(void *) \
+ ((char *)((_tq)->axq_q.stqh_last) - __offsetof(struct ath_buf, bf_list)))
+
+struct ath_vap {
+ struct ieee80211vap av_vap; /* base class */
+ int av_bslot; /* beacon slot index */
+ struct ath_buf *av_bcbuf; /* beacon buffer */
+ struct ieee80211_beacon_offsets av_boff;/* dynamic update state */
+ struct ath_txq av_mcastq; /* buffered mcast s/w queue */
+
+ void (*av_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, u_int32_t);
+ int (*av_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ void (*av_bmiss)(struct ieee80211vap *);
+};
+#define ATH_VAP(vap) ((struct ath_vap *)(vap))
struct taskqueue;
struct ath_tx99;
@@ -182,16 +206,13 @@ struct ath_tx99;
struct ath_softc {
struct ifnet *sc_ifp; /* interface common */
struct ath_stats sc_stats; /* interface statistics */
- struct ieee80211com sc_ic; /* IEEE 802.11 common */
int sc_debug;
- u_int32_t sc_countrycode;
- u_int32_t sc_regdomain;
- void (*sc_recv_mgmt)(struct ieee80211com *,
- struct mbuf *,
- struct ieee80211_node *,
- int, int, int, u_int32_t);
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
+ int sc_nvaps; /* # vaps */
+ int sc_nstavaps; /* # station vaps */
+ u_int8_t sc_hwbssidmask[IEEE80211_ADDR_LEN];
+ u_int8_t sc_nbssid0; /* # vap's using base mac */
+ uint32_t sc_bssidmask; /* bssid mask */
+
void (*sc_node_free)(struct ieee80211_node *);
device_t sc_dev;
HAL_BUS_TAG sc_st; /* bus space tag */
@@ -203,22 +224,28 @@ struct ath_softc {
struct ath_ratectrl *sc_rc; /* tx rate control support */
struct ath_tx99 *sc_tx99; /* tx99 adjunct state */
void (*sc_setdefantenna)(struct ath_softc *, u_int);
- unsigned int sc_invalid : 1, /* disable hardware accesses */
- sc_mrretry : 1, /* multi-rate retry support */
- sc_softled : 1, /* enable LED gpio status */
- sc_splitmic: 1, /* split TKIP MIC keys */
- sc_needmib : 1, /* enable MIB stats intr */
- sc_diversity : 1,/* enable rx diversity */
- sc_hasveol : 1, /* tx VEOL support */
- sc_ledstate: 1, /* LED on/off state */
- sc_blinking: 1, /* LED blink operation active */
- sc_mcastkey: 1, /* mcast key cache search */
- sc_scanning: 1, /* scanning active */
+ unsigned int sc_invalid : 1,/* disable hardware accesses */
+ sc_mrretry : 1,/* multi-rate retry support */
+ sc_softled : 1,/* enable LED gpio status */
+ sc_splitmic : 1,/* split TKIP MIC keys */
+ sc_needmib : 1,/* enable MIB stats intr */
+ sc_diversity: 1,/* enable rx diversity */
+ sc_hasveol : 1,/* tx VEOL support */
+ sc_ledstate : 1,/* LED on/off state */
+ sc_blinking : 1,/* LED blink operation active */
+ sc_mcastkey : 1,/* mcast key cache search */
+ sc_scanning : 1,/* scanning active */
sc_syncbeacon:1,/* sync/resync beacon timers */
- sc_hasclrkey:1, /* CLR key supported */
+ sc_hasclrkey: 1,/* CLR key supported */
sc_xchanmode: 1,/* extended channel mode */
sc_outdoor : 1,/* outdoor operation */
- sc_dturbo : 1; /* dynamic turbo in use */
+ sc_dturbo : 1,/* dynamic turbo in use */
+ sc_hasbmask : 1,/* bssid mask support */
+ sc_hastsfadd: 1,/* tsf adjust support */
+ sc_beacons : 1,/* beacons running */
+ sc_swbmiss : 1,/* sta mode using sw bmiss */
+ sc_stagbeacons:1,/* use staggered beacons */
+ sc_wmetkipmic:1;/* can do WME+TKIP MIC */
/* rate tables */
#define IEEE80211_MODE_HALF (IEEE80211_MODE_MAX+0)
#define IEEE80211_MODE_QUARTER (IEEE80211_MODE_MAX+1)
@@ -238,8 +265,6 @@ struct ath_softc {
u_int16_t ledon; /* softled on time */
u_int16_t ledoff; /* softled off time */
} sc_hwmap[32]; /* h/w rate ix mappings */
- u_int8_t sc_minrateix; /* min h/w rate index */
- u_int8_t sc_mcastrix; /* mcast h/w rate index */
u_int8_t sc_protrix; /* protection rate index */
u_int8_t sc_lastdatarix; /* last data frame rate index */
u_int sc_mcastrate; /* ieee rate for mcastrateix */
@@ -262,20 +287,13 @@ struct ath_softc {
u_int sc_rfsilentpin; /* GPIO pin for rfkill int */
u_int sc_rfsilentpol; /* pin setting for rfkill on */
- struct bpf_if *sc_drvbpf;
- union {
- struct ath_tx_radiotap_header th;
- u_int8_t pad[64];
- } u_tx_rt;
+ struct ath_tx_radiotap_header sc_tx_th;
int sc_tx_th_len;
- union {
- struct ath_rx_radiotap_header th;
- u_int8_t pad[64];
- } u_rx_rt;
+ struct ath_rx_radiotap_header sc_rx_th;
int sc_rx_th_len;
u_int sc_monpass; /* frames to pass in mon.mode */
- struct ath_descdma sc_rxdma; /* RX descriptos */
+ struct ath_descdma sc_rxdma; /* RX descriptors */
ath_bufhead sc_rxbuf; /* receive buffer */
struct mbuf *sc_rxpending; /* pending receive data */
u_int32_t *sc_rxlink; /* link ptr in last RX desc */
@@ -301,7 +319,6 @@ struct ath_softc {
u_int sc_bmisscount; /* missed beacon transmits */
u_int32_t sc_ant_tx[8]; /* recent tx frames/antenna */
struct ath_txq *sc_cabq; /* tx q for cab frames */
- struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */
struct task sc_bmisstask; /* bmiss int processing */
struct task sc_bstucktask; /* stuck beacon processing */
enum {
@@ -309,16 +326,15 @@ struct ath_softc {
UPDATE, /* update pending */
COMMIT /* beacon sent, commit change */
} sc_updateslot; /* slot time update fsm */
- struct ath_txq sc_mcastq; /* mcast xmits w/ ps sta's */
+ int sc_slotupdate; /* slot to advance fsm */
+ struct ieee80211vap *sc_bslot[ATH_BCBUF];
+ int sc_nbcnvaps; /* # vaps with beacons */
struct callout sc_cal_ch; /* callout handle for cals */
int sc_calinterval; /* current polling interval */
int sc_caltries; /* cals at current interval */
HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
- struct callout sc_dfs_ch; /* callout handle for dfs */
};
-#define sc_tx_th u_tx_rt.th
-#define sc_rx_th u_rx_rt.th
#define ATH_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
@@ -361,6 +377,10 @@ void ath_intr(void *);
((*(_ah)->ah_getMacAddress)((_ah), (_mac)))
#define ath_hal_setmac(_ah, _mac) \
((*(_ah)->ah_setMacAddress)((_ah), (_mac)))
+#define ath_hal_getbssidmask(_ah, _mask) \
+ ((*(_ah)->ah_getBssIdMask)((_ah), (_mask)))
+#define ath_hal_setbssidmask(_ah, _mask) \
+ ((*(_ah)->ah_setBssIdMask)((_ah), (_mask)))
#define ath_hal_intrset(_ah, _mask) \
((*(_ah)->ah_setInterrupts)((_ah), (_mask)))
#define ath_hal_intrget(_ah) \
@@ -483,15 +503,21 @@ void ath_intr(void *);
#define ath_hal_getregdomain(_ah, _prd) \
(ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd)) == HAL_OK)
#define ath_hal_setregdomain(_ah, _rd) \
- ((*(_ah)->ah_setRegulatoryDomain)((_ah), (_rd), NULL))
+ (*(uint16_t *)(((uint8_t *)(_ah)) + 520) = (_rd))
#define ath_hal_getcountrycode(_ah, _pcc) \
(*(_pcc) = (_ah)->ah_countryCode)
+#define ath_hal_gettkipmic(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TKIP_MIC, 1, NULL) == HAL_OK)
+#define ath_hal_settkipmic(_ah, _v) \
+ ath_hal_setcapability(_ah, HAL_CAP_TKIP_MIC, 1, _v, NULL)
#define ath_hal_hastkipsplit(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
#define ath_hal_gettkipsplit(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, NULL) == HAL_OK)
#define ath_hal_settkipsplit(_ah, _v) \
ath_hal_setcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, _v, NULL)
+#define ath_hal_haswmetkipmic(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_WME_TKIPMIC, 0, NULL) == HAL_OK)
#define ath_hal_hwphycounters(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
#define ath_hal_hasdiversity(_ah) \
@@ -542,6 +568,14 @@ void ath_intr(void *);
#endif
#define ath_hal_hasfastframes(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK)
+#define ath_hal_hasbssidmask(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_BSSIDMASK, 0, NULL) == HAL_OK)
+#define ath_hal_hastsfadjust(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 0, NULL) == HAL_OK)
+#define ath_hal_gettsfadjust(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 1, NULL) == HAL_OK)
+#define ath_hal_settsfadjust(_ah, _onoff) \
+ ath_hal_setcapability(_ah, HAL_CAP_TSF_ADJUST, 1, _onoff, NULL)
#define ath_hal_hasrfsilent(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK)
#define ath_hal_getrfkill(_ah) \
diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c
index c44ed53..896ddb3 100644
--- a/sys/dev/if_ndis/if_ndis.c
+++ b/sys/dev/if_ndis/if_ndis.c
@@ -133,16 +133,21 @@ static funcptr ndis_starttask_wrap;
static funcptr ndis_resettask_wrap;
static funcptr ndis_inputtask_wrap;
+static struct ieee80211vap *ndis_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ndis_vap_delete (struct ieee80211vap *);
static void ndis_tick (void *);
static void ndis_ticktask (device_object *, void *);
+static int ndis_raw_xmit (struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void ndis_start (struct ifnet *);
static void ndis_starttask (device_object *, void *);
static void ndis_resettask (device_object *, void *);
static void ndis_inputtask (device_object *, void *);
static int ndis_ioctl (struct ifnet *, u_long, caddr_t);
-static int ndis_80211_ioctl_get (struct ifnet *, u_long, caddr_t);
-static int ndis_80211_ioctl_set (struct ifnet *, u_long, caddr_t);
-static int ndis_newstate (struct ieee80211com *, enum ieee80211_state,
+static int ndis_newstate (struct ieee80211vap *, enum ieee80211_state,
int);
static int ndis_nettype_chan (uint32_t);
static int ndis_nettype_mode (uint32_t);
@@ -151,8 +156,8 @@ static void ndis_scan_results (struct ndis_softc *);
static void ndis_scan_start (struct ieee80211com *);
static void ndis_scan_end (struct ieee80211com *);
static void ndis_set_channel (struct ieee80211com *);
-static void ndis_scan_curchan (struct ieee80211com *, unsigned long);
-static void ndis_scan_mindwell (struct ieee80211com *);
+static void ndis_scan_curchan (struct ieee80211_scan_state *, unsigned long);
+static void ndis_scan_mindwell (struct ieee80211_scan_state *);
static void ndis_init (void *);
static void ndis_stop (struct ndis_softc *);
static void ndis_watchdog (struct ifnet *);
@@ -164,12 +169,11 @@ static int ndis_set_offload (struct ndis_softc *);
static void ndis_getstate_80211 (struct ndis_softc *);
static void ndis_setstate_80211 (struct ndis_softc *);
static int ndis_set_cipher (struct ndis_softc *, int);
-static int ndis_set_wpa (struct ndis_softc *);
-static int ndis_add_key (struct ieee80211com *,
+static int ndis_set_wpa (struct ndis_softc *, void *, int);
+static int ndis_add_key (struct ieee80211vap *,
const struct ieee80211_key *, const u_int8_t []);
-static int ndis_del_key (struct ieee80211com *,
+static int ndis_del_key (struct ieee80211vap *,
const struct ieee80211_key *);
-static void ndis_media_status (struct ifnet *, struct ifmediareq *);
static void ndis_setmulti (struct ndis_softc *);
static void ndis_map_sclist (void *, bus_dma_segment_t *,
@@ -520,16 +524,11 @@ ndis_attach(dev)
driver_object *pdrv;
device_object *pdo;
struct ifnet *ifp = NULL;
- int error = 0, len, mode, bands = 0;
+ int error = 0, len, mode;
+ uint8_t bands = 0;
int i;
sc = device_get_softc(dev);
- ifp = sc->ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- error = ENOSPC;
- goto fail;
- }
- ifp->if_softc = sc;
KeInitializeSpinLock(&sc->ndis_spinlock);
KeInitializeSpinLock(&sc->ndis_rxlock);
@@ -598,10 +597,6 @@ ndis_attach(dev)
sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj);
KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block);
- /* make sure drv flags are all cleared before initing the NIC. */
-
- ifp->if_drv_flags = 0;
-
/* Call driver's init routine. */
if (ndis_init_nic(sc)) {
device_printf (dev, "init handler failed\n");
@@ -677,6 +672,17 @@ ndis_attach(dev)
}
}
+ if (sc->ndis_80211)
+ ifp = if_alloc(IFT_IEEE80211);
+ else
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ error = ENOSPC;
+ goto fail;
+ }
+ sc->ifp = ifp;
+ ifp->if_softc = sc;
+
/* Check for task offload support. */
ndis_probe_offload(sc);
@@ -696,7 +702,7 @@ ndis_attach(dev)
/* Do media setup */
if (sc->ndis_80211) {
- struct ieee80211com *ic = (void *)&sc->ic;
+ struct ieee80211com *ic = ifp->if_l2com;
ndis_80211_rates_ex rates;
struct ndis_80211_nettype_list *ntl;
uint32_t arg;
@@ -709,8 +715,8 @@ ndis_attach(dev)
TASK_INIT(&sc->ndis_scantask, 0, ndis_scan, sc);
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_phytype = IEEE80211_T_DS;
ic->ic_caps = IEEE80211_C_IBSS;
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
len = 0;
@@ -840,7 +846,7 @@ nonettypes:
}
#undef SETRATE
#undef INCRATE
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
/*
* To test for WPA support, we need to see if we can
@@ -869,20 +875,22 @@ nonettypes:
arg = NDIS_80211_WEPSTAT_ENC3ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0) {
- ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP|
- IEEE80211_C_AES_CCM;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP
+ | IEEE80211_CRYPTO_TKIP
+ | IEEE80211_CRYPTO_AES_CCM;
goto got_crypto;
}
arg = NDIS_80211_WEPSTAT_ENC2ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0) {
- ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP
+ | IEEE80211_CRYPTO_TKIP;
goto got_crypto;
}
arg = NDIS_80211_WEPSTAT_ENC1ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0)
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
got_crypto:
i = sizeof(arg);
r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i);
@@ -890,21 +898,17 @@ got_crypto:
ic->ic_caps |= IEEE80211_C_PMGT;
bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr));
ieee80211_ifattach(ic);
- ieee80211_media_init(ic, ieee80211_media_change,
- ndis_media_status);
+ ic->ic_raw_xmit = ndis_raw_xmit;
ic->ic_scan_start = ndis_scan_start;
ic->ic_scan_end = ndis_scan_end;
ic->ic_set_channel = ndis_set_channel;
ic->ic_scan_curchan = ndis_scan_curchan;
ic->ic_scan_mindwell = ndis_scan_mindwell;
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
- ic->ic_bss->ni_chan = ic->ic_bsschan;
- /* override state transition machine */
- sc->ndis_newstate = ic->ic_newstate;
- ic->ic_newstate = ndis_newstate;
- /* install key handing routines */
- ic->ic_crypto.cs_key_set = ndis_add_key;
- ic->ic_crypto.cs_key_delete = ndis_del_key;
+ //ic->ic_bss->ni_chan = ic->ic_bsschan;
+ ic->ic_vap_create = ndis_vap_create;
+ ic->ic_vap_delete = ndis_vap_delete;
+
} else {
ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd,
ndis_ifmedia_sts);
@@ -928,6 +932,45 @@ fail:
return(error);
}
+static struct ieee80211vap *
+ndis_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ndis_vap *nvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ nvp = (struct ndis_vap *) malloc(sizeof(struct ndis_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (nvp == NULL)
+ return NULL;
+ vap = &nvp->vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ nvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ndis_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ /* install key handing routines */
+ vap->iv_key_set = ndis_add_key;
+ vap->iv_key_delete = ndis_del_key;
+ return vap;
+}
+
+static void
+ndis_vap_delete(struct ieee80211vap *vap)
+{
+ struct ndis_vap *nvp = NDIS_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(nvp, M_80211_VAP);
+}
+
/*
* Shutdown hardware and free up resources. This can be called any
* time after the mutex has been initialized. It is called in both
@@ -952,7 +995,7 @@ ndis_detach(dev)
NDIS_UNLOCK(sc);
ndis_stop(sc);
if (sc->ndis_80211)
- ieee80211_ifdetach(&sc->ic);
+ ieee80211_ifdetach(ifp->if_l2com);
else
ether_ifdetach(ifp);
} else
@@ -1422,10 +1465,14 @@ ndis_inputtask(dobj, arg)
struct ifnet *ifp;
struct ndis_softc *sc;
struct mbuf *m;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
uint8_t irql;
ifp = arg;
sc = ifp->if_softc;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
block = dobj->do_devext;
KeAcquireSpinLock(&sc->ndis_rxlock, &irql);
@@ -1434,8 +1481,10 @@ ndis_inputtask(dobj, arg)
if (m == NULL)
break;
KeReleaseSpinLock(&sc->ndis_rxlock, irql);
- ifp->if_ipackets++;
- (*ifp->if_input)(ifp, m);
+ if (sc->ndis_80211)
+ vap->iv_deliver_data(vap, vap->iv_bss, m);
+ else
+ (*ifp->if_input)(ifp, m);
KeAcquireSpinLock(&sc->ndis_rxlock, &irql);
}
KeReleaseSpinLock(&sc->ndis_rxlock, irql);
@@ -1600,11 +1649,13 @@ ndis_ticktask(d, xsc)
{
struct ndis_softc *sc;
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
ndis_checkforhang_handler hangfunc;
uint8_t rval;
sc = xsc;
- ic = &sc->ic;
+ ic = sc->ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
NDIS_LOCK(sc);
if (!NDIS_INITIALIZED(sc)) {
@@ -1631,7 +1682,7 @@ ndis_ticktask(d, xsc)
NDIS_UNLOCK(sc);
if (sc->ndis_80211) {
ndis_getstate_80211(sc);
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
}
NDIS_LOCK(sc);
if_link_state_change(sc->ifp, LINK_STATE_UP);
@@ -1641,7 +1692,7 @@ ndis_ticktask(d, xsc)
sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
sc->ndis_link = 0;
if (sc->ndis_80211)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
if_link_state_change(sc->ifp, LINK_STATE_DOWN);
}
@@ -1677,6 +1728,16 @@ ndis_map_sclist(arg, segs, nseg, mapsize, error)
return;
}
+static int
+ndis_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
ndis_starttask(d, arg)
device_object *d;
@@ -1791,7 +1852,8 @@ ndis_start(ifp)
* to him.
*/
- BPF_MTAP(ifp, m);
+ if (!sc->ndis_80211) /* XXX handle 80211 */
+ BPF_MTAP(ifp, m);
/*
* The array that p0 points to must appear contiguous,
@@ -1840,8 +1902,8 @@ ndis_init(xsc)
void *xsc;
{
struct ndis_softc *sc = xsc;
- struct ieee80211com *ic = (void *)&sc->ic;
struct ifnet *ifp = sc->ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i, len, error;
/*
@@ -1850,7 +1912,7 @@ ndis_init(xsc)
* fixing the upper layer modules so they don't
* call ifp->if_init() quite as often.
*/
- if (sc->ndis_link && sc->ndis_skip)
+ if (sc->ndis_link)
return;
/*
@@ -1908,23 +1970,14 @@ ndis_init(xsc)
if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
NDIS_UNLOCK(sc);
+ /* XXX force handling */
+ ieee80211_start_all(ic); /* start all vap's */
+
/*
* Some drivers don't set this value. The NDIS spec says
* the default checkforhang timeout is "approximately 2
@@ -2017,24 +2070,24 @@ ndis_set_cipher(sc, cipher)
int rval = 0, len;
uint32_t arg, save;
- ic = &sc->ic;
+ ic = sc->ifp->if_l2com;
len = sizeof(arg);
if (cipher == WPA_CSE_WEP40 || WPA_CSE_WEP104) {
- if (!(ic->ic_caps & IEEE80211_C_WEP))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC1ENABLED;
}
if (cipher == WPA_CSE_TKIP) {
- if (!(ic->ic_caps & IEEE80211_C_TKIP))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC2ENABLED;
}
if (cipher == WPA_CSE_CCMP) {
- if (!(ic->ic_caps & IEEE80211_C_AES_CCM))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_AES_CCM))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC3ENABLED;
}
@@ -2067,18 +2120,17 @@ ndis_set_cipher(sc, cipher)
*/
static int
-ndis_set_wpa(sc)
+ndis_set_wpa(sc, ie, ielen)
struct ndis_softc *sc;
+ void *ie;
+ int ielen;
{
- struct ieee80211com *ic;
struct ieee80211_ie_wpa *w;
struct ndis_ie *n;
char *pos;
uint32_t arg;
int i;
- ic = &sc->ic;
-
/*
* Apparently, the only way for us to know what ciphers
* and key management/authentication mode to use is for
@@ -2087,7 +2139,7 @@ ndis_set_wpa(sc)
* supplied by the WPA supplicant.
*/
- w = (struct ieee80211_ie_wpa *)ic->ic_opt_ie;
+ w = (struct ieee80211_ie_wpa *)ie;
/* Check for the right kind of IE. */
if (w->wpa_id != IEEE80211_ELEMID_VENDOR) {
@@ -2150,18 +2202,20 @@ ndis_setstate_80211(sc)
struct ndis_softc *sc;
{
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
struct ieee80211_node *ni;
ndis_80211_ssid ssid;
ndis_80211_macaddr bssid;
ndis_80211_config config;
ndis_80211_wep wep;
- int i, rval = 0, len;
+ int i, rval = 0, len, error;
uint32_t arg;
struct ifnet *ifp;
- ic = &sc->ic;
ifp = sc->ifp;
- ni = ic->ic_bss;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ ni = vap->iv_bss;
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: NDIS not initialized\n", __func__));
@@ -2177,7 +2231,7 @@ ndis_setstate_80211(sc)
/* Set network infrastructure mode. */
len = sizeof(arg);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
arg = NDIS_80211_NET_INFRA_IBSS;
else
arg = NDIS_80211_NET_INFRA_BSS;
@@ -2190,13 +2244,13 @@ ndis_setstate_80211(sc)
/* Set RTS threshold */
len = sizeof(arg);
- arg = ic->ic_rtsthreshold;
+ arg = vap->iv_rtsthreshold;
ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len);
/* Set fragmentation threshold */
len = sizeof(arg);
- arg = ic->ic_fragthreshold;
+ arg = vap->iv_fragthreshold;
ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len);
/* Set power management */
@@ -2231,11 +2285,11 @@ ndis_setstate_80211(sc)
/* Set WEP */
- if (ic->ic_flags & IEEE80211_F_PRIVACY &&
- !(ic->ic_flags & IEEE80211_F_WPA)) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY &&
+ !(vap->iv_flags & IEEE80211_F_WPA)) {
int keys_set = 0;
- if (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) {
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
len = sizeof(arg);
arg = NDIS_80211_AUTHMODE_SHARED;
DPRINTF(("Setting shared auth\n"));
@@ -2243,12 +2297,12 @@ ndis_setstate_80211(sc)
&arg, &len);
}
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- if (ic->ic_nw_keys[i].wk_keylen) {
- if (ic->ic_nw_keys[i].wk_cipher->ic_cipher !=
+ if (vap->iv_nw_keys[i].wk_keylen) {
+ if (vap->iv_nw_keys[i].wk_cipher->ic_cipher !=
IEEE80211_CIPHER_WEP)
continue;
bzero((char *)&wep, sizeof(wep));
- wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen;
+ wep.nw_keylen = vap->iv_nw_keys[i].wk_keylen;
/*
* 5, 13 and 16 are the only valid
@@ -2256,21 +2310,21 @@ ndis_setstate_80211(sc)
* in between will be zero padded out to
* the next highest boundary.
*/
- if (ic->ic_nw_keys[i].wk_keylen < 5)
+ if (vap->iv_nw_keys[i].wk_keylen < 5)
wep.nw_keylen = 5;
- else if (ic->ic_nw_keys[i].wk_keylen > 5 &&
- ic->ic_nw_keys[i].wk_keylen < 13)
+ else if (vap->iv_nw_keys[i].wk_keylen > 5 &&
+ vap->iv_nw_keys[i].wk_keylen < 13)
wep.nw_keylen = 13;
- else if (ic->ic_nw_keys[i].wk_keylen > 13 &&
- ic->ic_nw_keys[i].wk_keylen < 16)
+ else if (vap->iv_nw_keys[i].wk_keylen > 13 &&
+ vap->iv_nw_keys[i].wk_keylen < 16)
wep.nw_keylen = 16;
wep.nw_keyidx = i;
wep.nw_length = (sizeof(uint32_t) * 3)
+ wep.nw_keylen;
- if (i == ic->ic_def_txkey)
+ if (i == vap->iv_def_txkey)
wep.nw_keyidx |= NDIS_80211_WEPKEY_TX;
- bcopy(ic->ic_nw_keys[i].wk_key,
+ bcopy(vap->iv_nw_keys[i].wk_key,
wep.nw_keydata, wep.nw_length);
len = sizeof(wep);
DPRINTF(("Setting WEP key %d\n", i));
@@ -2291,7 +2345,7 @@ ndis_setstate_80211(sc)
if (rval)
device_printf(sc->ndis_dev,
"enable WEP failed: %d\n", rval);
- if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ if (vap->iv_flags & IEEE80211_F_DROPUNENC)
arg = NDIS_80211_PRIVFILT_8021XWEP;
else
arg = NDIS_80211_PRIVFILT_ACCEPTALL;
@@ -2300,20 +2354,23 @@ ndis_setstate_80211(sc)
ndis_set_info(sc,
OID_802_11_PRIVACY_FILTER, &arg, &len);
}
- }
+ }
/* Set up WPA. */
- if (ic->ic_flags & IEEE80211_F_WPA1 && ic->ic_opt_ie_len &&
- ic->ic_caps & IEEE80211_C_WPA)
- if (ndis_set_wpa(sc))
+ if ((vap->iv_flags & IEEE80211_F_WPA) &&
+ vap->iv_appie_assocreq != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_assocreq;
+ error = ndis_set_wpa(sc, ie->ie_data, ie->ie_len);
+ if (error != 0)
device_printf(sc->ndis_dev, "WPA setup failed\n");
+ }
#ifdef notyet
/* Set network type. */
arg = 0;
- switch (ic->ic_curmode) {
+ switch (vap->iv_curmode) {
case IEEE80211_MODE_11A:
arg = NDIS_80211_NETTYPE_11OFDM5;
break;
@@ -2325,7 +2382,7 @@ ndis_setstate_80211(sc)
break;
default:
device_printf(sc->ndis_dev, "unknown mode: %d\n",
- ic->ic_curmode);
+ vap->iv_curmode);
}
if (arg) {
@@ -2365,7 +2422,7 @@ ndis_setstate_80211(sc)
if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) {
config.nc_dsconfig =
ic->ic_bsschan->ic_freq * 1000;
- ic->ic_bss->ni_chan = ic->ic_bsschan;
+ ni->ni_chan = ic->ic_bsschan;
len = sizeof(config);
config.nc_length = len;
config.nc_fhconfig.ncf_length =
@@ -2397,8 +2454,8 @@ ndis_setstate_80211(sc)
*/
len = IEEE80211_ADDR_LEN;
- if (ic->ic_flags & IEEE80211_F_DESBSSID &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_flags & IEEE80211_F_DESBSSID &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
bcopy(ni->ni_bssid, bssid, len);
else
bcopy(ifp->if_broadcastaddr, bssid, len);
@@ -2432,67 +2489,12 @@ ndis_setstate_80211(sc)
if (rval)
device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval);
- if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
return;
}
-static void
-ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr)
-{
- struct ieee80211com *ic = &((struct ndis_softc *)ifp->if_softc)->ic;
- struct ieee80211_node *ni = NULL;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
- imr->ifm_active |= IFM_AUTO;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ni = ic->ic_bss;
- /* calculate rate subtype */
- imr->ifm_active |= ieee80211_rate2media(ic,
- ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
- break;
- case IEEE80211_M_IBSS:
- ni = ic->ic_bss;
- /* calculate rate subtype */
- imr->ifm_active |= ieee80211_rate2media(ic,
- ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_AHDEMO:
- /* should not come here */
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- case IEEE80211_M_WDS:
- printf("WARNING: WDS operation mode not supported by NDIS\n");
- break;
- }
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A);
- break;
- case IEEE80211_MODE_11B:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B);
- break;
- case IEEE80211_MODE_11G:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G);
- break;
- case IEEE80211_MODE_TURBO_A:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A)
- | IFM_IEEE80211_TURBO;
- break;
- }
-}
-
static int
ndis_get_assoc(sc, assoc)
struct ndis_softc *sc;
@@ -2557,14 +2559,18 @@ ndis_getstate_80211(sc)
struct ndis_softc *sc;
{
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
ndis_wlan_bssid_ex *bs;
int rval, len, i = 0;
int chanflag;
uint32_t arg;
struct ifnet *ifp;
- ic = &sc->ic;
ifp = sc->ifp;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ ni = vap->iv_bss;
if (!NDIS_INITIALIZED(sc))
return;
@@ -2575,12 +2581,12 @@ ndis_getstate_80211(sc)
/* We're associated, retrieve info on the current bssid. */
ic->ic_curmode = ndis_nettype_mode(bs->nwbx_nettype);
chanflag = ndis_nettype_chan(bs->nwbx_nettype);
- IEEE80211_ADDR_COPY(ic->ic_bss->ni_bssid, bs->nwbx_macaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bs->nwbx_macaddr);
/* Get SSID from current association info. */
- bcopy(bs->nwbx_ssid.ns_ssid, ic->ic_bss->ni_essid,
+ bcopy(bs->nwbx_ssid.ns_ssid, ni->ni_essid,
bs->nwbx_ssid.ns_ssidlen);
- ic->ic_bss->ni_esslen = bs->nwbx_ssid.ns_ssidlen;
+ ni->ni_esslen = bs->nwbx_ssid.ns_ssidlen;
len = sizeof(arg);
rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len);
@@ -2589,29 +2595,29 @@ ndis_getstate_80211(sc)
rval);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) {
- ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
- for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
- if ((ic->ic_bss->ni_rates.rs_rates[i] &
+ ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
+ for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
+ if ((ni->ni_rates.rs_rates[i] &
IEEE80211_RATE_VAL) == arg / 5000)
break;
}
}
- if (i == ic->ic_bss->ni_rates.rs_nrates &&
+ if (i == ni->ni_rates.rs_nrates &&
isset(ic->ic_modecaps, IEEE80211_MODE_11G)) {
- ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G];
- for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
- if ((ic->ic_bss->ni_rates.rs_rates[i] &
+ ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G];
+ for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
+ if ((ni->ni_rates.rs_rates[i] &
IEEE80211_RATE_VAL) == arg / 5000)
break;
}
}
- if (i == ic->ic_bss->ni_rates.rs_nrates)
+ if (i == ni->ni_rates.rs_nrates)
device_printf(sc->ndis_dev, "no matching rate for: %d\n",
arg / 5000);
else
- ic->ic_bss->ni_txrate = i;
+ ni->ni_txrate = i;
if (ic->ic_caps & IEEE80211_C_PMGT) {
len = sizeof(arg);
@@ -2634,7 +2640,7 @@ ndis_getstate_80211(sc)
bs->nwbx_config.nc_dsconfig / 1000, chanflag);
if (ic->ic_curchan == NULL)
ic->ic_curchan = &ic->ic_channels[0];
- ic->ic_bss->ni_chan = ic->ic_curchan;
+ ni->ni_chan = ic->ic_curchan;
ic->ic_bsschan = ic->ic_curchan;
free(bs, M_TEMP);
@@ -2653,27 +2659,27 @@ ndis_getstate_80211(sc)
ic->ic_flags &= ~IEEE80211_F_WPA;
switch(arg) {
case NDIS_80211_AUTHMODE_OPEN:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_OPEN;
+ ni->ni_authmode = IEEE80211_AUTH_OPEN;
break;
case NDIS_80211_AUTHMODE_SHARED:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_SHARED;
+ ni->ni_authmode = IEEE80211_AUTH_SHARED;
break;
case NDIS_80211_AUTHMODE_AUTO:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_AUTO;
+ ni->ni_authmode = IEEE80211_AUTH_AUTO;
break;
case NDIS_80211_AUTHMODE_WPA:
case NDIS_80211_AUTHMODE_WPAPSK:
case NDIS_80211_AUTHMODE_WPANONE:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_WPA;
+ ni->ni_authmode = IEEE80211_AUTH_WPA;
ic->ic_flags |= IEEE80211_F_WPA1;
break;
case NDIS_80211_AUTHMODE_WPA2:
case NDIS_80211_AUTHMODE_WPA2PSK:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_WPA;
+ ni->ni_authmode = IEEE80211_AUTH_WPA;
ic->ic_flags |= IEEE80211_F_WPA2;
break;
default:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_NONE;
+ ni->ni_authmode = IEEE80211_AUTH_NONE;
break;
}
}
@@ -2699,6 +2705,7 @@ ndis_ioctl(ifp, command, data)
caddr_t data;
{
struct ndis_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifreq *ifr = (struct ifreq *) data;
struct ndis_oid_data oid;
struct ndis_evt evt;
@@ -2744,14 +2751,9 @@ ndis_ioctl(ifp, command, data)
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
- if (sc->ndis_80211) {
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- /*ndis_init(sc);*/
- error = 0;
- }
- } else
+ if (sc->ndis_80211)
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command);
+ else
error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
break;
case SIOCSIFCAP:
@@ -2762,22 +2764,6 @@ ndis_ioctl(ifp, command, data)
ifp->if_hwassist = 0;
ndis_set_offload(sc);
break;
- case SIOCG80211:
- if (!NDIS_INITIALIZED(sc))
- goto do_80211;
- if (sc->ndis_80211)
- error = ndis_80211_ioctl_get(ifp, command, data);
- else
- error = ENOTTY;
- break;
- case SIOCS80211:
- if (!NDIS_INITIALIZED(sc))
- goto do_80211;
- if (sc->ndis_80211)
- error = ndis_80211_ioctl_set(ifp, command, data);
- else
- error = ENOTTY;
- break;
case SIOCGDRVSPEC:
if ((error = priv_check(curthread, PRIV_DRIVER)))
break;
@@ -2878,17 +2864,7 @@ ndis_ioctl(ifp, command, data)
NDIS_UNLOCK(sc);
break;
default:
-do_80211:
- sc->ndis_skip = 1;
- if (sc->ndis_80211) {
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- error = 0;
- }
- } else
- error = ether_ioctl(ifp, command, data);
- sc->ndis_skip = 0;
+ error = ether_ioctl(ifp, command, data);
break;
}
@@ -2897,55 +2873,16 @@ do_80211:
return(error);
}
-static int
-ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data)
-{
- struct ndis_softc *sc;
- struct ieee80211req *ireq;
- int error, len;
- uint16_t nodename_u[IEEE80211_NWID_LEN + 1];
- unicode_string us;
- ansi_string as;
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_MLME:
- error = 0;
- break;
- case IEEE80211_IOC_STATIONNAME:
- error = ndis_get_info(sc, OID_GEN_MACHINE_NAME,
- &nodename_u, &len);
- if (error)
- break;
- us.us_len = us.us_maxlen = len;
- us.us_buf = nodename_u;
- if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) {
- error = ENOMEM;
- break;
- }
- ireq->i_len = as.as_len;
- error = copyout(as.as_buf, ireq->i_data, ireq->i_len);
- RtlFreeAnsiString(&as);
- break;
- default:
- error = ieee80211_ioctl(&sc->ic, command, data);
- break;
- }
- return(error);
-}
-
int
-ndis_del_key(ic, key)
- struct ieee80211com *ic;
+ndis_del_key(vap, key)
+ struct ieee80211vap *vap;
const struct ieee80211_key *key;
{
struct ndis_softc *sc;
ndis_80211_key rkey;
int len, error = 0;
- sc = ic->ic_ifp->if_softc;
+ sc = vap->iv_ic->ic_ifp->if_softc;
bzero((char *)&rkey, sizeof(rkey));
len = sizeof(rkey);
@@ -2953,7 +2890,7 @@ ndis_del_key(ic, key)
rkey.nk_len = len;
rkey.nk_keyidx = key->wk_keyix;
- bcopy(ic->ic_ifp->if_broadcastaddr,
+ bcopy(vap->iv_ifp->if_broadcastaddr,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len);
@@ -2971,16 +2908,18 @@ ndis_del_key(ic, key)
*/
static int
-ndis_add_key(ic, key, mac)
- struct ieee80211com *ic;
+ndis_add_key(vap, key, mac)
+ struct ieee80211vap *vap;
const struct ieee80211_key *key;
const uint8_t mac[IEEE80211_ADDR_LEN];
{
struct ndis_softc *sc;
+ struct ifnet *ifp;
ndis_80211_key rkey;
int len, error = 0;
- sc = ic->ic_ifp->if_softc;
+ ifp = vap->iv_ic->ic_ifp;
+ sc = ifp->if_softc;
switch (key->wk_cipher->ic_cipher) {
case IEEE80211_CIPHER_TKIP:
@@ -3005,17 +2944,17 @@ ndis_add_key(ic, key, mac)
rkey.nk_keyidx |= 1 << 31;
if (key->wk_flags & IEEE80211_KEY_GROUP) {
- bcopy(ic->ic_ifp->if_broadcastaddr,
+ bcopy(ifp->if_broadcastaddr,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
} else {
- bcopy(ic->ic_bss->ni_bssid,
+ bcopy(vap->iv_bss->ni_bssid,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
/* pairwise key */
rkey.nk_keyidx |= 1 << 30;
}
/* need to set bit 29 based on keyrsc */
- rkey.nk_keyrsc = key->wk_keyrsc;
+ rkey.nk_keyrsc = key->wk_keyrsc[0]; /* XXX need tid */
if (rkey.nk_keyrsc)
rkey.nk_keyidx |= 1 << 29;
@@ -3050,54 +2989,6 @@ ndis_add_key(ic, key, mac)
return (1);
}
-static int
-ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data)
-{
- struct ndis_softc *sc;
- struct ieee80211req *ireq;
- int error = EINVAL, len;
- ansi_string as;
- unicode_string us;
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_COUNTERMEASURES:
- case IEEE80211_IOC_DROPUNENCRYPTED:
- error = 0;
- break;
- case IEEE80211_IOC_STATIONNAME:
- error = priv_check(curthread, PRIV_NET80211_MANAGE);
- if (error)
- break;
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- as.as_len = as.as_maxlen = ireq->i_len;
- as.as_buf = ireq->i_data;
- if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) {
- error = ENOMEM;
- break;
- }
- len = us.us_len;
- error = ndis_set_info(sc, OID_GEN_MACHINE_NAME,
- us.us_buf, &len);
- RtlFreeUnicodeString(&us);
- break;
- default:
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- error = 0;
- }
- }
-
- return(error);
-}
-
static void
ndis_resettask(d, arg)
device_object *d;
@@ -3142,13 +3033,8 @@ ndis_stop(sc)
struct ndis_softc *sc;
{
struct ifnet *ifp;
- struct ieee80211com *ic;
int i;
- ic = &sc->ic;
- if (sc->ndis_80211)
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
ifp = sc->ifp;
callout_drain(&sc->ndis_stat_callout);
@@ -3191,24 +3077,26 @@ ndis_shutdown(dev)
}
static int
-ndis_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ndis_vap *nvp = NDIS_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ndis_softc *sc = ifp->if_softc;
- enum ieee80211_state ostate;
+ enum ieee80211_state ostate;
DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]));
- ostate = ic->ic_state;
- ic->ic_state = nstate;
+ ostate = vap->iv_state;
+ vap->iv_state = nstate;
switch (nstate) {
/* pass on to net80211 */
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- return (sc->ndis_newstate(ic, nstate, arg));
+ return nvp->newstate(vap, nstate, arg);
case IEEE80211_S_ASSOC:
if (ostate != IEEE80211_S_AUTH)
@@ -3229,14 +3117,19 @@ static void
ndis_scan(void *arg, int npending)
{
struct ndis_softc *sc = arg;
- struct ieee80211com *ic = (void *)&sc->ic;
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
+ struct ieee80211_scan_state *ss;
ndis_80211_ssid ssid;
int error, len;
+ ic = sc->ifp->if_l2com;
+ ss = ic->ic_scan;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: scan aborted\n", __func__));
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
return;
}
@@ -3257,7 +3150,7 @@ ndis_scan(void *arg, int npending)
NULL, &len);
if (error) {
DPRINTF(("%s: scan command failed\n", __func__));
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
return;
}
@@ -3267,13 +3160,14 @@ ndis_scan(void *arg, int npending)
return;
ndis_scan_results(sc);
- ieee80211_scan_done(ic);
+ ieee80211_scan_done(vap);
}
static void
ndis_scan_results(struct ndis_softc *sc)
{
- struct ieee80211com *ic = (void *)&sc->ic;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
ndis_80211_bssid_list_ex *bl;
ndis_wlan_bssid_ex *wb;
struct ieee80211_scanparams sp;
@@ -3285,6 +3179,8 @@ ndis_scan_results(struct ndis_softc *sc)
uint8_t rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t *frm, *efrm;
+ ic = sc->ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
noise = -96;
len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16);
@@ -3344,10 +3240,7 @@ ndis_scan_results(struct ndis_softc *sc)
chanflag = ndis_nettype_chan(wb->nwbx_nettype);
freq = wb->nwbx_config.nc_dsconfig / 1000;
- sp.bchan = ieee80211_mhz2ieee(freq, chanflag);
- sp.curchan = ieee80211_find_channel(ic, freq, chanflag);
- if (sp.curchan == NULL)
- sp.curchan = &ic->ic_channels[0];
+ sp.chan = sp.bchan = ieee80211_mhz2ieee(freq, chanflag);
/* Process extended info from AP */
if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) {
@@ -3378,7 +3271,7 @@ done:
DPRINTF(("scan: bssid %s chan %dMHz (%d/%d) rssi %d\n",
ether_sprintf(wb->nwbx_macaddr), freq, sp.bchan, chanflag,
rssi));
- ieee80211_add_scan(ic, &sp, &wh, 0, rssi, noise, rstamp);
+ ieee80211_add_scan(vap, &sp, &wh, 0, rssi, noise, rstamp);
wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len);
}
free(bl, M_DEVBUF);
@@ -3400,13 +3293,13 @@ ndis_set_channel(struct ieee80211com *ic)
}
static void
-ndis_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+ndis_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* ignore */
}
static void
-ndis_scan_mindwell(struct ieee80211com *ic)
+ndis_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h
index cdbf543..6814709 100644
--- a/sys/dev/if_ndis/if_ndisvar.h
+++ b/sys/dev/if_ndis/if_ndisvar.h
@@ -99,8 +99,15 @@ struct ndis_evt {
char *ne_buf;
};
+struct ndis_vap {
+ struct ieee80211vap vap;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define NDIS_VAP(vap) ((struct ndis_vap *)(vap))
+
struct ndis_softc {
- struct ieee80211com ic; /* interface info */
struct ifnet *ifp;
struct ifmedia ifmedia; /* media info */
u_long ndis_hwassist;
diff --git a/sys/dev/ipw/if_ipw.c b/sys/dev/ipw/if_ipw.c
index 8a69a40..f21bc5b 100644
--- a/sys/dev/ipw/if_ipw.c
+++ b/sys/dev/ipw/if_ipw.c
@@ -107,13 +107,21 @@ static const struct ipw_ident ipw_ident_table[] = {
{ 0, 0, NULL }
};
+static struct ieee80211vap *ipw_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ipw_vap_delete(struct ieee80211vap *);
static int ipw_dma_alloc(struct ipw_softc *);
static void ipw_release(struct ipw_softc *);
-static int ipw_media_change(struct ifnet *);
static void ipw_media_status(struct ifnet *, struct ifmediareq *);
-static int ipw_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t);
static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *);
+static void ipw_assocsuccess(void *, int);
+static void ipw_assocfailed(void *, int);
+static void ipw_scandone(void *, int);
+static void ipw_bmiss(void *, int);
static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *);
static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *,
struct ipw_soft_bd *, struct ipw_soft_buf *);
@@ -126,6 +134,8 @@ static const char * ipw_cmdname(int);
static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t);
static int ipw_tx_start(struct ifnet *, struct mbuf *,
struct ieee80211_node *);
+static int ipw_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void ipw_start(struct ifnet *);
static void ipw_start_locked(struct ifnet *);
static void ipw_watchdog(void *);
@@ -138,12 +148,10 @@ static int ipw_load_ucode(struct ipw_softc *, const char *, int);
static int ipw_load_firmware(struct ipw_softc *, const char *, int);
static int ipw_config(struct ipw_softc *);
static void ipw_assoc_task(void *, int);
-static int ipw_auth_and_assoc(struct ipw_softc *);
static void ipw_disassoc_task(void *, int);
-static int ipw_disassociate(struct ipw_softc *);
static void ipw_init_task(void *, int);
static void ipw_init(void *);
-static void ipw_init_locked(struct ipw_softc *, int);
+static void ipw_init_locked(struct ipw_softc *);
static void ipw_stop(void *);
static void ipw_stop_locked(struct ipw_softc *);
static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS);
@@ -163,8 +171,9 @@ static int ipw_scan(struct ipw_softc *);
static void ipw_scan_start(struct ieee80211com *);
static void ipw_scan_end(struct ieee80211com *);
static void ipw_set_channel(struct ieee80211com *);
-static void ipw_scan_curchan(struct ieee80211com *, unsigned long maxdwell);
-static void ipw_scan_mindwell(struct ieee80211com *);
+static void ipw_scan_curchan(struct ieee80211_scan_state *,
+ unsigned long maxdwell);
+static void ipw_scan_mindwell(struct ieee80211_scan_state *);
static int ipw_probe(device_t);
static int ipw_attach(device_t);
@@ -219,7 +228,7 @@ ipw_attach(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ieee80211_channel *c;
uint16_t val;
int error, i;
@@ -231,8 +240,7 @@ ipw_attach(device_t dev)
TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc);
TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc);
- TASK_INIT(&sc->sc_assoc_task, 0, ipw_assoc_task, sc);
- TASK_INIT(&sc->sc_disassoc_task, 0, ipw_disassoc_task, sc);
+ TASK_INIT(&sc->sc_bmiss_task, 0, ipw_bmiss, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
@@ -262,24 +270,25 @@ ipw_attach(device_t dev)
RF_ACTIVE | RF_SHAREABLE);
if (sc->irq == NULL) {
device_printf(dev, "could not allocate interrupt resource\n");
- goto fail;
+ goto fail1;
}
if (ipw_reset(sc) != 0) {
device_printf(dev, "could not reset adapter\n");
- goto fail;
+ goto fail2;
}
if (ipw_dma_alloc(sc) != 0) {
device_printf(dev, "could not allocate DMA resources\n");
- goto fail;
+ goto fail2;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
- goto fail;
+ goto fail3;
}
+ ic = ifp->if_l2com;
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
@@ -292,9 +301,8 @@ ipw_attach(device_t dev)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_phytype = IEEE80211_T_DS;
/* set device capabilities */
ic->ic_caps = IEEE80211_C_IBSS /* IBSS mode supported */
@@ -333,20 +341,18 @@ ipw_attach(device_t dev)
sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH;
ieee80211_ifattach(ic);
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ipw_newstate;
- ieee80211_media_init(ic, ipw_media_change, ipw_media_status);
-
ic->ic_scan_start = ipw_scan_start;
ic->ic_scan_end = ipw_scan_end;
ic->ic_set_channel = ipw_set_channel;
ic->ic_scan_curchan = ipw_scan_curchan;
ic->ic_scan_mindwell = ipw_scan_mindwell;
+ ic->ic_raw_xmit = ipw_raw_xmit;
+
+ ic->ic_vap_create = ipw_vap_create;
+ ic->ic_vap_delete = ipw_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -359,8 +365,6 @@ ipw_attach(device_t dev)
/*
* Add a few sysctl knobs.
*/
- sc->dwelltime = 100;
-
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I",
@@ -371,11 +375,6 @@ ipw_attach(device_t dev)
CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S",
"statistics");
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
/*
* Hook our interrupt after all initialization is complete.
*/
@@ -383,15 +382,23 @@ ipw_attach(device_t dev)
NULL, ipw_intr, sc, &sc->sc_ih);
if (error != 0) {
device_printf(dev, "could not set up interrupt\n");
- goto fail;
+ goto fail4;
}
if (bootverbose)
ieee80211_announce(ic);
return 0;
-
-fail: ipw_detach(dev);
+fail4:
+ if_free(ifp);
+fail3:
+ ipw_release(sc);
+fail2:
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+fail1:
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
+fail:
+ mtx_destroy(&sc->sc_mtx);
return ENXIO;
}
@@ -399,33 +406,27 @@ static int
ipw_detach(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ipw_stop(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
callout_drain(&sc->sc_wdtimer);
taskqueue_drain(taskqueue_fast, &sc->sc_init_task);
taskqueue_drain(taskqueue_fast, &sc->sc_scan_task);
- taskqueue_drain(taskqueue_fast, &sc->sc_assoc_task);
- taskqueue_drain(taskqueue_fast, &sc->sc_disassoc_task);
-
- if (ifp != NULL) {
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
- }
+ taskqueue_drain(taskqueue_fast, &sc->sc_bmiss_task);
ipw_release(sc);
- if (sc->irq != NULL) {
- bus_teardown_intr(dev, sc->irq, sc->sc_ih);
- bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- }
+ bus_teardown_intr(dev, sc->irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- if (sc->mem != NULL)
- bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
- if (ifp != NULL)
- if_free(ifp);
+ if_free(ifp);
if (sc->sc_firmware != NULL) {
firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
@@ -437,6 +438,103 @@ ipw_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+ipw_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ipw_softc *sc = ifp->if_softc;
+ struct ipw_vap *ivp;
+ struct ieee80211vap *vap;
+ const struct firmware *fp;
+ const struct ipw_firmware_hdr *hdr;
+ const char *imagename;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ imagename = "ipw_bss";
+ break;
+ case IEEE80211_M_IBSS:
+ imagename = "ipw_ibss";
+ break;
+ case IEEE80211_M_MONITOR:
+ imagename = "ipw_monitor";
+ break;
+ default:
+ return NULL;
+ }
+
+ /*
+ * Load firmware image using the firmware(9) subsystem. Doing
+ * this unlocked is ok since we're single-threaded by the
+ * 802.11 layer.
+ */
+ if (sc->sc_firmware == NULL ||
+ strcmp(sc->sc_firmware->name, imagename) != 0) {
+ if (sc->sc_firmware != NULL)
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = firmware_get(imagename);
+ }
+ if (sc->sc_firmware == NULL) {
+ device_printf(sc->sc_dev,
+ "could not load firmware image '%s'\n", imagename);
+ return NULL;
+ }
+ fp = sc->sc_firmware;
+ if (fp->datasize < sizeof *hdr) {
+ device_printf(sc->sc_dev,
+ "firmware image too short %zu\n", fp->datasize);
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = NULL;
+ return NULL;
+ }
+ hdr = (const struct ipw_firmware_hdr *)fp->data;
+ if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) +
+ le32toh(hdr->ucodesz)) {
+ device_printf(sc->sc_dev,
+ "firmware image too short %zu\n", fp->datasize);
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = NULL;
+ return NULL;
+ }
+
+ ivp = (struct ipw_vap *) malloc(sizeof(struct ipw_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (ivp == NULL)
+ return NULL;
+ vap = &ivp->vap;
+
+ TASK_INIT(&ivp->assoc_task, 0, ipw_assoc_task, vap);
+ TASK_INIT(&ivp->disassoc_task, 0, ipw_disassoc_task, vap);
+ TASK_INIT(&ivp->assoc_success_task, 0, ipw_assocsuccess, vap);
+ TASK_INIT(&ivp->assoc_failed_task, 0, ipw_assocfailed, vap);
+ TASK_INIT(&ivp->scandone_task, 0, ipw_scandone, vap);
+
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ ivp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ipw_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ipw_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ipw_vap_delete(struct ieee80211vap *vap)
+{
+ struct ipw_vap *ivp = IPW_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(ivp, M_80211_VAP);
+}
+
static int
ipw_dma_alloc(struct ipw_softc *sc)
{
@@ -748,45 +846,17 @@ static int
ipw_resume(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
- if (ifp->if_flags & IFF_UP) {
- ipw_init_locked(sc, 0);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ipw_start_locked(ifp);
- }
-
- IPW_UNLOCK(sc);
+ if (ifp->if_flags & IFF_UP)
+ ipw_init(sc);
return 0;
}
static int
-ipw_media_change(struct ifnet *ifp)
-{
- struct ipw_softc *sc = ifp->if_softc;
- int error;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- ipw_init_locked(sc, 0);
- error = 0;
- }
- IPW_UNLOCK(sc);
-
- return (error);
-}
-
-static int
ipw_cvtrate(int ipwrate)
{
switch (ipwrate) {
@@ -805,47 +875,26 @@ ipw_cvtrate(int ipwrate)
static void
ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int rate;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ipw_softc *sc = ic->ic_ifp->if_softc;
/* read current transmission rate from adapter */
- rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
- imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
-
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_IBSS;
- break;
-
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
-
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_WDS:
- /* should not get there */
- break;
- }
+ vap->iv_bss->ni_txrate = ipw_cvtrate(
+ ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
+ ieee80211_media_status(ifp, imr);
}
static int
-ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ipw_vap *ivp = IPW_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ipw_softc *sc = ifp->if_softc;
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
switch (nstate) {
@@ -859,36 +908,40 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
- if (ic->ic_state == IEEE80211_S_SCAN)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_assoc_task);
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
+ }
}
break;
case IEEE80211_S_INIT:
if (sc->flags & IPW_FLAG_ASSOCIATED)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_disassoc_task);
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->disassoc_task);
break;
case IEEE80211_S_AUTH:
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_assoc_task);
- break;
+ taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
case IEEE80211_S_ASSOC:
/*
* If we are not transitioning from AUTH the resend the
* association request.
*/
- if (ic->ic_state != IEEE80211_S_AUTH)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_assoc_task);
+ if (vap->iv_state != IEEE80211_S_AUTH) {
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
+ }
break;
default:
break;
}
- return (*sc->sc_newstate)(ic, nstate, arg);
+ return ivp->newstate(vap, nstate, arg);
}
/*
@@ -965,10 +1018,44 @@ ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
}
static void
+ipw_assocsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
+}
+
+static void
+ipw_assocfailed(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+}
+
+static void
+ipw_scandone(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_scan_done(vap);
+}
+
+static void
+ipw_bmiss(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_beacon_miss(ic);
+}
+
+static void
ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
{
-#define IEEESTATE(ic) ieee80211_state_name[ic->ic_state]
- struct ieee80211com *ic = &sc->sc_ic;
+#define IEEESTATE(vap) ieee80211_state_name[vap->iv_state]
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t state;
bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD);
@@ -978,27 +1065,32 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
switch (state) {
case IPW_STATE_ASSOCIATED:
DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- sc->flags |= IPW_FLAG_ASSOCIATED;
+ IEEESTATE(vap), sc->flags));
/* XXX suppress state change in case the fw auto-associates */
- if (ic->ic_state != IEEE80211_S_ASSOC) {
- DPRINTF(("Unexpected association (state %u)\n",
- ic->ic_state));
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ if ((sc->flags & IPW_FLAG_ASSOCIATING) == 0) {
+ DPRINTF(("Unexpected association (%s, flags 0x%x)\n",
+ IEEESTATE(vap), sc->flags));
+ break;
+ }
+ sc->flags &= ~IPW_FLAG_ASSOCIATING;
+ sc->flags |= IPW_FLAG_ASSOCIATED;
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_success_task);
break;
case IPW_STATE_SCANNING:
DPRINTFN(3, ("Scanning (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
+ IEEESTATE(vap), sc->flags));
/*
* NB: Check driver state for association on assoc
* loss as the firmware will immediately start to
* scan and we would treat it as a beacon miss if
* we checked the 802.11 layer state.
*/
- if (sc->flags & IPW_FLAG_ASSOCIATED)
- ieee80211_beacon_miss(ic);
+ if (sc->flags & IPW_FLAG_ASSOCIATED) {
+ /* XXX probably need to issue disassoc to fw */
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task);
+ }
break;
case IPW_STATE_SCAN_COMPLETE:
@@ -1009,14 +1101,15 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
* around this by marking the HACK flag and skipping
* the first scan complete event.
*/
+ DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n",
+ IEEESTATE(vap), sc->flags));
if (sc->flags & IPW_FLAG_HACK) {
sc->flags &= ~IPW_FLAG_HACK;
break;
}
- DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
if (sc->flags & IPW_FLAG_SCANNING) {
- ieee80211_scan_done(ic);
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->scandone_task);
sc->flags &= ~IPW_FLAG_SCANNING;
sc->sc_scan_timer = 0;
}
@@ -1024,27 +1117,31 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
case IPW_STATE_ASSOCIATION_LOST:
DPRINTFN(2, ("Association lost (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- sc->flags &= ~IPW_FLAG_ASSOCIATED;
- if (ic->ic_state == IEEE80211_S_RUN)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ IEEESTATE(vap), sc->flags));
+ sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED);
+ if (vap->iv_state == IEEE80211_S_RUN)
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_failed_task);
break;
case IPW_STATE_DISABLED:
+ /* XXX? is this right? */
+ sc->flags &= ~(IPW_FLAG_HACK | IPW_FLAG_SCANNING |
+ IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED);
DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
+ IEEESTATE(vap), sc->flags));
break;
case IPW_STATE_RADIO_DISABLED:
- DPRINTFN(2, ("Radio off (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- ic->ic_ifp->if_flags &= ~IFF_UP;
+ device_printf(sc->sc_dev, "radio turned off\n");
+ ieee80211_notify_radio(ic, 0);
ipw_stop_locked(sc);
+ /* XXX start polling thread to detect radio on */
break;
default:
DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n",
- __func__, state, IEEESTATE(ic), sc->flags));
+ __func__, state, IEEESTATE(vap), sc->flags));
break;
}
#undef IEEESTATE
@@ -1056,7 +1153,8 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
static void
ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ic->ic_curchan = chan;
sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
@@ -1072,7 +1170,8 @@ ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan)
static void
ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_channel *c;
struct ieee80211_frame *wh;
uint8_t subtype;
@@ -1089,6 +1188,7 @@ ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m)
subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
return;
+ /* XXX use ieee80211_parse_beacon */
frm = (uint8_t *)(wh + 1);
efrm = mtod(m, uint8_t *) + m->m_len;
@@ -1116,10 +1216,9 @@ static void
ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct mbuf *mnew, *m;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
bus_addr_t physaddr;
int error;
@@ -1177,7 +1276,7 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = le32toh(status->len);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1185,21 +1284,19 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
if (sc->flags & IPW_FLAG_SCANNING)
ipw_fix_channel(sc, m);
- wh = mtod(m, struct ieee80211_frame *);
IPW_UNLOCK(sc);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, status->rssi, -95/*XXX*/, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, status->rssi, -95, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, status->rssi, -95, 0);
IPW_LOCK(sc);
bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE);
@@ -1208,7 +1305,6 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
static void
ipw_rx_intr(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ipw_status *status;
struct ipw_soft_bd *sbd;
struct ipw_soft_buf *sbuf;
@@ -1243,11 +1339,7 @@ ipw_rx_intr(struct ipw_softc *sc)
case IPW_STATUS_CODE_NOTIFICATION:
DPRINTFN(2, ("notification status, len %u flags 0x%x\n",
le32toh(status->len), status->flags));
- if (ic->ic_state == IEEE80211_S_AUTH) {
- /* XXX assume auth notification */
- ieee80211_node_authorize(ic->ic_bss);
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
- }
+ /* XXX maybe drive state machine AUTH->ASSOC? */
break;
default:
@@ -1311,7 +1403,7 @@ ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd)
static void
ipw_tx_intr(struct ipw_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct ipw_soft_bd *sbd;
uint32_t r, i;
@@ -1359,7 +1451,7 @@ ipw_intr(void *arg)
if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) {
device_printf(sc->sc_dev, "firmware error\n");
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_init_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task);
r = 0; /* don't process more interrupts */
}
@@ -1447,6 +1539,8 @@ ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len)
bus_addr_t physaddr;
int error;
+ IPW_LOCK_ASSERT(sc);
+
if (sc->flags & IPW_FLAG_BUSY) {
device_printf(sc->sc_dev, "%s: %s not sent, busy\n",
__func__, ipw_cmdname(type));
@@ -1514,7 +1608,7 @@ static int
ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_frame *wh;
struct ipw_soft_bd *sbd;
struct ipw_soft_hdr *shdr;
@@ -1528,24 +1622,23 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
-
/* packet header may have moved, reset our local pointer */
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ipw_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
shdr = SLIST_FIRST(&sc->free_shdr);
@@ -1660,6 +1753,16 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
return 0;
}
+static int
+ipw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
ipw_start(struct ifnet *ifp)
{
@@ -1675,54 +1778,31 @@ static void
ipw_start_locked(struct ifnet *ifp)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
struct ieee80211_node *ni;
+ struct mbuf *m;
IPW_LOCK_ASSERT(sc);
- if (ic->ic_state != IEEE80211_S_RUN)
- return;
-
for (;;) {
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
break;
-
if (sc->txfree < 1 + IPW_MAX_NSEG) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
-
- if (m0->m_len < sizeof (struct ether_header) &&
- (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL)
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
ieee80211_free_node(ni);
continue;
}
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ipw_tx_start(ifp, m0, ni) != 0) {
+ if (ipw_tx_start(ifp, m, ni) != 0) {
ieee80211_free_node(ni);
ifp->if_oerrors++;
break;
}
-
/* start watchdog timer */
sc->sc_tx_timer = 5;
}
@@ -1732,8 +1812,8 @@ static void
ipw_watchdog(void *arg)
{
struct ipw_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
IPW_LOCK_ASSERT(sc);
@@ -1741,8 +1821,7 @@ ipw_watchdog(void *arg)
if (--sc->sc_tx_timer == 0) {
if_printf(ifp, "device timeout\n");
ifp->if_oerrors++;
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_init_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task);
}
}
if (sc->sc_scan_timer > 0) {
@@ -1750,7 +1829,7 @@ ipw_watchdog(void *arg)
DPRINTFN(3, ("Scan timeout\n"));
/* End the scan */
if (sc->flags & IPW_FLAG_SCANNING) {
- ieee80211_scan_done(ic);
+ ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
sc->flags &= ~IPW_FLAG_SCANNING;
}
}
@@ -1763,37 +1842,35 @@ static int
ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
IPW_LOCK_DECL;
IPW_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- ipw_init_locked(sc, 0);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ ipw_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ipw_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- ipw_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
}
-
IPW_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2010,13 +2087,15 @@ ipw_load_firmware(struct ipw_softc *sc, const char *fw, int size)
static int
ipw_setwepkeys(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ipw_wep_key wepkey;
struct ieee80211_key *wk;
int error, i;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- wk = &ic->ic_crypto.cs_nw_keys[i];
+ wk = &vap->iv_nw_keys[i];
if (wk->wk_cipher == NULL ||
wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP)
@@ -2166,7 +2245,8 @@ done:
static int
ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t data;
int error;
@@ -2178,308 +2258,126 @@ ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan)
return error;
}
-static int
-ipw_config(struct ipw_softc *sc)
+static void
+ipw_assoc_task(void *context, int pending)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = context;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ipw_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni = vap->iv_bss;
struct ipw_security security;
- struct ipw_configuration config;
uint32_t data;
int error;
+ IPW_LOCK_DECL;
+ IPW_LOCK(sc);
error = ipw_disable(sc);
if (error != 0)
- return error;
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_WDS: /* XXX */
- data = htole32(IPW_MODE_BSS);
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- data = htole32(IPW_MODE_IBSS);
- break;
- case IEEE80211_M_MONITOR:
- data = htole32(IPW_MODE_MONITOR);
- break;
- }
- DPRINTF(("Setting mode to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_MONITOR) {
- error = ipw_setchannel(sc, ic->ic_curchan);
- if (error != 0)
- return error;
- }
-
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- return ipw_enable(sc);
-
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
- error = ipw_cmd(sc, IPW_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
- IEEE80211_ADDR_LEN);
- if (error != 0)
- return error;
-
- config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK |
- IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- config.flags |= htole32(IPW_CFG_IBSS_AUTO_START);
- if (ifp->if_flags & IFF_PROMISC)
- config.flags |= htole32(IPW_CFG_PROMISCUOUS);
- config.bss_chan = htole32(0x3fff); /* channels 1-14 */
- config.ibss_chan = htole32(0x7ff); /* channels 1-11 */
- DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags)));
- error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config);
- if (error != 0)
- return error;
-
- data = htole32(0x3); /* 1, 2 */
- DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- /* NB: use the same rate set */
- DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(0xf); /* 1, 2, 5.5, 11 */
- DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(IPW_POWER_MODE_CAM);
- DPRINTF(("Setting power mode to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- data = htole32(32); /* default value */
- DPRINTF(("Setting tx power index to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
-
- data = htole32(ic->ic_rtsthreshold);
- DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(ic->ic_fragthreshold);
- DPRINTF(("Setting frag threshold to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
- error = ipw_setssid(sc, ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len);
- if (error != 0)
- return error;
-
- error = ipw_setbssid(sc, NULL);
- if (error != 0)
- return error;
-
- if (ic->ic_flags & IEEE80211_F_DESBSSID) {
- DPRINTF(("Setting desired BSSID to %6D\n", ic->ic_des_bssid,
- ":"));
- error = ipw_cmd(sc, IPW_CMD_SET_DESIRED_BSSID,
- ic->ic_des_bssid, IEEE80211_ADDR_LEN);
- if (error != 0)
- return error;
- }
+ goto done;
memset(&security, 0, sizeof security);
- security.authmode = (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) ?
+ security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ?
IPW_AUTH_SHARED : IPW_AUTH_OPEN;
security.ciphers = htole32(IPW_CIPHER_NONE);
DPRINTF(("Setting authmode to %u\n", security.authmode));
error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security,
sizeof security);
if (error != 0)
- return error;
-
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- error = ipw_setwepkeys(sc);
- if (error != 0)
- return error;
-
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) {
- data = htole32(ic->ic_crypto.cs_def_txkey);
- DPRINTF(("Setting wep tx key index to %u\n",
- le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
- }
-
- data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
- DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opt_ie != NULL) {
- error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len);
- if (error != 0)
- return error;
- }
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- data = htole32(ic->ic_bintval);
- DPRINTF(("Setting beacon interval to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_BEACON_INTERVAL, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
-
- error = ipw_setscanopts(sc, 0x3fff, 0);
- if (error != 0)
- return error;
-
- return (ipw_enable(sc));
-}
-
-/*
- * Handler for sc_assoc_task. This is a simple wrapper around
- * ipw_auth_and_assoc().
- */
-static void
-ipw_assoc_task(void *context, int pending)
-{
- struct ipw_softc *sc = context;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
- ipw_auth_and_assoc(sc);
- IPW_UNLOCK(sc);
-}
-
-static int
-ipw_auth_and_assoc(struct ipw_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
- struct ipw_security security;
- uint32_t data;
- int error;
+ goto done;
- error = ipw_disable(sc);
+ data = htole32(vap->iv_rtsthreshold);
+ DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
if (error != 0)
- return (error);
+ goto done;
- memset(&security, 0, sizeof security);
- security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ?
- IPW_AUTH_SHARED : IPW_AUTH_OPEN;
- security.ciphers = htole32(IPW_CIPHER_NONE);
- DPRINTF(("Setting authmode to %u\n", security.authmode));
- error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security,
- sizeof security);
+ data = htole32(vap->iv_fragthreshold);
+ DPRINTF(("Setting frag threshold to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
if (error != 0)
- return (error);
+ goto done;
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
error = ipw_setwepkeys(sc);
if (error != 0)
- return error;
+ goto done;
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) {
- data = htole32(ic->ic_crypto.cs_def_txkey);
+ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) {
+ data = htole32(vap->iv_def_txkey);
DPRINTF(("Setting wep tx key index to %u\n",
le32toh(data)));
error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data,
sizeof data);
if (error != 0)
- return error;
+ goto done;
}
}
- data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
+ data = htole32((vap->iv_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data)));
error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data);
if (error != 0)
- return error;
+ goto done;
error = ipw_setssid(sc, ni->ni_essid, ni->ni_esslen);
if (error != 0)
- return (error);
+ goto done;
error = ipw_setbssid(sc, ni->ni_bssid);
if (error != 0)
- return (error);
+ goto done;
- if (ic->ic_opt_ie != NULL) {
- error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len);
+ if (vap->iv_appie_assocreq != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_assocreq;
+ error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len);
if (error != 0)
- return error;
+ goto done;
}
if (ic->ic_opmode == IEEE80211_M_IBSS) {
error = ipw_setchannel(sc, ni->ni_chan);
if (error != 0)
- return (error);
+ goto done;
}
/* lock scan to ap's channel and enable associate */
error = ipw_setscanopts(sc,
- 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0);
+ 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0);
+ if (error != 0)
+ goto done;
- return ipw_enable(sc); /* finally, enable adapter */
+ error = ipw_enable(sc); /* finally, enable adapter */
+ if (error == 0)
+ sc->flags |= IPW_FLAG_ASSOCIATING;
+done:
+ IPW_UNLOCK(sc);
}
-/*
- * Handler for sc_disassoc_task. This is a simple wrapper around
- * ipw_disassociate().
- */
static void
ipw_disassoc_task(void *context, int pending)
{
- struct ipw_softc *sc = context;
+ struct ieee80211vap *vap = context;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct ipw_softc *sc = ifp->if_softc;
IPW_LOCK_DECL;
IPW_LOCK(sc);
- ipw_disassociate(sc);
- IPW_UNLOCK(sc);
-}
-
-static int
-ipw_disassociate(struct ipw_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
-
DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":"));
-
/*
* NB: don't try to do this if ipw_stop_master has
* shutdown the firmware and disabled interrupts.
*/
- if (!(sc->flags & IPW_FLAG_FW_INITED))
- return (0);
-
- sc->flags &= ~IPW_FLAG_ASSOCIATED;
- /*
- * NB: firmware currently ignores bssid parameter, but
- * supply it in case this changes (follow linux driver).
- */
- return ipw_cmd(sc, IPW_CMD_DISASSOCIATE,
- ni->ni_bssid, IEEE80211_ADDR_LEN);
+ if (sc->flags & IPW_FLAG_FW_INITED) {
+ sc->flags &= ~IPW_FLAG_ASSOCIATED;
+ /*
+ * NB: firmware currently ignores bssid parameter, but
+ * supply it in case this changes (follow linux driver).
+ */
+ (void) ipw_cmd(sc, IPW_CMD_DISASSOCIATE,
+ ni->ni_bssid, IEEE80211_ADDR_LEN);
+ }
+ IPW_UNLOCK(sc);
}
/*
@@ -2496,27 +2394,31 @@ static void
ipw_init(void *priv)
{
struct ipw_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
IPW_LOCK_DECL;
IPW_LOCK(sc);
- ipw_init_locked(sc, 0);
+ ipw_init_locked(sc);
IPW_UNLOCK(sc);
+
+ ieee80211_start_all(ic);
}
static void
-ipw_init_locked(struct ipw_softc *sc, int force)
+ipw_init_locked(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
const struct firmware *fp;
const struct ipw_firmware_hdr *hdr;
- const char *imagename, *fw;
- IPW_LOCK_DECL;
+ const char *fw;
IPW_LOCK_ASSERT(sc);
DPRINTF(("%s: state %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state], sc->flags));
+ ieee80211_state_name[vap->iv_state], sc->flags));
/*
* Avoid re-entrant calls. We need to release the mutex in ipw_init()
@@ -2531,63 +2433,22 @@ ipw_init_locked(struct ipw_softc *sc, int force)
if (ipw_reset(sc) != 0) {
device_printf(sc->sc_dev, "could not reset adapter\n");
- goto fail1;
- }
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- imagename = "ipw_bss";
- break;
- case IEEE80211_M_IBSS:
- imagename = "ipw_ibss";
- break;
- case IEEE80211_M_MONITOR:
- imagename = "ipw_monitor";
- break;
- default:
- imagename = NULL; /* should not get there */
- }
-
- /*
- * Load firmware image using the firmware(9) subsystem. We need to
- * release the driver's lock first.
- */
- if (sc->sc_firmware == NULL || strcmp(sc->sc_firmware->name,
- imagename) != 0) {
- IPW_UNLOCK(sc);
- if (sc->sc_firmware != NULL)
- firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
- sc->sc_firmware = firmware_get(imagename);
- IPW_LOCK(sc);
+ goto fail;
}
if (sc->sc_firmware == NULL) {
- device_printf(sc->sc_dev,
- "could not load firmware image '%s'\n", imagename);
- goto fail1;
+ device_printf(sc->sc_dev, "no firmware\n");
+ goto fail;
}
-
+ /* NB: consistency already checked on load */
fp = sc->sc_firmware;
- if (fp->datasize < sizeof *hdr) {
- device_printf(sc->sc_dev,
- "firmware image too short %zu\n", fp->datasize);
- goto fail2;
- }
-
hdr = (const struct ipw_firmware_hdr *)fp->data;
- if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) +
- le32toh(hdr->ucodesz)) {
- device_printf(sc->sc_dev,
- "firmware image too short %zu\n", fp->datasize);
- goto fail2;
- }
-
- DPRINTF(("Loading firmware image '%s'\n", imagename));
+ DPRINTF(("Loading firmware image '%s'\n", fp->name));
fw = (const char *)fp->data + sizeof *hdr + le32toh(hdr->mainsz);
if (ipw_load_ucode(sc, fw, le32toh(hdr->ucodesz)) != 0) {
device_printf(sc->sc_dev, "could not load microcode\n");
- goto fail2;
+ goto fail;
}
ipw_stop_master(sc);
@@ -2615,7 +2476,7 @@ ipw_init_locked(struct ipw_softc *sc, int force)
fw = (const char *)fp->data + sizeof *hdr;
if (ipw_load_firmware(sc, fw, le32toh(hdr->mainsz)) != 0) {
device_printf(sc->sc_dev, "could not load firmware\n");
- goto fail2;
+ goto fail;
}
sc->flags |= IPW_FLAG_FW_INITED;
@@ -2628,21 +2489,9 @@ ipw_init_locked(struct ipw_softc *sc, int force)
if (ipw_config(sc) != 0) {
device_printf(sc->sc_dev, "device configuration failed\n");
- goto fail1;
+ goto fail;
}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL || force)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
@@ -2650,13 +2499,102 @@ ipw_init_locked(struct ipw_softc *sc, int force)
sc->flags &=~ IPW_FLAG_INIT_LOCKED;
return;
-fail2: firmware_put(fp, FIRMWARE_UNLOAD);
- sc->sc_firmware = NULL;
-fail1: ifp->if_flags &= ~IFF_UP;
+fail:
ipw_stop_locked(sc);
sc->flags &=~ IPW_FLAG_INIT_LOCKED;
}
+static int
+ipw_config(struct ipw_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ipw_configuration config;
+ uint32_t data;
+ int error;
+
+ error = ipw_disable(sc);
+ if (error != 0)
+ return error;
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_WDS: /* XXX */
+ data = htole32(IPW_MODE_BSS);
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ data = htole32(IPW_MODE_IBSS);
+ break;
+ case IEEE80211_M_MONITOR:
+ data = htole32(IPW_MODE_MONITOR);
+ break;
+ }
+ DPRINTF(("Setting mode to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_MONITOR) {
+ error = ipw_setchannel(sc, ic->ic_curchan);
+ if (error != 0)
+ return error;
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ return ipw_enable(sc);
+
+ config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK |
+ IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE);
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ config.flags |= htole32(IPW_CFG_IBSS_AUTO_START);
+ if (ifp->if_flags & IFF_PROMISC)
+ config.flags |= htole32(IPW_CFG_PROMISCUOUS);
+ config.bss_chan = htole32(0x3fff); /* channels 1-14 */
+ config.ibss_chan = htole32(0x7ff); /* channels 1-11 */
+ DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags)));
+ error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config);
+ if (error != 0)
+ return error;
+
+ data = htole32(0x3); /* 1, 2 */
+ DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ /* NB: use the same rate set */
+ DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ data = htole32(0xf); /* 1, 2, 5.5, 11 */
+ DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ data = htole32(IPW_POWER_MODE_CAM);
+ DPRINTF(("Setting power mode to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ data = htole32(32); /* default value */
+ DPRINTF(("Setting tx power index to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data,
+ sizeof data);
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
+
static void
ipw_stop(void *priv)
{
@@ -2671,14 +2609,11 @@ ipw_stop(void *priv)
static void
ipw_stop_locked(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
int i;
IPW_LOCK_ASSERT(sc);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
callout_stop(&sc->sc_wdtimer);
ipw_stop_master(sc);
@@ -2795,7 +2730,7 @@ ipw_scan_start(struct ieee80211com *ic)
IPW_LOCK(sc);
if (!(sc->flags & IPW_FLAG_SCANNING))
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_scan_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_scan_task);
IPW_UNLOCK(sc);
}
@@ -2816,13 +2751,13 @@ ipw_set_channel(struct ieee80211com *ic)
}
static void
-ipw_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+ipw_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* NB: all channels are scanned at once */
}
static void
-ipw_scan_mindwell(struct ieee80211com *ic)
+ipw_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
diff --git a/sys/dev/ipw/if_ipwvar.h b/sys/dev/ipw/if_ipwvar.h
index f6ec999..de35019 100644
--- a/sys/dev/ipw/if_ipwvar.h
+++ b/sys/dev/ipw/if_ipwvar.h
@@ -76,30 +76,40 @@ struct ipw_tx_radiotap_header {
((1 << IEEE80211_RADIOTAP_FLAGS) | \
(1 << IEEE80211_RADIOTAP_CHANNEL))
+struct ipw_vap {
+ struct ieee80211vap vap;
+ struct task assoc_task;
+ struct task disassoc_task;
+ struct task assoc_success_task;
+ struct task assoc_failed_task;
+ struct task scandone_task;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define IPW_VAP(vap) ((struct ipw_vap *)(vap))
+
struct ipw_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
struct mtx sc_mtx;
struct task sc_init_task;
struct task sc_scan_task;
struct task sc_chan_task;
- struct task sc_assoc_task;
- struct task sc_disassoc_task;
+ struct task sc_bmiss_task;
struct callout sc_wdtimer; /* watchdog timer */
uint32_t flags;
-#define IPW_FLAG_FW_INITED (1 << 0)
-#define IPW_FLAG_INIT_LOCKED (1 << 1)
-#define IPW_FLAG_HAS_RADIO_SWITCH (1 << 2)
-#define IPW_FLAG_HACK (1 << 3)
-#define IPW_FLAG_SCANNING (1 << 4)
-#define IPW_FLAG_ENABLED (1 << 5)
-#define IPW_FLAG_BUSY (1 << 6)
-#define IPW_FLAG_ASSOCIATED (1 << 7)
+#define IPW_FLAG_FW_INITED 0x0001
+#define IPW_FLAG_INIT_LOCKED 0x0002
+#define IPW_FLAG_HAS_RADIO_SWITCH 0x0004
+#define IPW_FLAG_HACK 0x0008
+#define IPW_FLAG_SCANNING 0x0010
+#define IPW_FLAG_ENABLED 0x0020
+#define IPW_FLAG_BUSY 0x0040
+#define IPW_FLAG_ASSOCIATING 0x0080
+#define IPW_FLAG_ASSOCIATED 0x0100
int irq_rid;
int mem_rid;
@@ -152,22 +162,10 @@ struct ipw_softc {
uint32_t rxcur;
int txfree;
- int dwelltime;
-
- struct bpf_if *sc_drvbpf;
-
- union {
- struct ipw_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct ipw_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct ipw_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct ipw_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index 97dad68..1ddb59b 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_input.h>
#include <net80211/ieee80211_regdomain.h>
#include <netinet/in.h>
@@ -90,6 +91,14 @@ __FBSDID("$FreeBSD$");
#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0)
int iwi_debug = 0;
SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level");
+
+static const char *iwi_fw_states[] = {
+ "IDLE", /* IWI_FW_IDLE */
+ "LOADING", /* IWI_FW_LOADING */
+ "ASSOCIATING", /* IWI_FW_ASSOCIATING */
+ "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */
+ "SCANNING", /* IWI_FW_SCANNING */
+};
#else
#define DPRINTF(x)
#define DPRINTFN(n, x)
@@ -120,6 +129,11 @@ static const struct iwi_ident iwi_ident_table[] = {
{ 0, 0, NULL }
};
+static struct ieee80211vap *iwi_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void iwi_vap_delete(struct ieee80211vap *);
static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int);
static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *,
int);
@@ -135,15 +149,17 @@ static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
static struct ieee80211_node *iwi_node_alloc(struct ieee80211_node_table *);
static void iwi_node_free(struct ieee80211_node *);
-static int iwi_media_change(struct ifnet *);
static void iwi_media_status(struct ifnet *, struct ifmediareq *);
-static int iwi_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void iwi_wme_init(struct iwi_softc *);
-static int iwi_wme_setparams(struct iwi_softc *);
+static int iwi_wme_setparams(struct iwi_softc *, struct ieee80211com *);
static int iwi_wme_update(struct ieee80211com *);
static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t);
static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
struct iwi_frame *);
+static void iwi_authsuccess(void *, int);
+static void iwi_assocsuccess(void *, int);
+static void iwi_assocfailed(void *, int);
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 *, struct iwi_tx_ring *);
@@ -152,6 +168,9 @@ static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t);
static void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int);
static int iwi_tx_start(struct ifnet *, struct mbuf *,
struct ieee80211_node *, int);
+static int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void iwi_start_locked(struct ifnet *);
static void iwi_start(struct ifnet *);
static void iwi_watchdog(void *);
static int iwi_ioctl(struct ifnet *, u_long, caddr_t);
@@ -161,27 +180,27 @@ static int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *);
static int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *);
static void iwi_release_fw_dma(struct iwi_softc *sc);
static int iwi_config(struct iwi_softc *);
-static int iwi_get_firmware(struct iwi_softc *);
+static int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode);
static void iwi_put_firmware(struct iwi_softc *);
static int iwi_scanchan(struct iwi_softc *, unsigned long, int);
static void iwi_scan_start(struct ieee80211com *);
static void iwi_scan_end(struct ieee80211com *);
static void iwi_scanabort(void *, int);
static void iwi_set_channel(struct ieee80211com *);
-static void iwi_scan_curchan(struct ieee80211com *, unsigned long maxdwell);
+static void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell);
#if 0
static void iwi_scan_allchan(struct ieee80211com *, unsigned long maxdwell);
#endif
-static void iwi_scan_mindwell(struct ieee80211com *);
-static void iwi_assoc(struct ieee80211com *ic);
-static void iwi_disassoc(struct ieee80211com *);
+static void iwi_scan_mindwell(struct ieee80211_scan_state *);
static void iwi_ops(void *, int);
-static int iwi_queue_cmd(struct iwi_softc *, int);
-static int iwi_auth_and_assoc(struct iwi_softc *);
+static int iwi_queue_cmd(struct iwi_softc *, int, unsigned long);
+static int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *);
static int iwi_disassociate(struct iwi_softc *, int quiet);
+static void iwi_init_locked(struct iwi_softc *);
static void iwi_init(void *);
-static void iwi_init_locked(void *, int);
-static void iwi_stop(void *);
+static int iwi_init_fw_dma(struct iwi_softc *, int);
+static void iwi_stop_locked(void *);
+static void iwi_stop(struct iwi_softc *);
static void iwi_restart(void *, int);
static int iwi_getrfkill(struct iwi_softc *);
static void iwi_radio_on(void *, int);
@@ -256,18 +275,25 @@ iwi_attach(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
uint16_t val;
- int i, error, bands;
+ int i, error;
+ uint8_t bands;
sc->sc_dev = dev;
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(dev, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
IWI_LOCK_INIT(sc);
IWI_CMD_LOCK_INIT(sc);
sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx);
-#if __FreeBSD_version >= 700000
sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
@@ -276,22 +302,14 @@ iwi_attach(device_t dev)
taskqueue_thread_enqueue, &sc->sc_tq2);
taskqueue_start_threads(&sc->sc_tq2, 1, PI_NET, "%s taskq2",
device_get_nameunit(dev));
-#else
- sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO,
- taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc);
- kproc_create(taskqueue_thread_loop, &sc->sc_tq, &sc->sc_tqproc,
- 0, 0, "%s taskq", device_get_nameunit(dev));
- sc->sc_tq2 = taskqueue_create("iwi_taskq2", M_NOWAIT | M_ZERO,
- taskqueue_thread_enqueue, &sc->sc_tq2, &sc->sc_tqproc);
- kproc_create(taskqueue_thread_loop, &sc->sc_tq2, &sc->sc_tqproc,
- 0, 0, "%s taskq2", device_get_nameunit(dev));
-#endif
+
TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc);
TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc);
TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc);
TASK_INIT(&sc->sc_opstask, 0, iwi_ops, sc);
TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
+ callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
device_printf(dev, "chip is in D%d power mode "
@@ -354,12 +372,6 @@ iwi_attach(device_t dev)
iwi_wme_init(sc);
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- device_printf(dev, "can not if_alloc()\n");
- goto fail;
- }
- ic->ic_ifp = ifp;
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -370,10 +382,9 @@ iwi_attach(device_t dev)
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- ic->ic_wme.wme_update = iwi_wme_update;
+ ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
@@ -383,7 +394,9 @@ iwi_attach(device_t dev)
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_WPA /* 802.11i */
| IEEE80211_C_WME /* 802.11e */
+#if 0
| IEEE80211_C_BGSCAN /* capable of bg scanning */
+#endif
;
/* read MAC address from EEPROM */
@@ -402,28 +415,26 @@ iwi_attach(device_t dev)
setbit(&bands, IEEE80211_MODE_11G);
if (pci_get_device(dev) >= 0x4223)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_bmissthreshold = 10; /* override default */
/* override default methods */
ic->ic_node_alloc = iwi_node_alloc;
sc->sc_node_free = ic->ic_node_free;
ic->ic_node_free = iwi_node_free;
+ ic->ic_raw_xmit = iwi_raw_xmit;
ic->ic_scan_start = iwi_scan_start;
ic->ic_scan_end = iwi_scan_end;
ic->ic_set_channel = iwi_set_channel;
ic->ic_scan_curchan = iwi_scan_curchan;
ic->ic_scan_mindwell = iwi_scan_mindwell;
+ ic->ic_wme.wme_update = iwi_wme_update;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = iwi_newstate;
- ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
+ ic->ic_vap_create = iwi_vap_create;
+ ic->ic_vap_delete = iwi_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -450,8 +461,9 @@ iwi_attach(device_t dev)
ieee80211_announce(ic);
return 0;
-
-fail: iwi_detach(dev);
+fail:
+ /* XXX fix */
+ iwi_detach(dev);
return ENXIO;
}
@@ -459,19 +471,18 @@ static int
iwi_detach(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- IWI_LOCK_DECL;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
- if (ifp != NULL) {
- IWI_LOCK(sc);
- iwi_stop(sc);
- IWI_UNLOCK(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
- }
+ iwi_stop(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
+ /* NB: do early to drain any pending tasks */
+ taskqueue_free(sc->sc_tq);
+ taskqueue_free(sc->sc_tq2);
- callout_drain(&sc->sc_wdtimer);
iwi_put_firmware(sc);
iwi_release_fw_dma(sc);
@@ -482,29 +493,81 @@ iwi_detach(device_t dev)
iwi_free_tx_ring(sc, &sc->txq[3]);
iwi_free_rx_ring(sc, &sc->rxq);
- if (sc->irq != NULL) {
- bus_teardown_intr(dev, sc->irq, sc->sc_ih);
- bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- }
-
- if (sc->mem != NULL)
- bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
-
- if (ifp != NULL)
- if_free(ifp);
+ bus_teardown_intr(dev, sc->irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- taskqueue_free(sc->sc_tq);
- taskqueue_free(sc->sc_tq2);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
- if (sc->sc_unr != NULL)
- delete_unrhdr(sc->sc_unr);
+ delete_unrhdr(sc->sc_unr);
IWI_LOCK_DESTROY(sc);
IWI_CMD_LOCK_DESTROY(sc);
+ if_free(ifp);
+
return 0;
}
+static struct ieee80211vap *
+iwi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct iwi_softc *sc = ifp->if_softc;
+ struct iwi_vap *ivp;
+ struct ieee80211vap *vap;
+ int i;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ /*
+ * Get firmware image (and possibly dma memory) on mode change.
+ */
+ if (iwi_get_firmware(sc, opmode))
+ return NULL;
+ /* allocate DMA memory for mapping firmware image */
+ i = sc->fw_fw.size;
+ if (sc->fw_boot.size > i)
+ i = sc->fw_boot.size;
+ /* XXX do we dma the ucode as well ? */
+ if (sc->fw_uc.size > i)
+ i = sc->fw_uc.size;
+ if (iwi_init_fw_dma(sc, i))
+ return NULL;
+
+ ivp = (struct iwi_vap *) malloc(sizeof(struct iwi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (ivp == NULL)
+ return NULL;
+ vap = &ivp->iwi_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override the default, the setting comes from the linux driver */
+ vap->iv_bmissthreshold = 24;
+ /* override with driver methods */
+ ivp->iwi_newstate = vap->iv_newstate;
+ vap->iv_newstate = iwi_newstate;
+
+ TASK_INIT(&ivp->iwi_authsuccess_task, 0, iwi_authsuccess, vap);
+ TASK_INIT(&ivp->iwi_assocsuccess_task, 0, iwi_assocsuccess, vap);
+ TASK_INIT(&ivp->iwi_assocfailed_task, 0, iwi_assocfailed, vap);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+iwi_vap_delete(struct ieee80211vap *vap)
+{
+ struct iwi_vap *ivp = IWI_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(ivp, M_80211_VAP);
+}
+
static void
iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
@@ -807,11 +870,8 @@ static int
iwi_shutdown(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
iwi_stop(sc);
- IWI_UNLOCK(sc);
iwi_put_firmware(sc); /* ??? XXX */
return 0;
@@ -821,11 +881,8 @@ static int
iwi_suspend(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
iwi_stop(sc);
- IWI_UNLOCK(sc);
return 0;
}
@@ -834,20 +891,12 @@ static int
iwi_resume(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
-
- IWI_UNLOCK(sc);
+ if (ifp->if_flags & IFF_UP)
+ iwi_init(sc);
return 0;
}
@@ -882,25 +931,6 @@ iwi_node_free(struct ieee80211_node *ni)
sc->sc_node_free(ni);
}
-static int
-iwi_media_change(struct ifnet *ifp)
-{
- struct iwi_softc *sc = ifp->if_softc;
- int error;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET &&
- (ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- iwi_init_locked(sc, 0);
-
- IWI_UNLOCK(sc);
-
- return error;
-}
-
/*
* Convert h/w rate code to IEEE rate code.
*/
@@ -931,43 +961,47 @@ iwi_cvtrate(int iwirate)
static void
iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int rate;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwi_softc *sc = ic->ic_ifp->if_softc;
/* read current transmission rate from adapter */
- rate = iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
- imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode);
-
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- else if (ic->ic_opmode == IEEE80211_M_MONITOR)
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
+ vap->iv_bss->ni_txrate =
+ iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
+ ieee80211_media_status(ifp, imr);
}
static int
-iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct iwi_vap *ivp = IWI_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- int error = 0;
+ IWI_LOCK_DECL;
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
- /* XXX state change race with taskqueue */
switch (nstate) {
- case IEEE80211_S_AUTH:
- iwi_assoc(ic);
+ case IEEE80211_S_INIT:
+ IWI_LOCK(sc);
+ /*
+ * NB: don't try to do this if iwi_stop_master has
+ * shutdown the firmware and disabled interrupts.
+ */
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ (sc->flags & IWI_FLAG_FW_INITED))
+ iwi_queue_cmd(sc, IWI_DISASSOC, 1);
+ IWI_UNLOCK(sc);
break;
+ case IEEE80211_S_AUTH:
+ iwi_queue_cmd(sc, IWI_AUTH, arg);
+ return EINPROGRESS;
case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ vap->iv_state == IEEE80211_S_SCAN) {
/*
* XXX when joining an ibss network we are called
* with a SCAN -> RUN transition on scan complete.
@@ -976,35 +1010,24 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
- if (ic->ic_state == IEEE80211_S_SCAN)
- iwi_assoc(ic);
- }
- break;
- case IEEE80211_S_INIT:
- /*
- * NB: don't try to do this if iwi_stop_master has
- * shutdown the firmware and disabled interrupts.
- */
- if (ic->ic_state == IEEE80211_S_RUN &&
- (sc->flags & IWI_FLAG_FW_INITED))
- iwi_disassoc(ic);
- if (ic->ic_state == IEEE80211_S_SCAN &&
- (sc->fw_state == IWI_FW_SCANNING))
- ieee80211_cancel_scan(ic);
+ iwi_queue_cmd(sc, IWI_ASSOC, 0);
+ return EINPROGRESS;
+ }
break;
case IEEE80211_S_ASSOC:
/*
- * If we are not transitioning from AUTH the resend the
- * association request.
+ * If we are transitioning from AUTH then just wait
+ * for the ASSOC status to come back from the firmware.
+ * Otherwise we need to issue the association request.
*/
- if (ic->ic_state != IEEE80211_S_AUTH)
- iwi_assoc(ic);
- break;
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ break;
+ iwi_queue_cmd(sc, IWI_ASSOC, arg);
+ return EINPROGRESS;
default:
break;
}
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
-
+ return ivp->iwi_newstate(vap, nstate, arg);
}
/*
@@ -1055,9 +1078,8 @@ iwi_wme_init(struct iwi_softc *sc)
}
static int
-iwi_wme_setparams(struct iwi_softc *sc)
+iwi_wme_setparams(struct iwi_softc *sc, struct ieee80211com *ic)
{
- struct ieee80211com *ic = &sc->sc_ic;
const struct wmeParams *wmep;
int ac;
@@ -1090,7 +1112,7 @@ iwi_wme_update(struct ieee80211com *ic)
* will get sent down to the adapter as part of the
* work iwi_auth_and_assoc does.
*/
- return (iwi_queue_cmd(sc, IWI_SET_WME));
+ return iwi_queue_cmd(sc, IWI_SET_WME, 0);
}
static int
@@ -1171,7 +1193,8 @@ iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
static void
iwi_setcurchan(struct iwi_softc *sc, int chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
sc->curchan = chan;
@@ -1185,8 +1208,8 @@ static void
iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
struct iwi_frame *frame)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct mbuf *mnew, *m;
struct ieee80211_node *ni;
int type, error, framelen;
@@ -1261,7 +1284,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1269,17 +1292,16 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
tap->wr_antsignal = frame->signal;
tap->wr_antenna = frame->antenna;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
IWI_UNLOCK(sc);
ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
-
- /* send the frame to the 802.11 layer */
- type = ieee80211_input(ic, m, ni, frame->rssi_dbm, 0, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ if (ni != NULL) {
+ type = ieee80211_input(ni, m, frame->rssi_dbm, 0, 0);
+ ieee80211_free_node(ni);
+ } else
+ type = ieee80211_input_all(ic, m, frame->rssi_dbm, 0, 0);
IWI_LOCK(sc);
if (sc->sc_softled) {
@@ -1297,30 +1319,6 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
}
}
-/* unaligned little endian access */
-#define LE_READ_2(p) \
- ((u_int16_t) \
- ((((const u_int8_t *)(p))[0] ) | \
- (((const u_int8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((u_int32_t) \
- ((((const u_int8_t *)(p))[0] ) | \
- (((const u_int8_t *)(p))[1] << 8) | \
- (((const u_int8_t *)(p))[2] << 16) | \
- (((const u_int8_t *)(p))[3] << 24)))
-
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
- if ((_len) < (_minlen)) { \
- return; \
- } \
-} while (0)
-
-static int __inline
-iswmeoui(const u_int8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
-}
-
/*
* Check for an association response frame to see if QoS
* has been negotiated. We parse just enough to figure
@@ -1330,7 +1328,8 @@ iswmeoui(const u_int8_t *frm)
* done in the driver.
*/
static void
-iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
+iwi_checkforqos(struct ieee80211vap *vap,
+ const struct ieee80211_frame *wh, int len)
{
#define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
const uint8_t *frm, *efrm, *wme;
@@ -1362,7 +1361,7 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
wme = NULL;
while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], return);
switch (*frm) {
case IEEE80211_ELEMID_VENDOR:
if (iswmeoui(frm))
@@ -1372,7 +1371,7 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
frm += frm[1] + 2;
}
- ni = sc->sc_ic.ic_bss;
+ ni = vap->iv_bss;
ni->ni_capinfo = capinfo;
ni->ni_associd = associd;
if (wme != NULL)
@@ -1382,10 +1381,40 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
#undef SUBTYPE
}
+/*
+ * Task queue callbacks for iwi_notification_intr used to avoid LOR's.
+ */
+
+static void
+iwi_authsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1);
+}
+
+static void
+iwi_assocsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
+}
+
+static void
+iwi_assocfailed(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+}
+
static void
iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct iwi_notif_scan_channel *chan;
struct iwi_notif_scan_complete *scan;
struct iwi_notif_authentication *auth;
@@ -1411,85 +1440,95 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
IWI_STATE_END(sc, IWI_FW_SCANNING);
- if (scan->status == IWI_SCAN_COMPLETED)
- ieee80211_scan_next(ic);
-
+ if (scan->status == IWI_SCAN_COMPLETED) {
+ /* NB: don't need to defer, net80211 does it for us */
+ ieee80211_scan_next(vap);
+ }
break;
case IWI_NOTIF_TYPE_AUTHENTICATION:
auth = (struct iwi_notif_authentication *)(notif + 1);
-
switch (auth->state) {
case IWI_AUTH_SUCCESS:
DPRINTFN(2, ("Authentication succeeeded\n"));
- ieee80211_node_authorize(ic->ic_bss);
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_authsuccess_task);
break;
-
case IWI_AUTH_FAIL:
- DPRINTFN(2, ("Authentication failed\n"));
+ /*
+ * These are delivered as an unsolicited deauth
+ * (e.g. due to inactivity) or in response to an
+ * associate request.
+ */
sc->flags &= ~IWI_FLAG_ASSOCIATED;
- IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- /* XXX */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ DPRINTFN(2, ("Authentication failed\n"));
+ vap->iv_stats.is_rx_auth_fail++;
+ IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
+ } else {
+ DPRINTFN(2, ("Deauthenticated\n"));
+ vap->iv_stats.is_rx_deauth++;
+ }
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
break;
-
case IWI_AUTH_SENT_1:
case IWI_AUTH_RECV_2:
case IWI_AUTH_SEQ1_PASS:
break;
-
case IWI_AUTH_SEQ1_FAIL:
DPRINTFN(2, ("Initial authentication handshake failed; "
"you probably need shared key\n"));
+ vap->iv_stats.is_rx_auth_fail++;
IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
/* XXX retry shared key when in auto */
break;
-
default:
device_printf(sc->sc_dev,
"unknown authentication state %u\n", auth->state);
+ break;
}
break;
case IWI_NOTIF_TYPE_ASSOCIATION:
assoc = (struct iwi_notif_association *)(notif + 1);
-
switch (assoc->state) {
case IWI_AUTH_SUCCESS:
/* re-association, do nothing */
break;
-
case IWI_ASSOC_SUCCESS:
DPRINTFN(2, ("Association succeeded\n"));
sc->flags |= IWI_FLAG_ASSOCIATED;
IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- iwi_checkforqos(sc,
+ iwi_checkforqos(vap,
(const struct ieee80211_frame *)(assoc+1),
le16toh(notif->len) - sizeof(*assoc));
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocsuccess_task);
break;
-
case IWI_ASSOC_INIT:
+ sc->flags &= ~IWI_FLAG_ASSOCIATED;
switch (sc->fw_state) {
- case IWI_FW_ASSOCIATING:
- DPRINTFN(2, ("Association failed\n"));
- IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- ieee80211_new_state(ic,
- IEEE80211_S_SCAN, -1);
- break;
+ case IWI_FW_ASSOCIATING:
+ DPRINTFN(2, ("Association failed\n"));
+ IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
+ break;
- case IWI_FW_DISASSOCIATING:
- DPRINTFN(2, ("Dissassociated\n"));
- IWI_STATE_END(sc,
- IWI_FW_DISASSOCIATING);
- break;
+ case IWI_FW_DISASSOCIATING:
+ DPRINTFN(2, ("Dissassociated\n"));
+ IWI_STATE_END(sc, IWI_FW_DISASSOCIATING);
+ vap->iv_stats.is_rx_disassoc++;
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
+ break;
}
- sc->flags &= ~IWI_FLAG_ASSOCIATED;
break;
-
default:
device_printf(sc->sc_dev,
"unknown association state %u\n", assoc->state);
+ break;
}
break;
@@ -1508,11 +1547,20 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
* 802.11 layer.
* XXX try to roam, drop assoc only on much higher count
*/
- if (le32toh(beacon->number) >= ic->ic_bmissthreshold) {
+ if (le32toh(beacon->number) >= vap->iv_bmissthreshold) {
DPRINTF(("Beacon miss: %u >= %u\n",
le32toh(beacon->number),
- ic->ic_bmissthreshold));
- ieee80211_beacon_miss(ic);
+ vap->iv_bmissthreshold));
+ vap->iv_stats.is_beacon_miss++;
+ /*
+ * It's pointless to notify the 802.11 layer
+ * as it'll try to send a probe request (which
+ * we'll discard) and then timeout and drop us
+ * into scan state. Instead tell the firmware
+ * to disassociate and then on completion we'll
+ * kick the state machine to scan.
+ */
+ iwi_queue_cmd(sc, IWI_DISASSOC, 1);
}
}
break;
@@ -1526,6 +1574,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
default:
DPRINTF(("unknown notification type %u flags 0x%x len %u\n",
notif->type, notif->flags, le16toh(notif->len)));
+ break;
}
}
@@ -1575,8 +1624,7 @@ iwi_rx_intr(struct iwi_softc *sc)
static void
iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct iwi_tx_data *data;
uint32_t hw;
@@ -1609,7 +1657,7 @@ iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
if (sc->sc_softled)
iwi_led_event(sc, IWI_LED_TX);
- iwi_start(ifp);
+ iwi_start_locked(ifp);
}
static void
@@ -1631,9 +1679,7 @@ iwi_intr(void *arg)
if (r & IWI_INTR_FATAL_ERROR) {
device_printf(sc->sc_dev, "firmware error\n");
- /* don't restart if the interface isn't up */
- if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
- taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
+ taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
sc->flags &= ~IWI_FLAG_BUSY;
sc->sc_busy_timer = 0;
@@ -1734,7 +1780,8 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
int ac)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct iwi_node *in = (struct iwi_node *)ni;
const struct ieee80211_frame *wh;
struct ieee80211_key *k;
@@ -1756,7 +1803,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
if (!ismcast)
flags |= IWI_DATA_FLAG_NEED_ACK;
- if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (vap->iv_flags & IEEE80211_F_SHPREAMBLE)
flags |= IWI_DATA_FLAG_SHPREAMBLE;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
xflags |= IWI_DATA_XFLAG_QOS;
@@ -1769,7 +1816,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
* This is only used in IBSS mode where the firmware expect an index
* in a h/w table instead of a destination address.
*/
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if (!ismcast) {
if (in->in_station == -1) {
in->in_station = alloc_unr(sc->sc_unr);
@@ -1803,7 +1850,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
staid = 0;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1813,12 +1860,12 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data = &txq->data[txq->cur];
@@ -1868,8 +1915,8 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
desc->xflags = xflags;
#if 0
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
- desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
+ desc->wep_txkey = vap->iv_def_txkey;
else
#endif
desc->flags |= IWI_DATA_FLAG_NO_WEP;
@@ -1893,99 +1940,70 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
return 0;
}
+static int
+iwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
-iwi_start(struct ifnet *ifp)
+iwi_start_locked(struct ifnet *ifp)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
int ac;
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
+ IWI_LOCK_ASSERT(sc);
- if (ic->ic_state != IEEE80211_S_RUN) {
- IWI_UNLOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
- }
for (;;) {
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- if (m0 == NULL) {
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
-
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- /* XXX does not belong here */
- /* no QoS encapsulation for EAPOL frames */
- ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
- M_WME_GETAC(m0) : WME_AC_BE;
-
- if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
+ /* there is no place left in this ring; tail drop */
+ /* XXX tail drop */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
- BPF_MTAP(ifp, m0);
+ BPF_MTAP(ifp, m);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
- } else {
- ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
- /* XXX no way to send mgt frames (yet), discard */
- m_freem(m0);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
ieee80211_free_node(ni);
+ ifp->if_oerrors++;
continue;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (iwi_tx_start(ifp, m0, ni, ac) != 0) {
+ if (iwi_tx_start(ifp, m, ni, ac) != 0) {
ieee80211_free_node(ni);
ifp->if_oerrors++;
break;
}
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
+
+static void
+iwi_start(struct ifnet *ifp)
+{
+ struct iwi_softc *sc = ifp->if_softc;
+ IWI_LOCK_DECL;
+ IWI_LOCK(sc);
+ iwi_start_locked(ifp);
IWI_UNLOCK(sc);
}
@@ -2004,26 +2022,15 @@ iwi_watchdog(void *arg)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
}
}
- if (sc->sc_rfkill_timer > 0) {
- if (--sc->sc_rfkill_timer == 0) {
- /*
- * Check for a change in rfkill state. We get an
- * interrupt when a radio is disabled but not when
- * it is enabled so we must poll for the latter.
- */
- if (!iwi_getrfkill(sc))
- taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
- else
- sc->sc_rfkill_timer = 2;
- }
- }
if (sc->sc_state_timer > 0) {
if (--sc->sc_state_timer == 0) {
if_printf(ifp, "firmware stuck in state %d, resetting\n",
sc->fw_state);
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
- if (sc->fw_state == IWI_FW_SCANNING)
- ieee80211_cancel_scan(&sc->sc_ic);
+ if (sc->fw_state == IWI_FW_SCANNING) {
+ struct ieee80211com *ic = ifp->if_l2com;
+ ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps));
+ }
sc->sc_state_timer = 3;
}
}
@@ -2033,61 +2040,43 @@ iwi_watchdog(void *arg)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
}
}
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
+ callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
}
static int
iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
IWI_LOCK_DECL;
IWI_LOCK(sc);
-
- /*
- * wait until pending iwi_cmd() are completed, to avoid races
- * that could cause problems.
- */
- while (sc->flags & IWI_FLAG_BUSY)
- msleep(sc, &sc->sc_mtx, 0, "iwiioctl", hz);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- iwi_init_locked(sc, 0);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ iwi_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- iwi_stop(sc);
- else {
- /*
- * If device was stopped due to rfkill then
- * marked down we'll have the polling thread
- * running; stop it explicitly.
- */
- sc->sc_rfkill_timer = 0;
- }
+ iwi_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- iwi_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
IWI_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2221,30 +2210,26 @@ iwi_getfw(struct iwi_fw *fw, const char *fwname,
* the boot firmware as "master".
*/
static int
-iwi_get_firmware(struct iwi_softc *sc)
+iwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode)
{
- struct ieee80211com *ic = &sc->sc_ic;
const struct iwi_firmware_hdr *hdr;
const struct firmware *fp;
/* invalidate cached firmware on mode change */
- if (sc->fw_mode != ic->ic_opmode)
+ if (sc->fw_mode != opmode)
iwi_put_firmware(sc);
- switch (ic->ic_opmode) {
+ switch (opmode) {
case IEEE80211_M_STA:
iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss");
break;
-
case IEEE80211_M_IBSS:
iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss");
break;
-
case IEEE80211_M_MONITOR:
iwi_getfw(&sc->fw_fw, "iwi_monitor",
&sc->fw_uc, "iwi_ucode_monitor");
break;
-
default:
break;
}
@@ -2324,7 +2309,7 @@ iwi_get_firmware(struct iwi_softc *sc)
sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size);
#endif
- sc->fw_mode = ic->ic_opmode;
+ sc->fw_mode = opmode;
return 0;
bad:
iwi_put_firmware(sc);
@@ -2437,6 +2422,7 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
int ntries, error;
IWI_LOCK_ASSERT(sc);
+
/* copy firmware image to DMA memory */
memcpy(sc->fw_virtaddr, fw->data, fw->size);
@@ -2527,12 +2513,11 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
}
static int
-iwi_setpowermode(struct iwi_softc *sc)
+iwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
uint32_t data;
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
/* XXX set more fine-grained operation */
data = htole32(IWI_POWER_MODE_MAX);
} else
@@ -2543,15 +2528,14 @@ iwi_setpowermode(struct iwi_softc *sc)
}
static int
-iwi_setwepkeys(struct iwi_softc *sc)
+iwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct iwi_wep_key wepkey;
struct ieee80211_key *wk;
int error, i;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- wk = &ic->ic_crypto.cs_nw_keys[i];
+ wk = &vap->iv_nw_keys[i];
wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
wepkey.idx = i;
@@ -2571,13 +2555,14 @@ iwi_setwepkeys(struct iwi_softc *sc)
static int
iwi_config(struct iwi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct iwi_configuration config;
struct iwi_rateset rs;
struct iwi_txpower power;
uint32_t data;
int error, i;
+
IWI_LOCK_ASSERT(sc);
IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
@@ -2599,23 +2584,6 @@ iwi_config(struct iwi_softc *sc)
error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
if (error != 0)
return error;
-
- error = iwi_setpowermode(sc);
- 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);
- if (error != 0)
- return error;
-
- data = htole32(ic->ic_fragthreshold);
- DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
- error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
if (ic->ic_opmode == IEEE80211_M_IBSS) {
power.mode = IWI_MODE_11B;
power.nchan = 11;
@@ -2657,32 +2625,12 @@ iwi_config(struct iwi_softc *sc)
if (error != 0)
return error;
- /* if we have a desired ESSID, set it now */
- if (ic->ic_des_ssid[0].len != 0) {
-#ifdef IWI_DEBUG
- if (iwi_debug > 0) {
- printf("Setting desired ESSID to ");
- ieee80211_print_essid(ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- printf("\n");
- }
-#endif
- error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- 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);
if (error != 0)
return error;
- error = iwi_setwepkeys(sc);
- if (error != 0)
- return error;
-
/* enable adapter */
DPRINTF(("Enabling adapter\n"));
return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0);
@@ -2740,7 +2688,7 @@ iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode)
}
IWI_STATE_BEGIN(sc, IWI_FW_SCANNING);
- ic = &sc->sc_ic;
+ ic = sc->sc_ifp->if_l2com;
ss = ic->ic_scan;
memset(&scan, 0, sizeof scan);
@@ -2864,15 +2812,16 @@ iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm)
}
static int
-iwi_auth_and_assoc(struct iwi_softc *sc)
+iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_node *ni = vap->iv_bss;
struct iwi_configuration config;
struct iwi_associate *assoc = &sc->assoc;
struct iwi_rateset rs;
uint16_t capinfo;
+ uint32_t data;
int error, mode;
IWI_LOCK_ASSERT(sc);
@@ -2901,7 +2850,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
if (mode == IWI_MODE_11G)
config.use_protection = 1;
config.answer_pbreq =
- (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
+ (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0;
config.disable_unicast_decryption = 1;
config.disable_multicast_decryption = 1;
DPRINTF(("Configuring adapter\n"));
@@ -2921,6 +2870,22 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
if (error != 0)
goto done;
+ error = iwi_setpowermode(sc, vap);
+ if (error != 0)
+ goto done;
+
+ data = htole32(vap->iv_rtsthreshold);
+ DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
+ error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
+ if (error != 0)
+ goto done;
+
+ data = htole32(vap->iv_fragthreshold);
+ DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
+ error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
+ if (error != 0)
+ goto done;
+
/* the rate set has already been "negotiated" */
memset(&rs, 0, sizeof rs);
rs.mode = mode;
@@ -2939,22 +2904,23 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
memset(assoc, 0, sizeof *assoc);
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
+ if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) {
/* NB: don't treat WME setup as failure */
- if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0)
+ if (iwi_wme_setparams(sc, ic) == 0 && iwi_wme_setie(sc) == 0)
assoc->policy |= htole16(IWI_POLICY_WME);
/* XXX complain on failure? */
}
- if (ic->ic_opt_ie != NULL) {
- DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
- error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
- ic->ic_opt_ie_len);
+ if (vap->iv_appie_wpa != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_wpa;
+
+ DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len));
+ error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len);
if (error != 0)
goto done;
}
- error = iwi_set_sensitivity(sc, ni->ni_rssi);
+ error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni));
if (error != 0)
goto done;
@@ -2964,7 +2930,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
* NB: do not arrange for shared key auth w/o privacy
* (i.e. a wep key); it causes a firmware error.
*/
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) &&
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) &&
ni->ni_authmode == IEEE80211_AUTH_SHARED) {
assoc->auth = IWI_AUTH_SHARED;
/*
@@ -2973,26 +2939,26 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
* but if we blindly grab the key the firmware will
* barf so avoid it for now.
*/
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE)
- assoc->auth |= ic->ic_crypto.cs_def_txkey << 4;
+ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE)
+ assoc->auth |= vap->iv_def_txkey << 4;
- error = iwi_setwepkeys(sc);
+ error = iwi_setwepkeys(sc, vap);
if (error != 0)
goto done;
}
- if (ic->ic_flags & IEEE80211_F_WPA)
+ if (vap->iv_flags & IEEE80211_F_WPA)
assoc->policy |= htole16(IWI_POLICY_WPA);
- if (ic->ic_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
+ if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
assoc->type = IWI_HC_IBSS_START;
else
assoc->type = IWI_HC_ASSOC;
memcpy(assoc->tstamp, ni->ni_tstamp.data, 8);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
@@ -3004,7 +2970,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
assoc->lintval = htole16(ic->ic_lintval);
assoc->intval = htole16(ni->ni_intval);
IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr);
else
IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid);
@@ -3046,17 +3012,6 @@ iwi_disassociate(struct iwi_softc *sc, int quiet)
return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
}
-static void
-iwi_init(void *priv)
-{
- struct iwi_softc *sc = priv;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
- iwi_init_locked(sc, 0);
- IWI_UNLOCK(sc);
-}
-
/*
* release dma resources for the firmware
*/
@@ -3118,54 +3073,35 @@ error:
}
static void
-iwi_init_locked(void *priv, int force)
+iwi_init_locked(struct iwi_softc *sc)
{
- struct iwi_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct iwi_rx_data *data;
int i;
- IWI_LOCK_DECL;
IWI_LOCK_ASSERT(sc);
+
if (sc->fw_state == IWI_FW_LOADING) {
device_printf(sc->sc_dev, "%s: already loading\n", __func__);
return; /* XXX: condvar? */
}
- iwi_stop(sc);
+ iwi_stop_locked(sc);
+
IWI_STATE_BEGIN(sc, IWI_FW_LOADING);
+ taskqueue_unblock(sc->sc_tq);
+ taskqueue_unblock(sc->sc_tq2);
+
if (iwi_reset(sc) != 0) {
device_printf(sc->sc_dev, "could not reset adapter\n");
goto fail;
}
-
- IWI_UNLOCK(sc);
- if (iwi_get_firmware(sc)) {
- IWI_LOCK(sc);
- goto fail;
- }
-
- /* allocate DMA memory for mapping firmware image */
- i = sc->fw_fw.size;
- if (sc->fw_boot.size > i)
- i = sc->fw_boot.size;
- /* XXX do we dma the ucode as well ? */
- if (sc->fw_uc.size > i)
- i = sc->fw_uc.size;
- if (iwi_init_fw_dma(sc, i)) {
- IWI_LOCK(sc);
- goto fail;
- }
- IWI_LOCK(sc);
-
if (iwi_load_firmware(sc, &sc->fw_boot) != 0) {
device_printf(sc->sc_dev,
"could not load boot firmware %s\n", sc->fw_boot.name);
goto fail;
}
-
if (iwi_load_ucode(sc, &sc->fw_uc) != 0) {
device_printf(sc->sc_dev,
"could not load microcode %s\n", sc->fw_uc.name);
@@ -3208,50 +3144,58 @@ iwi_init_locked(void *priv, int force)
}
sc->flags |= IWI_FLAG_FW_INITED;
+ IWI_STATE_END(sc, IWI_FW_LOADING);
+
if (iwi_config(sc) != 0) {
- device_printf(sc->sc_dev, "device configuration failed\n");
- goto fail;
+ device_printf(sc->sc_dev, "unable to enable adapter\n");
+ goto fail2;
}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL || force)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- IWI_STATE_END(sc, IWI_FW_LOADING);
return;
-
-fail: ifp->if_flags &= ~IFF_UP;
+fail:
IWI_STATE_END(sc, IWI_FW_LOADING);
- iwi_stop(sc);
- iwi_put_firmware(sc);
+fail2:
+ iwi_stop_locked(sc);
}
static void
-iwi_stop(void *priv)
+iwi_init(void *priv)
{
struct iwi_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ IWI_LOCK_DECL;
+
+ IWI_LOCK(sc);
+ iwi_init_locked(sc);
+ IWI_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic);
+}
+
+static void
+iwi_stop_locked(void *priv)
+{
+ struct iwi_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
IWI_LOCK_ASSERT(sc);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ taskqueue_block(sc->sc_tq);
+ taskqueue_block(sc->sc_tq2);
if (sc->sc_softled) {
callout_stop(&sc->sc_ledtimer);
sc->sc_blinking = 0;
}
-
callout_stop(&sc->sc_wdtimer);
+ callout_stop(&sc->sc_rftimer);
+
iwi_stop_master(sc);
CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET);
@@ -3264,31 +3208,33 @@ iwi_stop(void *priv)
iwi_reset_tx_ring(sc, &sc->txq[3]);
iwi_reset_rx_ring(sc, &sc->rxq);
- ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
-
memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd));
sc->sc_tx_timer = 0;
- sc->sc_rfkill_timer = 0;
sc->sc_state_timer = 0;
sc->sc_busy_timer = 0;
sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED);
sc->fw_state = IWI_FW_IDLE;
wakeup(sc);
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
static void
-iwi_restart(void *arg, int npending)
+iwi_stop(struct iwi_softc *sc)
{
- struct iwi_softc *sc = arg;
IWI_LOCK_DECL;
IWI_LOCK(sc);
- iwi_init_locked(sc, 1); /* NB: force state machine */
+ iwi_stop_locked(sc);
IWI_UNLOCK(sc);
}
+static void
+iwi_restart(void *arg, int npending)
+{
+ struct iwi_softc *sc = arg;
+
+ iwi_init(sc);
+}
+
/*
* Return whether or not the radio is enabled in hardware
* (i.e. the rfkill switch is "off").
@@ -3303,21 +3249,48 @@ static void
iwi_radio_on(void *arg, int pending)
{
struct iwi_softc *sc = arg;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
device_printf(sc->sc_dev, "radio turned on\n");
+
iwi_init(sc);
+ ieee80211_notify_radio(ic, 1);
+}
+
+static void
+iwi_rfkill_poll(void *arg)
+{
+ struct iwi_softc *sc = arg;
+
+ IWI_LOCK_ASSERT(sc);
+
+ /*
+ * Check for a change in rfkill state. We get an
+ * interrupt when a radio is disabled but not when
+ * it is enabled so we must poll for the latter.
+ */
+ if (!iwi_getrfkill(sc)) {
+ taskqueue_unblock(sc->sc_tq);
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
+ return;
+ }
+ callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc);
}
static void
iwi_radio_off(void *arg, int pending)
{
struct iwi_softc *sc = arg;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
IWI_LOCK_DECL;
device_printf(sc->sc_dev, "radio turned off\n");
+
+ ieee80211_notify_radio(ic, 0);
+
IWI_LOCK(sc);
- iwi_stop(sc);
- sc->sc_rfkill_timer = 2;
+ iwi_stop_locked(sc);
+ iwi_rfkill_poll(sc);
IWI_UNLOCK(sc);
}
@@ -3564,22 +3537,37 @@ iwi_ledattach(struct iwi_softc *sc)
}
static void
-iwi_ops(void *arg, int npending)
-{
- struct iwi_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+iwi_ops(void *arg0, int npending)
+{
+ static const char *opnames[] = {
+ [IWI_CMD_FREE] = "FREE",
+ [IWI_SCAN_START] = "SCAN_START",
+ [IWI_SET_CHANNEL] = "SET_CHANNEL",
+ [IWI_AUTH] = "AUTH",
+ [IWI_ASSOC] = "ASSOC",
+ [IWI_DISASSOC] = "DISASSOC",
+ [IWI_SCAN_CURCHAN] = "SCAN_CURCHAN",
+ [IWI_SCAN_ALLCHAN] = "SCAN_ALLCHAN",
+ [IWI_SET_WME] = "SET_WME",
+ };
+ struct iwi_softc *sc = arg0;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
IWI_LOCK_DECL;
int cmd;
+ unsigned long arg;
again:
IWI_CMD_LOCK(sc);
cmd = sc->sc_cmd[sc->sc_cmd_cur];
- if (cmd == 0) {
+ if (cmd == IWI_CMD_FREE) {
/* No more commands to process */
IWI_CMD_UNLOCK(sc);
return;
}
- sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */
+ arg = sc->sc_arg[sc->sc_cmd_cur];
+ sc->sc_cmd[sc->sc_cmd_cur] = IWI_CMD_FREE; /* free the slot */
sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWI_CMD_MAXOPS;
IWI_CMD_UNLOCK(sc);
@@ -3588,21 +3576,28 @@ again:
msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz/10);
}
- if (!(sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
IWI_UNLOCK(sc);
return;
}
+ DPRINTF(("%s: %s arg %lu\n", __func__, opnames[cmd], arg));
switch (cmd) {
+ case IWI_AUTH:
case IWI_ASSOC:
- iwi_auth_and_assoc(sc);
+ if (cmd == IWI_AUTH)
+ vap->iv_state = IEEE80211_S_AUTH;
+ else
+ vap->iv_state = IEEE80211_S_ASSOC;
+ iwi_auth_and_assoc(sc, vap);
+ /* NB: completion done in iwi_notification_intr */
break;
case IWI_DISASSOC:
iwi_disassociate(sc, 0);
break;
case IWI_SET_WME:
- if (ic->ic_state == IEEE80211_S_RUN)
- (void) iwi_wme_setparams(sc);
+ if (vap->iv_state == IEEE80211_S_RUN)
+ (void) iwi_wme_setparams(sc, ic);
break;
case IWI_SCAN_START:
sc->flags |= IWI_FLAG_CHANNEL_SCAN;
@@ -3614,9 +3609,8 @@ again:
__func__));
goto done;
}
- if (iwi_scanchan(sc, sc->sc_maxdwell, cmd))
- ieee80211_cancel_scan(ic);
-
+ if (iwi_scanchan(sc, arg, cmd))
+ ieee80211_cancel_scan(vap);
break;
}
done:
@@ -3627,7 +3621,7 @@ done:
}
static int
-iwi_queue_cmd(struct iwi_softc *sc, int cmd)
+iwi_queue_cmd(struct iwi_softc *sc, int cmd, unsigned long arg)
{
IWI_CMD_LOCK(sc);
if (sc->sc_cmd[sc->sc_cmd_next] != 0) {
@@ -3637,6 +3631,7 @@ iwi_queue_cmd(struct iwi_softc *sc, int cmd)
}
sc->sc_cmd[sc->sc_cmd_next] = cmd;
+ sc->sc_arg[sc->sc_cmd_next] = arg;
sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWI_CMD_MAXOPS;
taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask);
IWI_CMD_UNLOCK(sc);
@@ -3649,7 +3644,7 @@ iwi_scan_start(struct ieee80211com *ic)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- iwi_queue_cmd(sc, IWI_SCAN_START);
+ iwi_queue_cmd(sc, IWI_SCAN_START, 0);
}
static void
@@ -3662,13 +3657,13 @@ iwi_set_channel(struct ieee80211com *ic)
}
static void
-iwi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- sc->sc_maxdwell = maxdwell;
- iwi_queue_cmd(sc, IWI_SCAN_CURCHAN);
+ iwi_queue_cmd(sc, IWI_SCAN_CURCHAN, maxdwell);
}
#if 0
@@ -3678,13 +3673,12 @@ iwi_scan_allchan(struct ieee80211com *ic, unsigned long maxdwell)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- sc->sc_maxdwell = maxdwell;
- iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN);
+ iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN, maxdwell);
}
#endif
static void
-iwi_scan_mindwell(struct ieee80211com *ic)
+iwi_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
@@ -3697,25 +3691,3 @@ iwi_scan_end(struct ieee80211com *ic)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_scanaborttask);
}
-
-static void
-iwi_assoc(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct iwi_softc *sc = ifp->if_softc;
-
- /* The firmware will fail if we are already associated */
- if (sc->flags & IWI_FLAG_ASSOCIATED)
- iwi_disassoc(ic);
-
- iwi_queue_cmd(sc, IWI_ASSOC);
-}
-
-static void
-iwi_disassoc(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct iwi_softc *sc = ifp->if_softc;
-
- iwi_queue_cmd(sc, IWI_DISASSOC);
-}
diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h
index fca0b7a..abc6f9c 100644
--- a/sys/dev/iwi/if_iwivar.h
+++ b/sys/dev/iwi/if_iwivar.h
@@ -114,11 +114,19 @@ struct iwi_fw {
const char *name; /* associated image name */
};
+struct iwi_vap {
+ struct ieee80211vap iwi_vap;
+ struct task iwi_authsuccess_task;
+ struct task iwi_assocsuccess_task;
+ struct task iwi_assocfailed_task;
+
+ int (*iwi_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define IWI_VAP(vap) ((struct iwi_vap *)(vap))
+
struct iwi_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
void (*sc_node_free)(struct ieee80211_node *);
device_t sc_dev;
@@ -129,9 +137,6 @@ struct iwi_softc {
struct unrhdr *sc_unr;
struct taskqueue *sc_tq; /* private task queue */
struct taskqueue *sc_tq2; /* reset task queue */
-#if __FreeBSD_version < 700000
- struct proc *sc_tqproc;
-#endif
uint32_t flags;
#define IWI_FLAG_FW_INITED (1 << 0)
@@ -208,39 +213,31 @@ struct iwi_softc {
u_int16_t sc_ledoff; /* off time for current blink */
struct callout sc_ledtimer; /* led off timer */
struct callout sc_wdtimer; /* watchdog timer */
+ struct callout sc_rftimer; /* rfkill timer */
int sc_tx_timer;
- int sc_rfkill_timer;/* poll for rfkill change */
int sc_state_timer; /* firmware state timer */
int sc_busy_timer; /* firmware cmd timer */
-#define IWI_SCAN_START (1 << 0)
-#define IWI_SET_CHANNEL (1 << 1)
-#define IWI_SCAN_END (1 << 2)
-#define IWI_ASSOC (1 << 3)
-#define IWI_DISASSOC (1 << 4)
-#define IWI_SCAN_CURCHAN (1 << 5)
-#define IWI_SCAN_ALLCHAN (1 << 6)
-#define IWI_SET_WME (1 << 7)
#define IWI_CMD_MAXOPS 10
- int sc_cmd[IWI_CMD_MAXOPS];
- int sc_cmd_cur; /* current queued scan task */
- int sc_cmd_next; /* last queued scan task */
- unsigned long sc_maxdwell; /* max dwell time for curchan */
- struct bpf_if *sc_drvbpf;
+ int sc_cmd[IWI_CMD_MAXOPS];
+ unsigned long sc_arg[IWI_CMD_MAXOPS];
+ int sc_cmd_cur; /* current queued scan task */
+ int sc_cmd_next; /* last queued scan task */
+#define IWI_CMD_FREE 0 /* for marking slots unused */
+#define IWI_SCAN_START 1
+#define IWI_SET_CHANNEL 2
+#define IWI_AUTH 3
+#define IWI_ASSOC 4
+#define IWI_DISASSOC 5
+#define IWI_SCAN_CURCHAN 6
+#define IWI_SCAN_ALLCHAN 7
+#define IWI_SET_WME 8
- union {
- struct iwi_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct iwi_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct iwi_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct iwi_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
@@ -249,15 +246,15 @@ struct iwi_softc {
("iwi firmware not idle")); \
_sc->fw_state = _state; \
_sc->sc_state_timer = 5; \
- DPRINTF(("enter FW state %d\n", _state)); \
+ DPRINTF(("enter %s state\n", iwi_fw_states[_state])); \
} while (0)
#define IWI_STATE_END(_sc, _state) do { \
if (_sc->fw_state == _state) \
- DPRINTF(("exit FW state %d\n", _state)); \
+ DPRINTF(("exit %s state\n", iwi_fw_states[_state])); \
else \
- DPRINTF(("expected FW state %d, got %d\n", \
- _state, _sc->fw_state)); \
+ DPRINTF(("expected %s state, got %s\n", \
+ iwi_fw_states[_state], iwi_fw_states[_sc->fw_state])); \
_sc->fw_state = IWI_FW_IDLE; \
wakeup(_sc); \
_sc->sc_state_timer = 0; \
diff --git a/sys/dev/malo/if_malo.c b/sys/dev/malo/if_malo.c
index a1a6285..8e70f40 100644
--- a/sys/dev/malo/if_malo.c
+++ b/sys/dev/malo/if_malo.c
@@ -34,6 +34,8 @@
__FBSDID("$FreeBSD$");
#endif
+#include "opt_malo.h"
+
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/kernel.h>
@@ -123,6 +125,11 @@ enum {
MALLOC_DEFINE(M_MALODEV, "malodev", "malo driver dma buffers");
+static struct ieee80211vap *malo_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void malo_vap_delete(struct ieee80211vap *);
static int malo_dma_setup(struct malo_softc *);
static int malo_setup_hwdma(struct malo_softc *);
static void malo_txq_init(struct malo_softc *, struct malo_txq *, int);
@@ -131,13 +138,12 @@ static void malo_start(struct ifnet *);
static void malo_watchdog(struct ifnet *);
static int malo_ioctl(struct ifnet *, u_long, caddr_t);
static void malo_updateslot(struct ifnet *);
-static int malo_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int malo_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void malo_scan_start(struct ieee80211com *);
static void malo_scan_end(struct ieee80211com *);
static void malo_set_channel(struct ieee80211com *);
static int malo_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
-static int malo_media_change(struct ifnet *);
static void malo_bpfattach(struct malo_softc *);
static void malo_sysctlattach(struct malo_softc *);
static void malo_announce(struct malo_softc *);
@@ -163,7 +169,7 @@ malo_bar0_read4(struct malo_softc *sc, bus_size_t off)
static void
malo_bar0_write4(struct malo_softc *sc, bus_size_t off, uint32_t val)
{
- DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%x val 0x%x\n",
+ DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%zx val 0x%x\n",
__func__, off, val);
bus_space_write_4(sc->malo_io0t, sc->malo_io0h, off, val);
@@ -178,17 +184,18 @@ malo_bar1_read1(struct malo_softc *sc, bus_size_t off)
int
malo_attach(uint16_t devid, struct malo_softc *sc)
{
- int error, i;
- struct ieee80211com *ic = &sc->malo_ic;
+ int error;
+ struct ieee80211com *ic;
struct ifnet *ifp;
struct malo_hal *mh;
uint8_t bands;
- ifp = sc->malo_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->malo_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->malo_dev, "can not if_alloc()\n");
return ENOSPC;
}
+ ic = ifp->if_l2com;
MALO_LOCK_INIT(sc);
@@ -215,6 +222,45 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
}
sc->malo_mh = mh;
+ /*
+ * Load firmware so we can get setup. We arbitrarily pick station
+ * firmware; we'll re-load firmware as needed so setting up
+ * the wrong mode isn't a big deal.
+ */
+ error = malo_hal_fwload(mh, "malo8335-h", "malo8335-m");
+ if (error != 0) {
+ if_printf(ifp, "unable to setup firmware\n");
+ goto bad1;
+ }
+ /* XXX gethwspecs() extracts correct informations? not maybe! */
+ error = malo_hal_gethwspecs(mh, &sc->malo_hwspecs);
+ if (error != 0) {
+ if_printf(ifp, "unable to fetch h/w specs\n");
+ goto bad1;
+ }
+
+ DPRINTF(sc, MALO_DEBUG_FW,
+ "malo_hal_gethwspecs: hwversion 0x%x hostif 0x%x"
+ "maxnum_wcb 0x%x maxnum_mcaddr 0x%x maxnum_tx_wcb 0x%x"
+ "regioncode 0x%x num_antenna 0x%x fw_releasenum 0x%x"
+ "wcbbase0 0x%x rxdesc_read 0x%x rxdesc_write 0x%x"
+ "ul_fw_awakecookie 0x%x w[4] = %x %x %x %x",
+ sc->malo_hwspecs.hwversion,
+ sc->malo_hwspecs.hostinterface, sc->malo_hwspecs.maxnum_wcb,
+ sc->malo_hwspecs.maxnum_mcaddr, sc->malo_hwspecs.maxnum_tx_wcb,
+ sc->malo_hwspecs.regioncode, sc->malo_hwspecs.num_antenna,
+ sc->malo_hwspecs.fw_releasenum, sc->malo_hwspecs.wcbbase0,
+ sc->malo_hwspecs.rxdesc_read, sc->malo_hwspecs.rxdesc_write,
+ sc->malo_hwspecs.ul_fw_awakecookie,
+ sc->malo_hwspecs.wcbbase[0], sc->malo_hwspecs.wcbbase[1],
+ sc->malo_hwspecs.wcbbase[2], sc->malo_hwspecs.wcbbase[3]);
+
+ /* NB: firmware looks that it does not export regdomain info API. */
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
sc->malo_txantenna = 0x2; /* h/w default */
sc->malo_rxantenna = 0xffff; /* h/w default */
@@ -228,6 +274,9 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
if_printf(ifp, "failed to setup descriptors: %d\n", error);
goto bad1;
}
+ error = malo_setup_hwdma(sc); /* push to firmware */
+ if (error != 0) /* NB: malo_setupdma prints msg */
+ goto bad1;
sc->malo_tq = taskqueue_create_fast("malo_taskq", M_NOWAIT,
taskqueue_thread_enqueue, &sc->malo_tq);
@@ -247,12 +296,6 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- /* NB: firmware looks that it does not export regdomain info API. */
- bands = 0;
- setbit(&bands, IEEE80211_MODE_11B);
- setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
-
ic->ic_ifp = ifp;
/* XXX not right but it's not used anywhere important */
ic->ic_phytype = IEEE80211_T_OFDM;
@@ -273,24 +316,23 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
* packets so we can add it efficiently.
*/
ic->ic_headroom = sizeof(struct malo_txrec) -
- sizeof(struct ieee80211_frame);
+ sizeof(struct ieee80211_frame);
+
+ /* get mac address from hardware */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->malo_hwspecs.macaddr);
/* call MI attach routine. */
ieee80211_ifattach(ic);
/* override default methods */
- ic->ic_updateslot = malo_updateslot;
+ ic->ic_vap_create = malo_vap_create;
+ ic->ic_vap_delete = malo_vap_delete;
ic->ic_raw_xmit = malo_raw_xmit;
-
- sc->malo_newstate = ic->ic_newstate;
- ic->ic_newstate = malo_newstate;
+ ic->ic_updateslot = malo_updateslot;
ic->ic_scan_start = malo_scan_start;
ic->ic_scan_end = malo_scan_end;
ic->ic_set_channel = malo_set_channel;
- /* complete initialization */
- ieee80211_media_init(ic, malo_media_change, ieee80211_media_status);
-
sc->malo_invalid = 0; /* ready to go, enable int handling */
malo_bpfattach(sc);
@@ -302,6 +344,7 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
if (bootverbose)
ieee80211_announce(ic);
+ malo_announce(sc);
return 0;
bad1:
@@ -313,6 +356,61 @@ bad:
return error;
}
+static struct ieee80211vap *
+malo_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct malo_vap *mvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "multiple vaps not supported\n");
+ return NULL;
+ }
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ /* fall thru... */
+ case IEEE80211_M_MONITOR:
+ break;
+ default:
+ if_printf(ifp, "%s mode not supported\n",
+ ieee80211_opmode_name[opmode]);
+ return NULL; /* unsupported */
+ }
+ mvp = (struct malo_vap *) malloc(sizeof(struct malo_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (mvp == NULL) {
+ if_printf(ifp, "cannot allocate vap state block\n");
+ return NULL;
+ }
+ vap = &mvp->malo_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ mvp->malo_newstate = vap->iv_newstate;
+ vap->iv_newstate = malo_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap,
+ ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+malo_vap_delete(struct ieee80211vap *vap)
+{
+ struct malo_vap *mvp = MALO_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(mvp, M_80211_VAP);
+}
+
int
malo_intr(void *arg)
{
@@ -353,14 +451,12 @@ malo_intr(void *arg)
/* TKIP ICV error */
sc->malo_stats.mst_rx_badtkipicv++;
}
-
#ifdef MALO_DEBUG
if (((status | sc->malo_imask) ^ sc->malo_imask) != 0)
DPRINTF(sc, MALO_DEBUG_INTR,
"%s: can't handle interrupt status 0x%x\n",
__func__, status);
#endif
-
return (FILTER_HANDLED);
}
@@ -1009,8 +1105,8 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
int error, ismcast, iswep;
int copyhdrlen, hdrlen, pktlen;
struct ieee80211_frame *wh;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_txdesc *ds;
struct malo_txrec *tr;
struct malo_txq *txq;
@@ -1045,7 +1141,7 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
* ExtIV filled in for CCMP and this also adjusts
* the headers which simplifies our work below.
*/
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
/*
* This can happen when the key is yanked after the
@@ -1068,15 +1164,14 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->malo_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
sc->malo_tx_th.wt_flags = 0; /* XXX */
if (iswep)
sc->malo_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
sc->malo_tx_th.wt_txpower = ni->ni_txpower;
sc->malo_tx_th.wt_antenna = sc->malo_txantenna;
- bpf_mtap2(sc->malo_drvbpf,
- &sc->malo_tx_th, sc->malo_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->malo_tx_th, sc->malo_tx_th_len, m0);
}
/*
@@ -1186,137 +1281,40 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
static void
malo_start(struct ifnet *ifp)
{
- int nqueued = 0;
- struct ether_header *eh;
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct malo_txq *txq = &sc->malo_txq[0];
struct malo_txbuf *bf = NULL;
- struct malo_txq *txq = NULL;
struct mbuf *m;
+ int nqueued = 0;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->malo_invalid)
return;
for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ bf = malo_getbuf(sc, txq);
+ if (bf == NULL) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+
+ /* XXX blocks other traffic */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->malo_stats.mst_tx_qstop++;
+ break;
+ }
/*
- * Poll the management queue for frames; they
- * have priority over normal data frames.
+ * Encapsulate the packet in prep for transmission.
*/
- IF_DEQUEUE(&ic->ic_mgtq, m);
+ m = ieee80211_encap(ni, m);
if (m == NULL) {
- /*
- * No data frames go out unless we're associated.
- */
- if (ic->ic_state != IEEE80211_S_RUN) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: discard data packet, state %s\n",
- __func__,
- ieee80211_state_name[ic->ic_state]);
- sc->malo_stats.mst_tx_discard++;
- break;
- }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
- if (m == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- /*
- * Find the node for the destination so we can do
- * things like power save and fast frames aggregation.
- */
- if (m->m_len < sizeof(struct ether_header) &&
- (m = m_pullup(m, sizeof(struct ether_header))) ==
- NULL) {
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ni = NULL;
- goto bad;
- }
- eh = mtod(m, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- /* NB: ieee80211_find_txnode does stat+msg */
- m_freem(m);
- goto bad;
- }
- /* calculate priority so we can find the tx queue */
- if (ieee80211_classify(ic, m, ni)) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: discard, classification failure\n",
- __func__);
- m_freem(m);
- goto bad;
- }
-
- txq = &sc->malo_txq[0];
-
- bf = malo_getbuf(sc, txq);
- if (bf == NULL) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m);
- ieee80211_free_node(ni);
-
- /* XXX blocks other traffic */
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->malo_stats.mst_tx_qstop++;
- break;
- }
- ifp->if_opackets++;
-
- if (bpf_peers_present(ifp->if_bpf))
- bpf_mtap(ifp->if_bpf, m);
-
- /*
- * Encapsulate the packet in prep for transmission.
- */
- m = ieee80211_encap(ic, m, ni);
- if (m == NULL) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: encapsulation failure\n", __func__);
- sc->malo_stats.mst_tx_encap++;
- goto bad;
- }
- } else {
- /*
- * Grab a TX buffer and associated resources.
- * Note that we depend on the classification
- * by the 802.11 layer to get to the right h/w
- * queue. Management frames must ALWAYS go on
- * queue 1 but we cannot just force that here
- * because we may receive non-mgt frames through
- * the ic_mgtq (e.g. null data frames).
- */
- txq = &sc->malo_txq[0];
- bf = malo_getbuf(sc, txq);
- if (bf == NULL) {
- IF_PREPEND(&ic->ic_mgtq, m);
- /* XXX stat */
- break;
- }
-
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
- m->m_pkthdr.rcvif = NULL;
-
- wh = mtod(m, struct ieee80211_frame *);
- sc->malo_stats.mst_tx_mgmt++;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
+ DPRINTF(sc, MALO_DEBUG_XMIT,
+ "%s: encapsulation failure\n", __func__);
+ sc->malo_stats.mst_tx_encap++;
+ goto bad;
}
-
/*
* Pass the frame to the h/w for transmission.
*/
@@ -1382,7 +1380,8 @@ static int
malo_hal_reset(struct malo_softc *sc)
{
static int first = 0;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
if (first == 0) {
@@ -1511,10 +1510,8 @@ malo_startrecv(struct malo_softc *sc)
}
static void
-malo_init(void *arg)
+malo_init_locked(struct malo_softc *sc)
{
- struct malo_softc *sc = (struct malo_softc *) arg;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
struct malo_hal *mh = sc->malo_mh;
int error;
@@ -1522,56 +1519,7 @@ malo_init(void *arg)
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags 0x%x\n",
__func__, ifp->if_flags);
- if (!sc->malo_fw_loaded) {
- /*
- * Load firmware so we can get setup.
- */
- error = malo_hal_fwload(mh, "malo8335-h", "malo8335-m");
- if (error != 0) {
- if_printf(ifp, "unable to setup firmware\n");
- return;
- }
- /* XXX gethwspecs() extracts correct informations? not maybe! */
- error = malo_hal_gethwspecs(mh, &sc->malo_hwspecs);
- if (error != 0) {
- if_printf(ifp, "unable to fetch h/w specs\n");
- return;
- }
-
- DPRINTF(sc, MALO_DEBUG_FW,
- "malo_hal_gethwspecs: hwversion 0x%x hostif 0x%x"
- "maxnum_wcb 0x%x maxnum_mcaddr 0x%x maxnum_tx_wcb 0x%x"
- "regioncode 0x%x num_antenna 0x%x fw_releasenum 0x%x"
- "wcbbase0 0x%x rxdesc_read 0x%x rxdesc_write 0x%x"
- "ul_fw_awakecookie 0x%x w[4] = %x %x %x %x",
- sc->malo_hwspecs.hwversion,
- sc->malo_hwspecs.hostinterface, sc->malo_hwspecs.maxnum_wcb,
- sc->malo_hwspecs.maxnum_mcaddr,
- sc->malo_hwspecs.maxnum_tx_wcb,
- sc->malo_hwspecs.regioncode, sc->malo_hwspecs.num_antenna,
- sc->malo_hwspecs.fw_releasenum, sc->malo_hwspecs.wcbbase0,
- sc->malo_hwspecs.rxdesc_read, sc->malo_hwspecs.rxdesc_write,
- sc->malo_hwspecs.ul_fw_awakecookie,
- sc->malo_hwspecs.wcbbase[0], sc->malo_hwspecs.wcbbase[1],
- sc->malo_hwspecs.wcbbase[2], sc->malo_hwspecs.wcbbase[3]);
-
- error = malo_setup_hwdma(sc); /* push to firmware */
- /* NB: malo_setupdma prints msg */
- if (error != 0) {
- if_printf(ifp, "%s: failed to set up h/w dma\n",
- __func__);
- return;
- }
-
- /* set reddomain. */
- ic->ic_regdomain = sc->malo_hwspecs.regioncode;
-
- malo_announce(sc);
-
- sc->malo_fw_loaded = 1;
- }
-
- MALO_LOCK(sc);
+ MALO_LOCK_ASSERT(sc);
/*
* Stop anything previously setup. This is safe whether this is
@@ -1584,7 +1532,7 @@ malo_init(void *arg)
*/
if (!malo_hal_reset(sc)) {
if_printf(ifp, "%s: unable to reset hardware\n", __func__);
- goto done;
+ return;
}
/*
@@ -1594,7 +1542,7 @@ malo_init(void *arg)
if (error != 0) {
if_printf(ifp, "%s: unable to start recv logic, error %d\n",
__func__, error);
- goto done;
+ return;
}
/*
@@ -1610,30 +1558,26 @@ malo_init(void *arg)
| MALO_A2HRIC_BIT_CHAN_SWITCH;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ic->ic_state = IEEE80211_S_INIT;
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
-
malo_hal_intrset(mh, sc->malo_imask);
+}
- /*
- * The hardware should be ready to go now so it's safe to kick
- * the 802.11 state machine as it's likely to immediately call back
- * to us to send mgmt frames.
- */
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+static void
+malo_init(void *arg)
+{
+ struct malo_softc *sc = (struct malo_softc *) arg;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags 0x%x\n",
+ __func__, ifp->if_flags);
-done:
- if (error != 0)
- if_printf(ifp,
- "error(%d) occurred during the initializing.\n", error);
+ MALO_LOCK(sc);
+ malo_init_locked(sc);
MALO_UNLOCK(sc);
- return;
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
}
/*
@@ -1642,9 +1586,9 @@ done:
static void
malo_setmcastfilter(struct malo_softc *sc)
{
- struct ieee80211com *ic = &sc->malo_ic;
- struct ifmultiaddr *ifma;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmultiaddr *ifma;
uint8_t macs[IEEE80211_ADDR_LEN * MALO_HAL_MCAST_MAX];
uint8_t *mp;
int nmc;
@@ -1686,8 +1630,8 @@ all:
static int
malo_mode_init(struct malo_softc *sc)
{
- struct ieee80211com *ic = &sc->malo_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
/*
@@ -1733,11 +1677,12 @@ malo_tx_draintxq(struct malo_softc *sc, struct malo_txq *txq)
MALO_TXQ_UNLOCK(txq);
#ifdef MALO_DEBUG
if (sc->malo_debug & MALO_DEBUG_RESET) {
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct malo_txrec *tr =
mtod(bf->bf_m, const struct malo_txrec *);
malo_printtxbuf(bf, txq->qnum, ix);
- ieee80211_dump_pkt(&sc->malo_ic,
- (const uint8_t *)&tr->wh,
+ ieee80211_dump_pkt(ic, (const uint8_t *)&tr->wh,
bf->bf_m->m_len - sizeof(tr->fwlen), 0, -1);
}
#endif /* MALO_DEBUG */
@@ -1763,10 +1708,9 @@ malo_tx_draintxq(struct malo_softc *sc, struct malo_txq *txq)
static void
malo_stop_locked(struct ifnet *ifp, int disable)
{
- int i;
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
struct malo_hal *mh = sc->malo_mh;
+ int i;
DPRINTF(sc, MALO_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
__func__, sc->malo_invalid, ifp->if_flags);
@@ -1778,28 +1722,19 @@ malo_stop_locked(struct ifnet *ifp, int disable)
/*
* Shutdown the hardware and driver:
- * reset 802.11 state machine
- * turn off timers
* disable interrupts
* turn off the radio
- * clear transmit machinery
- * clear receive machinery
* drain and release tx queues
- * reclaim beacon resources
- * power down hardware
*
* Note that some of this work is not possible if the hardware
* is gone (invalid).
*/
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
ifp->if_timer = 0;
- if (sc->malo_fw_loaded == 1) {
- /* diable interrupt. */
- malo_hal_intrset(mh, 0);
- /* turn off the radio. */
- malo_hal_setradio(mh, 0, MHP_AUTO_PREAMBLE);
- }
+ /* diable interrupt. */
+ malo_hal_intrset(mh, 0);
+ /* turn off the radio. */
+ malo_hal_setradio(mh, 0, MHP_AUTO_PREAMBLE);
/* drain and release tx queues. */
for (i = 0; i < MALO_NUM_TX_QUEUES; i++)
@@ -1812,11 +1747,11 @@ malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
#define MALO_IS_RUNNING(ifp) \
((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
MALO_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (MALO_IS_RUNNING(ifp)) {
@@ -1836,38 +1771,25 @@ malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* torn down much of our state. There's
* probably a better way to deal with this.
*/
- if (!sc->malo_invalid)
- malo_init(sc);
+ if (!sc->malo_invalid) {
+ malo_init_locked(sc);
+ startall = 1;
+ }
} else
malo_stop_locked(ifp, 1);
break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- /*
- * The upper layer has already installed/removed
- * the multicast address(es), just recalculate the
- * multicast filter for the card.
- */
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- malo_mode_init(sc);
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (MALO_IS_RUNNING(ifp) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- malo_init(sc);
- error = 0;
- }
- if (error == ERESTART) {
- /* XXX we need to reset the device here. */
- error = 0;
- }
+ error = ether_ioctl(ifp, cmd, data);
break;
}
-
MALO_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
#undef MALO_IS_RUNNING
}
@@ -1882,7 +1804,7 @@ static void
malo_updateslot(struct ifnet *ifp)
{
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
int error;
@@ -1906,72 +1828,46 @@ malo_updateslot(struct ifnet *ifp)
}
static int
-malo_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+malo_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
- struct ieee80211_node *ni = ic->ic_bss;
- struct ifnet *ifp = ic->ic_ifp;
- struct malo_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct malo_softc *sc = ic->ic_ifp->if_softc;
struct malo_hal *mh = sc->malo_mh;
int error;
DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
/*
- * Carry out firmware actions per-state.
+ * Invoke the net80211 layer first so iv_bss is setup.
*/
- switch (nstate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- /* NB: do nothing. */
- break;
- case IEEE80211_S_ASSOC:
- malo_hal_setradio(mh, 1,
- (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
- MHP_SHORT_PREAMBLE : MHP_LONG_PREAMBLE);
- break;
- case IEEE80211_S_RUN:
+ error = MALO_VAP(vap)->malo_newstate(vap, nstate, arg);
+ if (error != 0)
+ return error;
+
+ if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ enum ieee80211_phymode mode = ieee80211_chan2mode(ni->ni_chan);
+ const struct ieee80211_txparam *tp = &vap->iv_txparms[mode];
+
DPRINTF(sc, MALO_DEBUG_STATE,
- "%s: %s(RUN): ic_flags 0x%08x bintvl %d bssid %s "
- "capinfo 0x%04x chan %d\n",
- ifp->if_xname, __func__, ic->ic_flags,
+ "%s: %s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
+ "capinfo 0x%04x chan %d associd 0x%x mode %d rate %d\n",
+ vap->iv_ifp->if_xname, __func__, vap->iv_flags,
ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo,
- ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s: aid 0x%x\n",
- ic->ic_ifp->if_xname, __func__, ni->ni_associd);
- malo_hal_setassocid(sc->malo_mh,
- ni->ni_bssid, ni->ni_associd);
-
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- /* automatic rate adaption */
- malo_hal_set_rate(mh, ic->ic_curmode, 0);
- else
- /* fixed rate */
- malo_hal_set_rate(mh, ic->ic_curmode,
- malo_fix2rate(ic->ic_fixed_rate));
- break;
- default:
- break;
- }
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ni->ni_associd, mode, tp->ucastrate);
- break;
- default:
- if_printf(ifp, "%s: can't handle state %s -> %s\n",
- __func__, ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]);
+ malo_hal_setradio(mh, 1,
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
+ MHP_SHORT_PREAMBLE : MHP_LONG_PREAMBLE);
+ malo_hal_setassocid(sc->malo_mh, ni->ni_bssid, ni->ni_associd);
+ malo_hal_set_rate(mh, mode,
+ tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
+ 0 : malo_fix2rate(tp->ucastrate));
}
-
- /*
- * Invoke the parent method to complete the work.
- */
- error = sc->malo_newstate(ic, nstate, arg);
-
- return error;
+ return 0;
}
static int
@@ -2038,33 +1934,13 @@ malo_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return 0;
}
-static int
-malo_media_change(struct ifnet *ifp)
-{
-#define IS_UP(ifp) \
- ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- struct malo_softc *sc = ifp->if_softc;
-
- if (IS_UP(ifp))
- malo_init(sc);
- error = 0;
- }
- return error;
-#undef IS_UP
-}
-
static void
malo_bpfattach(struct malo_softc *sc)
{
struct ifnet *ifp = sc->malo_ifp;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->malo_tx_th),
- &sc->malo_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->malo_tx_th));
/*
* Initialize constant fields.
@@ -2206,9 +2082,9 @@ malo_rx_proc(void *arg, int npending)
((((const struct ieee80211_frame *)wh)->i_fc[1] & \
IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
struct malo_softc *sc = arg;
- struct malo_rxbuf *bf;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct malo_rxbuf *bf;
struct malo_rxdesc *ds;
struct mbuf *m, *mnew;
struct ieee80211_qosframe *wh;
@@ -2232,8 +2108,7 @@ malo_rx_proc(void *arg, int npending)
return;
bf = sc->malo_rxnext;
- for (ntodo = malo_rxquota; ntodo > 0 && (readptr != writeptr);
- ntodo--) {
+ for (ntodo = malo_rxquota; ntodo > 0 && readptr != writeptr; ntodo--) {
if (bf == NULL) {
bf = STAILQ_FIRST(&sc->malo_rxbuf);
break;
@@ -2281,12 +2156,12 @@ malo_rx_proc(void *arg, int npending)
* payload prior to constructing the header.
*/
m = bf->bf_m;
- data = mtod(m, uint8_t *);
+ data = mtod(m, uint8_t *);;
hdrlen = ieee80211_anyhdrsize(data + sizeof(uint16_t));
off = sizeof(uint16_t) + sizeof(struct ieee80211_frame_addr4);
/*
- * Calculate RSSI. XXX wrong
+ * Calculate RSSI. XXX wrong
*/
rssi = 2 * ((int) ds->snr - ds->nf); /* NB: .5 dBm */
if (rssi > 100)
@@ -2307,7 +2182,6 @@ malo_rx_proc(void *arg, int npending)
ifp->if_ierrors++;
goto rx_next;
}
-
/*
* Attach the dma buffer to the mbuf; malo_rxbuf_init will
* re-setup the rx descriptor using the replacement dma
@@ -2340,8 +2214,8 @@ malo_rx_proc(void *arg, int npending)
sc->malo_rx_th.wr_antsignal = rssi;
sc->malo_rx_th.wr_antnoise = ds->nf;
- bpf_mtap2(sc->malo_drvbpf,
- &sc->malo_rx_th, sc->malo_rx_th_len, m);
+ bpf_mtap2(ifp->if_bpf, &sc->malo_rx_th,
+ sc->malo_rx_th_len, m);
}
#ifdef MALO_DEBUG
if (IFF_DUMPPKTS_RECV(sc, wh)) {
@@ -2353,10 +2227,12 @@ malo_rx_proc(void *arg, int npending)
/* dispatch */
ni = ieee80211_find_rxnode(ic,
- (const struct ieee80211_frame_min *) wh);
- (void) ieee80211_input(ic, m, ni, rssi, ds->nf, 0/*XXX*/);
- ieee80211_free_node(ni);
-
+ (struct ieee80211_frame_min *)wh);
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, ds->nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, ds->nf, 0);
rx_next:
/* NB: ignore ENOMEM so we process more descriptors */
(void) malo_rxbuf_init(sc, bf);
@@ -2378,9 +2254,7 @@ malo_stop(struct ifnet *ifp, int disable)
struct malo_softc *sc = ifp->if_softc;
MALO_LOCK(sc);
-
malo_stop_locked(ifp, disable);
-
MALO_UNLOCK(sc);
}
@@ -2400,6 +2274,7 @@ int
malo_detach(struct malo_softc *sc)
{
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n",
__func__, ifp->if_flags);
@@ -2427,7 +2302,7 @@ malo_detach(struct malo_softc *sc)
* it last
* Other than that, it's straightforward...
*/
- ieee80211_ifdetach(&sc->malo_ic);
+ ieee80211_ifdetach(ic);
malo_dma_cleanup(sc);
malo_tx_cleanup(sc);
malo_hal_detach(sc->malo_mh);
@@ -2441,7 +2316,6 @@ malo_detach(struct malo_softc *sc)
void
malo_shutdown(struct malo_softc *sc)
{
-
malo_stop(sc->malo_ifp, 1);
}
@@ -2464,9 +2338,6 @@ malo_resume(struct malo_softc *sc)
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n",
__func__, ifp->if_flags);
- if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_UP)
malo_init(sc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- malo_start(ifp);
- }
}
diff --git a/sys/dev/malo/if_malo.h b/sys/dev/malo/if_malo.h
index 0cb5bc6..070649a 100644
--- a/sys/dev/malo/if_malo.h
+++ b/sys/dev/malo/if_malo.h
@@ -512,8 +512,14 @@ struct malo_txrec {
struct ieee80211_frame_addr4 wh;
} __packed;
+struct malo_vap {
+ struct ieee80211vap malo_vap;
+ int (*malo_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define MALO_VAP(vap) ((struct malo_vap *)(vap))
+
struct malo_softc {
- struct ieee80211com malo_ic; /* IEEE 802.11 common */
device_t malo_dev;
struct ifnet *malo_ifp; /* interface common */
struct mtx malo_mtx; /* master lock (recursive) */
@@ -527,8 +533,7 @@ struct malo_softc {
unsigned int malo_invalid : 1,/* disable hardware accesses */
malo_recvsetup : 1, /* recv setup */
- malo_fixedrate : 1, /* use fixed tx rate */
- malo_fw_loaded : 1; /* fw loaded */
+ malo_fixedrate: 1; /* use fixed tx rate */
struct malo_hal *malo_mh; /* h/w access layer */
struct malo_hal_hwspec malo_hwspecs; /* h/w capabilities */
@@ -546,9 +551,6 @@ struct malo_softc {
struct malo_txq malo_txq[MALO_NUM_TX_QUEUES];
struct task malo_txtask; /* tx int processing */
- int (*malo_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
-
struct bpf_if *malo_drvbpf;
struct malo_tx_radiotap_header malo_tx_th;
int malo_tx_th_len;
diff --git a/sys/dev/ral/if_ral_pci.c b/sys/dev/ral/if_ral_pci.c
index 2ceafe4..a36218b 100644
--- a/sys/dev/ral/if_ral_pci.c
+++ b/sys/dev/ral/if_ral_pci.c
@@ -50,16 +50,18 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_amrr.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2560var.h>
#include <dev/ral/rt2661var.h>
MODULE_DEPEND(ral, pci, 1, 1, 1);
+MODULE_DEPEND(ral, firmware, 1, 1, 1);
MODULE_DEPEND(ral, wlan, 1, 1, 1);
+MODULE_DEPEND(ral, wlan_amrr, 1, 1, 1);
struct ral_pci_ident {
uint16_t vendor;
diff --git a/sys/dev/ral/if_ralrate.c b/sys/dev/ral/if_ralrate.c
deleted file mode 100644
index b8922ba..0000000
--- a/sys/dev/ral/if_ralrate.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/* $FreeBSD$ */
-/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
-/*-
- * Copyright (c) 2003, 2004 David Young. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- * 3. The name of David Young may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
- * Young 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.
- */
-
-#include <sys/param.h>
-#include <sys/sockio.h>
-#include <sys/mbuf.h>
-#include <sys/kernel.h>
-#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_dl.h>
-#include <net/if_media.h>
-#include <net/if_types.h>
-
-#include <net80211/ieee80211_var.h>
-
-#include <dev/ral/if_ralrate.h>
-
-#ifdef interpolate
-#undef interpolate
-#endif
-#define interpolate(parm, old, new) ((parm##_old * (old) + \
- (parm##_denom - parm##_old) * (new)) / \
- parm##_denom)
-
-static struct ral_rssadapt_expavgctl master_expavgctl = {
- rc_decay_denom : 16,
- rc_decay_old : 15,
- rc_thresh_denom : 8,
- rc_thresh_old : 4,
- rc_avgrssi_denom : 8,
- rc_avgrssi_old : 4
-};
-
-int
-ral_rssadapt_choose(struct ral_rssadapt *ra, struct ieee80211_rateset *rs,
- struct ieee80211_frame *wh, u_int len, const char *dvname, int do_not_adapt)
-{
- u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
- int flags = 0, i, rateidx = 0, thridx, top;
-
- if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
- flags |= IEEE80211_RATE_BASIC;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thridx = i;
- if (len <= top)
- break;
- }
-
- thrs = &ra->ra_rate_thresh[thridx];
-
- i = rs->rs_nrates;
- while (--i >= 0) {
- rateidx = i;
- if ((rs->rs_rates[i] & flags) != flags)
- continue;
- if (do_not_adapt)
- break;
- if ((*thrs)[i] < ra->ra_avg_rssi)
- break;
- }
-
- return rateidx;
-}
-
-void
-ral_rssadapt_updatestats(struct ral_rssadapt *ra)
-{
- long interval;
-
- ra->ra_pktrate =
- (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2;
- ra->ra_nfail = ra->ra_nok = 0;
-
- /* a node is eligible for its rate to be raised every 1/10 to 10
- * seconds, more eligible in proportion to recent packet rates.
- */
- interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate));
- ra->ra_raise_interval.tv_sec = interval / (1000 * 1000);
- ra->ra_raise_interval.tv_usec = interval % (1000 * 1000);
-}
-
-void
-ral_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct ral_rssadapt *ra, int rssi)
-{
- ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi,
- ra->ra_avg_rssi, (rssi << 8));
-}
-
-/*
- * Adapt the data rate to suit the conditions. When a transmitted
- * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
- * raise the RSS threshold for transmitting packets of similar length at
- * the same data rate.
- */
-void
-ral_rssadapt_lower_rate(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct ral_rssadapt *ra, struct ral_rssdesc *id)
-{
- struct ieee80211_rateset *rs = &ni->ni_rates;
- u_int16_t last_thr;
- u_int i, thridx, top;
-
- ra->ra_nfail++;
-
- if (id->id_rateidx >= rs->rs_nrates)
- return;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thridx = i;
- if (id->id_len <= top)
- break;
- }
-
- last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx];
- ra->ra_rate_thresh[thridx][id->id_rateidx] =
- interpolate(master_expavgctl.rc_thresh, last_thr,
- (id->id_rssi << 8));
-}
-
-void
-ral_rssadapt_raise_rate(struct ieee80211com *ic, struct ral_rssadapt *ra,
- struct ral_rssdesc *id)
-{
- u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr;
- struct ieee80211_node *ni = id->id_node;
- struct ieee80211_rateset *rs = &ni->ni_rates;
- int i, rate, top;
-
- ra->ra_nok++;
-
- if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval))
- return;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thrs = &ra->ra_rate_thresh[i];
- if (id->id_len <= top)
- break;
- }
-
- if (id->id_rateidx + 1 < rs->rs_nrates &&
- (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) {
- rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL);
-
- oldthr = (*thrs)[id->id_rateidx + 1];
- if ((*thrs)[id->id_rateidx] == 0)
- newthr = ra->ra_avg_rssi;
- else
- newthr = (*thrs)[id->id_rateidx];
- (*thrs)[id->id_rateidx + 1] =
- interpolate(master_expavgctl.rc_decay, oldthr, newthr);
- }
-}
diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c
index 63f7de0..1536a35 100644
--- a/sys/dev/ral/rt2560.c
+++ b/sys/dev/ral/rt2560.c
@@ -52,8 +52,10 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_amrr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -61,7 +63,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip.h>
#include <netinet/if_ether.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2560reg.h>
#include <dev/ral/rt2560var.h>
@@ -69,15 +70,26 @@ __FBSDID("$FreeBSD$");
((rssi) > (RT2560_NOISE_FLOOR + (sc)->rssi_corr) ? \
((rssi) - RT2560_NOISE_FLOOR - (sc)->rssi_corr) : 0)
+#define RAL_DEBUG
#ifdef RAL_DEBUG
-#define DPRINTF(x) do { if (ral_debug > 0) printf x; } while (0)
-#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
-extern int ral_debug;
+#define DPRINTF(sc, fmt, ...) do { \
+ if (sc->sc_debug > 0) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#define DPRINTFN(sc, n, fmt, ...) do { \
+ if (sc->sc_debug >= (n)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
#else
-#define DPRINTF(x)
-#define DPRINTFN(n, x)
+#define DPRINTF(sc, fmt, ...)
+#define DPRINTFN(sc, n, fmt, ...)
#endif
+static struct ieee80211vap *rt2560_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rt2560_vap_delete(struct ieee80211vap *);
static void rt2560_dma_map_addr(void *, bus_dma_segment_t *, int,
int);
static int rt2560_alloc_tx_ring(struct rt2560_softc *,
@@ -94,10 +106,8 @@ static void rt2560_free_rx_ring(struct rt2560_softc *,
struct rt2560_rx_ring *);
static struct ieee80211_node *rt2560_node_alloc(
struct ieee80211_node_table *);
-static int rt2560_media_change(struct ifnet *);
-static void rt2560_iter_func(void *, struct ieee80211_node *);
-static void rt2560_update_rssadapt(void *);
-static int rt2560_newstate(struct ieee80211com *,
+static void rt2560_newassoc(struct ieee80211_node *, int);
+static int rt2560_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static uint16_t rt2560_eeprom_read(struct rt2560_softc *, uint8_t);
static void rt2560_encryption_intr(struct rt2560_softc *);
@@ -105,16 +115,12 @@ static void rt2560_tx_intr(struct rt2560_softc *);
static void rt2560_prio_intr(struct rt2560_softc *);
static void rt2560_decryption_intr(struct rt2560_softc *);
static void rt2560_rx_intr(struct rt2560_softc *);
-static void rt2560_beacon_update(struct ieee80211com *, int item);
+static void rt2560_beacon_update(struct ieee80211vap *, int item);
static void rt2560_beacon_expire(struct rt2560_softc *);
static void rt2560_wakeup_expire(struct rt2560_softc *);
-static uint8_t rt2560_rxrate(struct rt2560_rx_desc *);
-static int rt2560_ack_rate(struct ieee80211com *, int);
static void rt2560_scan_start(struct ieee80211com *);
static void rt2560_scan_end(struct ieee80211com *);
static void rt2560_set_channel(struct ieee80211com *);
-static uint16_t rt2560_txtime(int, int, uint32_t);
-static uint8_t rt2560_plcp_signal(int);
static void rt2560_setup_tx_desc(struct rt2560_softc *,
struct rt2560_tx_desc *, uint32_t, int, int, int,
bus_addr_t);
@@ -122,13 +128,11 @@ static int rt2560_tx_bcn(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
static int rt2560_tx_mgt(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
-static struct mbuf *rt2560_get_rts(struct rt2560_softc *,
- struct ieee80211_frame *, uint16_t);
static int rt2560_tx_data(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
+static void rt2560_start_locked(struct ifnet *);
static void rt2560_start(struct ifnet *);
static void rt2560_watchdog(void *);
-static int rt2560_reset(struct ifnet *);
static int rt2560_ioctl(struct ifnet *, u_long, caddr_t);
static void rt2560_bbp_write(struct rt2560_softc *, uint8_t,
uint8_t);
@@ -148,13 +152,15 @@ static void rt2560_update_led(struct rt2560_softc *, int, int);
static void rt2560_set_bssid(struct rt2560_softc *, const uint8_t *);
static void rt2560_set_macaddr(struct rt2560_softc *, uint8_t *);
static void rt2560_get_macaddr(struct rt2560_softc *, uint8_t *);
-static void rt2560_update_promisc(struct rt2560_softc *);
+static void rt2560_update_promisc(struct ifnet *);
static const char *rt2560_get_rf(int);
static void rt2560_read_config(struct rt2560_softc *);
static int rt2560_bbp_init(struct rt2560_softc *);
static void rt2560_set_txantenna(struct rt2560_softc *, int);
static void rt2560_set_rxantenna(struct rt2560_softc *, int);
+static void rt2560_init_locked(struct rt2560_softc *);
static void rt2560_init(void *);
+static void rt2560_stop_locked(struct rt2560_softc *);
static int rt2560_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
@@ -192,9 +198,10 @@ int
rt2560_attach(device_t dev, int id)
{
struct rt2560_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
- int error, bands;
+ int error;
+ uint8_t bands;
sc->sc_dev = dev;
@@ -202,14 +209,10 @@ rt2560_attach(device_t dev, int id)
MTX_DEF | MTX_RECURSE);
callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
- callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE);
/* retrieve RT2560 rev. no */
sc->asic_rev = RAL_READ(sc, RT2560_CSR0);
- /* retrieve MAC address */
- rt2560_get_macaddr(sc, ic->ic_myaddr);
-
/* retrieve RF rev. no and various other things from EEPROM */
rt2560_read_config(sc);
@@ -249,11 +252,15 @@ rt2560_attach(device_t dev, int id)
goto fail5;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
goto fail6;
}
+ ic = ifp->if_l2com;
+
+ /* retrieve MAC address */
+ rt2560_get_macaddr(sc, ic->ic_myaddr);
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
@@ -266,48 +273,47 @@ rt2560_attach(device_t dev, int id)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
- IEEE80211_C_BGSCAN | /* bg scanning support */
- IEEE80211_C_WPA; /* 802.11i */
+ IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
+ | IEEE80211_C_HOSTAP /* hostap mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+#ifdef notyet
+ | IEEE80211_C_TXFRAG /* handle tx frags */
+#endif
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RT2560_RF_5222)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rt2560_newassoc;
+ ic->ic_raw_xmit = rt2560_raw_xmit;
+ ic->ic_updateslot = rt2560_update_slot;
+ ic->ic_update_promisc = rt2560_update_promisc;
+ ic->ic_node_alloc = rt2560_node_alloc;
ic->ic_scan_start = rt2560_scan_start;
ic->ic_scan_end = rt2560_scan_end;
ic->ic_set_channel = rt2560_set_channel;
- ic->ic_node_alloc = rt2560_node_alloc;
- ic->ic_updateslot = rt2560_update_slot;
- ic->ic_reset = rt2560_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rt2560_newstate;
- ic->ic_raw_xmit = rt2560_raw_xmit;
- ic->ic_update_beacon = rt2560_beacon_update;
- ieee80211_media_init(ic, rt2560_media_change, ieee80211_media_status);
+ ic->ic_vap_create = rt2560_vap_create;
+ ic->ic_vap_delete = rt2560_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -320,8 +326,11 @@ rt2560_attach(device_t dev, int id)
/*
* Add a few sysctl knobs.
*/
- sc->dwelltime = 200;
-
+#ifdef RAL_DEBUG
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs");
+#endif
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"txantenna", CTLFLAG_RW, &sc->tx_ant, 0, "tx antenna (0=auto)");
@@ -330,11 +339,6 @@ rt2560_attach(device_t dev, int id)
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"rxantenna", CTLFLAG_RW, &sc->rx_ant, 0, "rx antenna (0=auto)");
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
if (bootverbose)
ieee80211_announce(ic);
@@ -354,11 +358,10 @@ int
rt2560_detach(void *xsc)
{
struct rt2560_softc *sc = xsc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
rt2560_stop(sc);
- callout_stop(&sc->rssadapt_ch);
bpfdetach(ifp);
ieee80211_ifdetach(ic);
@@ -376,17 +379,88 @@ rt2560_detach(void *xsc)
return 0;
}
+static struct ieee80211vap *
+rt2560_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2560_vap *rvp;
+ struct ieee80211vap *vap;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "only 1 vap supported\n");
+ return NULL;
+ }
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ break;
+ case IEEE80211_M_WDS:
+ if (TAILQ_EMPTY(&ic->ic_vaps) ||
+ ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ if_printf(ifp, "wds only supported in ap mode\n");
+ return NULL;
+ }
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ default:
+ if_printf(ifp, "unknown opmode %d\n", opmode);
+ return NULL;
+ }
+ rvp = (struct rt2560_vap *) malloc(sizeof(struct rt2560_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->ral_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ rvp->ral_newstate = vap->iv_newstate;
+ vap->iv_newstate = rt2560_newstate;
+ vap->iv_update_beacon = rt2560_beacon_update;
+
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /* ms */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ if (TAILQ_FIRST(&ic->ic_vaps) == vap)
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rt2560_vap_delete(struct ieee80211vap *vap)
+{
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
void
rt2560_resume(void *xsc)
{
struct rt2560_softc *sc = xsc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
+ if (ifp->if_flags & IFF_UP)
+ rt2560_init(sc);
}
static void
@@ -702,117 +776,68 @@ rt2560_node_alloc(struct ieee80211_node_table *nt)
return (rn != NULL) ? &rn->ni : NULL;
}
-static int
-rt2560_media_change(struct ifnet *ifp)
-{
- struct rt2560_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rt2560_init(sc);
- }
- return error;
-}
-
-/*
- * This function is called for each node present in the node station table.
- */
static void
-rt2560_iter_func(void *arg, struct ieee80211_node *ni)
+rt2560_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct rt2560_node *rn = (struct rt2560_node *)ni;
+ struct ieee80211vap *vap = ni->ni_vap;
- ral_rssadapt_updatestats(&rn->rssadapt);
-}
-
-/*
- * This function is called periodically (every 100ms) in RUN state to update
- * the rate adaptation statistics.
- */
-static void
-rt2560_update_rssadapt(void *arg)
-{
- struct rt2560_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- RAL_LOCK(sc);
-
- ieee80211_iterate_nodes(&ic->ic_sta, rt2560_iter_func, arg);
- callout_reset(&sc->rssadapt_ch, hz / 10, rt2560_update_rssadapt, sc);
-
- RAL_UNLOCK(sc);
+ ieee80211_amrr_node_init(&RT2560_VAP(vap)->amrr,
+ &RT2560_NODE(ni)->amrr, ni);
}
static int
-rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rt2560_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
- struct rt2560_softc *sc = ic->ic_ifp->if_softc;
- enum ieee80211_state ostate;
- struct ieee80211_node *ni;
- struct mbuf *m;
- int error = 0;
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct rt2560_softc *sc = ifp->if_softc;
+ int error;
- ostate = ic->ic_state;
+ if (nstate == IEEE80211_S_INIT && vap->iv_state == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ RAL_WRITE(sc, RT2560_CSR14, 0);
- switch (nstate) {
- case IEEE80211_S_INIT:
- callout_stop(&sc->rssadapt_ch);
+ /* turn association led off */
+ rt2560_update_led(sc, 0, 0);
+ }
- if (ostate == IEEE80211_S_RUN) {
- /* abort TSF synchronization */
- RAL_WRITE(sc, RT2560_CSR14, 0);
+ error = rvp->ral_newstate(vap, nstate, arg);
- /* turn association led off */
- rt2560_update_led(sc, 0, 0);
- }
- break;
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mbuf *m;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rt2560_update_plcp(sc);
rt2560_set_basicrates(sc);
rt2560_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- m = ieee80211_beacon_alloc(ni, &sc->sc_bo);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &rvp->ral_bo);
if (m == NULL) {
- device_printf(sc->sc_dev,
- "could not allocate beacon\n");
- error = ENOBUFS;
- break;
+ if_printf(ifp, "could not allocate beacon\n");
+ return ENOBUFS;
}
-
ieee80211_ref_node(ni);
error = rt2560_tx_bcn(sc, m, ni);
if (error != 0)
- break;
+ return error;
}
/* turn assocation led on */
rt2560_update_led(sc, 1, 0);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- callout_reset(&sc->rssadapt_ch, hz / 10,
- rt2560_update_rssadapt, sc);
-
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ /* fake a join to init the tx rate */
+ rt2560_newassoc(ni, 1);
+ }
rt2560_enable_tsf_sync(sc);
}
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- default:
- break;
}
-
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+ return error;
}
/*
@@ -912,8 +937,8 @@ rt2560_encryption_intr(struct rt2560_softc *sc)
desc->flags |= htole32(RT2560_TX_VALID);
desc->flags |= htole32(RT2560_TX_BUSY);
- DPRINTFN(15, ("encryption done idx=%u\n",
- sc->txq.next_encrypt));
+ DPRINTFN(sc, 15, "encryption done idx=%u\n",
+ sc->txq.next_encrypt);
sc->txq.next_encrypt =
(sc->txq.next_encrypt + 1) % RT2560_TX_RING_COUNT;
@@ -929,11 +954,13 @@ rt2560_encryption_intr(struct rt2560_softc *sc)
static void
rt2560_tx_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct rt2560_node *rn;
+ struct mbuf *m;
+ uint32_t flags;
+ int retrycnt;
bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
BUS_DMASYNC_POSTREAD);
@@ -942,36 +969,43 @@ rt2560_tx_intr(struct rt2560_softc *sc)
desc = &sc->txq.desc[sc->txq.next];
data = &sc->txq.data[sc->txq.next];
- if ((le32toh(desc->flags) & RT2560_TX_BUSY) ||
- (le32toh(desc->flags) & RT2560_TX_CIPHER_BUSY) ||
- !(le32toh(desc->flags) & RT2560_TX_VALID))
+ flags = le32toh(desc->flags);
+ if ((flags & RT2560_TX_BUSY) ||
+ (flags & RT2560_TX_CIPHER_BUSY) ||
+ !(flags & RT2560_TX_VALID))
break;
rn = (struct rt2560_node *)data->ni;
+ m = data->m;
- switch (le32toh(desc->flags) & RT2560_TX_RESULT_MASK) {
+ switch (flags & RT2560_TX_RESULT_MASK) {
case RT2560_TX_SUCCESS:
- DPRINTFN(10, ("data frame sent successfully\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_raise_rate(ic, &rn->rssadapt,
- &data->id);
- }
+ DPRINTFN(sc, 10, "%s\n", "data frame sent successfully");
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, 0);
ifp->if_opackets++;
break;
case RT2560_TX_SUCCESS_RETRY:
- DPRINTFN(9, ("data frame sent after %u retries\n",
- (le32toh(desc->flags) >> 5) & 0x7));
+ retrycnt = RT2560_TX_RETRYCNT(flags);
+
+ DPRINTFN(sc, 9, "data frame sent after %u retries\n",
+ retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, retrycnt);
ifp->if_opackets++;
break;
case RT2560_TX_FAIL_RETRY:
- DPRINTFN(9, ("sending data frame failed (too much "
- "retries)\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_lower_rate(ic, data->ni,
- &rn->rssadapt, &data->id);
- }
+ retrycnt = RT2560_TX_RETRYCNT(flags);
+
+ DPRINTFN(sc, 9, "data frame failed after %d retries\n",
+ retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_FAILURE, retrycnt);
ifp->if_oerrors++;
break;
@@ -979,14 +1013,14 @@ rt2560_tx_intr(struct rt2560_softc *sc)
case RT2560_TX_FAIL_OTHER:
default:
device_printf(sc->sc_dev, "sending data frame failed "
- "0x%08x\n", le32toh(desc->flags));
+ "0x%08x\n", flags);
ifp->if_oerrors++;
}
bus_dmamap_sync(sc->txq.data_dmat, data->map,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->txq.data_dmat, data->map);
- m_freem(data->m);
+ m_freem(m);
data->m = NULL;
ieee80211_free_node(data->ni);
data->ni = NULL;
@@ -994,7 +1028,7 @@ rt2560_tx_intr(struct rt2560_softc *sc)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2560_TX_VALID);
- DPRINTFN(15, ("tx done idx=%u\n", sc->txq.next));
+ DPRINTFN(sc, 15, "tx done idx=%u\n", sc->txq.next);
sc->txq.queued--;
sc->txq.next = (sc->txq.next + 1) % RT2560_TX_RING_COUNT;
@@ -1011,15 +1045,14 @@ rt2560_tx_intr(struct rt2560_softc *sc)
if ((sc->sc_flags &
(RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2560_start(ifp);
+ rt2560_start_locked(ifp);
}
}
static void
rt2560_prio_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct ieee80211_node *ni;
@@ -1039,17 +1072,17 @@ rt2560_prio_intr(struct rt2560_softc *sc)
switch (flags & RT2560_TX_RESULT_MASK) {
case RT2560_TX_SUCCESS:
- DPRINTFN(10, ("mgt frame sent successfully\n"));
+ DPRINTFN(sc, 10, "%s\n", "mgt frame sent successfully");
break;
case RT2560_TX_SUCCESS_RETRY:
- DPRINTFN(9, ("mgt frame sent after %u retries\n",
- (flags >> 5) & 0x7));
+ DPRINTFN(sc, 9, "mgt frame sent after %u retries\n",
+ (flags >> 5) & 0x7);
break;
case RT2560_TX_FAIL_RETRY:
- DPRINTFN(9, ("sending mgt frame failed (too much "
- "retries)\n"));
+ DPRINTFN(sc, 9, "%s\n",
+ "sending mgt frame failed (too much retries)");
break;
case RT2560_TX_FAIL_INVALID:
@@ -1072,7 +1105,7 @@ rt2560_prio_intr(struct rt2560_softc *sc)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2560_TX_VALID);
- DPRINTFN(15, ("prio done idx=%u\n", sc->prioq.next));
+ DPRINTFN(sc, 15, "prio done idx=%u\n", sc->prioq.next);
sc->prioq.queued--;
sc->prioq.next = (sc->prioq.next + 1) % RT2560_PRIO_RING_COUNT;
@@ -1096,25 +1129,24 @@ rt2560_prio_intr(struct rt2560_softc *sc)
if ((sc->sc_flags &
(RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2560_start(ifp);
+ rt2560_start_locked(ifp);
}
}
/*
* Some frames were processed by the hardware cipher engine and are ready for
- * transmission to the IEEE802.11 layer.
+ * handoff to the IEEE802.11 layer.
*/
static void
rt2560_decryption_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2560_rx_desc *desc;
struct rt2560_rx_data *data;
bus_addr_t physaddr;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct rt2560_node *rn;
struct mbuf *mnew, *m;
int hw, error;
@@ -1193,7 +1225,7 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
m->m_pkthdr.len = m->m_len =
(le32toh(desc->flags) >> 16) & 0xfff;
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_rx_radiotap_header *tap = &sc->sc_rxtap;
uint32_t tsf_lo, tsf_hi;
@@ -1204,13 +1236,12 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
tap->wr_tsf =
htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
tap->wr_flags = 0;
- tap->wr_rate = rt2560_rxrate(desc);
- tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2560_RX_OFDM);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = RT2560_RSSI(sc, desc->rssi);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
sc->sc_flags |= RT2560_F_INPUT_RUNNING;
@@ -1218,24 +1249,19 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
wh = mtod(m, struct ieee80211_frame *);
ni = ieee80211_find_rxnode(ic,
(struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, RT2560_RSSI(sc, desc->rssi),
- RT2560_NOISE_FLOOR, 0);
-
- /* give rssi to the rate adatation algorithm */
- rn = (struct rt2560_node *)ni;
- ral_rssadapt_input(ic, ni, &rn->rssadapt,
- RT2560_RSSI(sc, desc->rssi));
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m,
+ RT2560_RSSI(sc, desc->rssi), RT2560_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m,
+ RT2560_RSSI(sc, desc->rssi), RT2560_NOISE_FLOOR, 0);
RAL_LOCK(sc);
sc->sc_flags &= ~RT2560_F_INPUT_RUNNING;
skip: desc->flags = htole32(RT2560_RX_BUSY);
- DPRINTFN(15, ("decryption done idx=%u\n", sc->rxq.cur_decrypt));
+ DPRINTFN(sc, 15, "decryption done idx=%u\n", sc->rxq.cur_decrypt);
sc->rxq.cur_decrypt =
(sc->rxq.cur_decrypt + 1) % RT2560_RX_RING_COUNT;
@@ -1274,20 +1300,20 @@ rt2560_rx_intr(struct rt2560_softc *sc)
* This should not happen since we did not request
* to receive those frames when we filled RXCSR0.
*/
- DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
- le32toh(desc->flags)));
+ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n",
+ le32toh(desc->flags));
data->drop = 1;
}
if (((le32toh(desc->flags) >> 16) & 0xfff) > MCLBYTES) {
- DPRINTFN(5, ("bad length\n"));
+ DPRINTFN(sc, 5, "%s\n", "bad length");
data->drop = 1;
}
/* mark the frame for decryption */
desc->flags |= htole32(RT2560_RX_CIPHER_BUSY);
- DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
+ DPRINTFN(sc, 15, "rx done idx=%u\n", sc->rxq.cur);
sc->rxq.cur = (sc->rxq.cur + 1) % RT2560_RX_RING_COUNT;
}
@@ -1300,10 +1326,10 @@ rt2560_rx_intr(struct rt2560_softc *sc)
}
static void
-rt2560_beacon_update(struct ieee80211com *ic, int item)
+rt2560_beacon_update(struct ieee80211vap *vap, int item)
{
- struct rt2560_softc *sc = ic->ic_ifp->if_softc;
- struct ieee80211_beacon_offsets *bo = &sc->sc_bo;
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+ struct ieee80211_beacon_offsets *bo = &rvp->ral_bo;
setbit(bo->bo_flags, item);
}
@@ -1315,7 +1341,10 @@ rt2560_beacon_update(struct ieee80211com *ic, int item)
static void
rt2560_beacon_expire(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
struct rt2560_tx_data *data;
if (ic->ic_opmode != IEEE80211_M_IBSS &&
@@ -1332,14 +1361,12 @@ rt2560_beacon_expire(struct rt2560_softc *sc)
bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->bcnq.data_dmat, data->map);
- ieee80211_beacon_update(data->ni, &sc->sc_bo, data->m, 1);
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, data->m);
+ /* XXX 1 =>'s mcast frames which means all PS sta's will wakeup! */
+ ieee80211_beacon_update(data->ni, &rvp->ral_bo, data->m, 1);
rt2560_tx_bcn(sc, data->m, data->ni);
- DPRINTFN(15, ("beacon expired\n"));
+ DPRINTFN(sc, 15, "%s", "beacon expired\n");
sc->bcnq.next = (sc->bcnq.next + 1) % RT2560_BEACON_RING_COUNT;
}
@@ -1348,7 +1375,7 @@ rt2560_beacon_expire(struct rt2560_softc *sc)
static void
rt2560_wakeup_expire(struct rt2560_softc *sc)
{
- DPRINTFN(2, ("wakeup expired\n"));
+ DPRINTFN(sc, 2, "%s", "wakeup expired\n");
}
void
@@ -1401,137 +1428,16 @@ rt2560_intr(void *arg)
RAL_UNLOCK(sc);
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
#define RAL_SIFS 10 /* us */
#define RT2560_TXRX_TURNAROUND 10 /* us */
-/*
- * This function is only used by the Rx radiotap code.
- */
-static uint8_t
-rt2560_rxrate(struct rt2560_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2560_RX_OFDM) {
- /* reverse function of rt2560_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-rt2560_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rt2560_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
-
- return txtime;
-}
-
-static uint8_t
-rt2560_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc,
uint32_t flags, int len, int rate, int encrypt, bus_addr_t physaddr)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1545,11 +1451,11 @@ rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc,
RT2560_LOGCWMAX(8));
/* setup PLCP fields */
- desc->plcp_signal = rt2560_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2560_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1579,7 +1485,9 @@ static int
rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
@@ -1588,7 +1496,8 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
desc = &sc->bcnq.desc[sc->bcnq.cur];
data = &sc->bcnq.data[sc->bcnq.cur];
- rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2;
+ /* XXX maybe a separate beacon rate? */
+ rate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].mgmtrate;
error = bus_dmamap_load_mbuf_sg(sc->bcnq.data_dmat, data->map, m0,
segs, &nsegs, BUS_DMA_NOWAIT);
@@ -1599,7 +1508,7 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1608,7 +1517,7 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
@@ -1617,8 +1526,8 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
rt2560_setup_tx_desc(sc, desc, RT2560_TX_IFS_NEWBACKOFF |
RT2560_TX_TIMESTAMP, m0->m_pkthdr.len, rate, 0, segs->ds_addr);
- DPRINTFN(10, ("sending beacon frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->bcnq.cur, rate));
+ DPRINTFN(sc, 10, "sending beacon frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->bcnq.cur, rate);
bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->bcnq.desc_dmat, sc->bcnq.desc_map,
@@ -1633,7 +1542,9 @@ static int
rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct ieee80211_frame *wh;
@@ -1646,12 +1557,12 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
desc = &sc->prioq.desc[sc->prioq.cur];
data = &sc->prioq.data[sc->prioq.cur];
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1667,7 +1578,7 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1676,19 +1587,21 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
+ /* management frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
wh = mtod(m0, struct ieee80211_frame *);
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2560_TX_ACK;
- dur = rt2560_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
- RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1706,8 +1619,8 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->prioq.cur, rate));
+ DPRINTFN(sc, 10, "sending mgt frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->prioq.cur, rate);
/* kick prio */
sc->prioq.queued++;
@@ -1718,10 +1631,80 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
}
static int
+rt2560_sendprot(struct rt2560_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rt2560_tx_desc *desc;
+ struct rt2560_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort, error;
+ uint16_t dur;
+ bus_dma_segment_t segs[RT2560_MAX_SCATTER];
+ int nsegs;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2560_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2560_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+
+ desc = &sc->txq.desc[sc->txq.cur_encrypt];
+ data = &sc->txq.data[sc->txq.cur_encrypt];
+
+ error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map,
+ mprot, segs, &nsegs, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not map mbuf (error %d)\n", error);
+ m_freem(mprot);
+ return error;
+ }
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ /* ctl frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
+
+ rt2560_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate, 1,
+ segs->ds_addr);
+
+ bus_dmamap_sync(sc->txq.data_dmat, data->map,
+ BUS_DMASYNC_PREWRITE);
+
+ sc->txq.queued++;
+ sc->txq.cur_encrypt = (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
+
+ return 0;
+}
+
+static int
rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
@@ -1734,10 +1717,26 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
/* XXX validate */
if (rate == 0) {
+ /* XXX fall back to mcast/mgmt rate? */
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2560_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rt2560_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
+ }
+
error = bus_dmamap_load_mbuf_sg(sc->prioq.data_dmat, data->map, m0,
segs, &nsegs, 0);
if (error != 0) {
@@ -1747,7 +1746,7 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1756,16 +1755,12 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RT2560_TX_ACK;
-
/* XXX need to setup descriptor ourself */
rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len,
rate, (params->ibp_flags & IEEE80211_BPF_CRYPTO) != 0,
@@ -1775,8 +1770,8 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending raw frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->prioq.cur, rate));
+ DPRINTFN(sc, 10, "sending raw frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->prioq.cur, rate);
/* kick prio */
sc->prioq.queued++;
@@ -1786,70 +1781,40 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
return 0;
}
-/*
- * Build a RTS control frame.
- */
-static struct mbuf *
-rt2560_get_rts(struct rt2560_softc *sc, struct ieee80211_frame *wh,
- uint16_t dur)
-{
- struct ieee80211_frame_rts *rts;
- struct mbuf *m;
-
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL) {
- sc->sc_ic.ic_stats.is_tx_nobuf++;
- device_printf(sc->sc_dev, "could not allocate RTS frame\n");
- return NULL;
- }
-
- rts = mtod(m, struct ieee80211_frame_rts *);
-
- rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
- IEEE80211_FC0_SUBTYPE_RTS;
- rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- *(uint16_t *)rts->i_dur = htole16(dur);
- IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
- IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
-
- m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts);
-
- return m;
-}
-
static int
rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
- struct rt2560_node *rn;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
struct mbuf *mnew;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
uint16_t dur;
- uint32_t flags = 0;
+ uint32_t flags;
int nsegs, rate, error;
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ } else if (m0->m_flags & M_EAPOL) {
+ rate = tp->mgmtrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- struct ieee80211_rateset *rs;
-
- rs = &ni->ni_rates;
- rn = (struct rt2560_node *)ni;
- ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs, wh,
- m0->m_pkthdr.len, NULL, 0);
- rate = rs->rs_rates[ni->ni_txrate];
+ (void) ieee80211_amrr_choose(ni, &RT2560_NODE(ni)->amrr);
+ rate = ni->ni_txrate;
}
- rate &= IEEE80211_RATE_VAL;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1859,66 +1824,22 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- /*
- * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
- * for directed frames only when the length of the MPDU is greater
- * than the length threshold indicated by [...]" ic_rtsthreshold.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- m0->m_pkthdr.len > ic->ic_rtsthreshold) {
- struct mbuf *m;
- uint16_t dur;
- int rtsrate, ackrate;
-
- rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
- ackrate = rt2560_ack_rate(ic, rate);
-
- dur = rt2560_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
- rt2560_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
- rt2560_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
- 3 * RAL_SIFS;
-
- m = rt2560_get_rts(sc, wh, dur);
-
- desc = &sc->txq.desc[sc->txq.cur_encrypt];
- data = &sc->txq.data[sc->txq.cur_encrypt];
-
- error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map,
- m, segs, &nsegs, 0);
- if (error != 0) {
- device_printf(sc->sc_dev,
- "could not map mbuf (error %d)\n", error);
- m_freem(m);
- m_freem(m0);
- return error;
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rt2560_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
}
-
- /* avoid multiple free() of the same node for each fragment */
- ieee80211_ref_node(ni);
-
- data->m = m;
- data->ni = ni;
-
- /* RTS frames are not taken into account for rssadapt */
- data->id.id_node = NULL;
-
- rt2560_setup_tx_desc(sc, desc, RT2560_TX_ACK |
- RT2560_TX_MORE_FRAG, m->m_pkthdr.len, rtsrate, 1,
- segs->ds_addr);
-
- bus_dmamap_sync(sc->txq.data_dmat, data->map,
- BUS_DMASYNC_PREWRITE);
-
- sc->txq.queued++;
- sc->txq.cur_encrypt =
- (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
-
- /*
- * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
- * asynchronous data frame shall be transmitted after the CTS
- * frame and a SIFS period.
- */
- flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
}
data = &sc->txq.data[sc->txq.cur_encrypt];
@@ -1955,35 +1876,32 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
/* remember link conditions for rate adaptation algorithm */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- data->id.id_len = m0->m_pkthdr.len;
- data->id.id_rateidx = ni->ni_txrate;
- data->id.id_node = ni;
- data->id.id_rssi = ni->ni_rssi;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ data->rix = ni->ni_txrate;
+ /* XXX probably need last rssi value and not avg */
+ data->rssi = ic->ic_node_getrssi(ni);
} else
- data->id.id_node = NULL;
+ data->rix = IEEE80211_FIXED_RATE_NONE;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2560_TX_ACK;
- dur = rt2560_txtime(RAL_ACK_SIZE, rt2560_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
@@ -1994,8 +1912,8 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->txq.cur_encrypt, rate));
+ DPRINTFN(sc, 10, "sending data frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->txq.cur_encrypt, rate);
/* kick encrypt */
sc->txq.queued++;
@@ -2006,113 +1924,50 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
}
static void
-rt2560_start(struct ifnet *ifp)
+rt2560_start_locked(struct ifnet *ifp)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
- RAL_LOCK(sc);
-
- /* prevent management frames from being sent if we're not ready */
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- RAL_UNLOCK(sc);
- return;
- }
+ RAL_LOCK_ASSERT(sc);
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->sc_flags |= RT2560_F_PRIO_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rt2560_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->sc_flags |= RT2560_F_DATA_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m0->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m0);
- /*
- * If we're in power save mode 'cuz of a bg
- * scan cancel it so the traffic can flow.
- * The packet we just queued will automatically
- * get sent when we drop out of power save.
- * XXX locking
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
- ieee80211_free_node(ni);
- continue;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->sc_flags |= RT2560_F_DATA_OACTIVE;
+ break;
+ }
- BPF_MTAP(ifp, m0);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rt2560_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ if (rt2560_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
+static void
+rt2560_start(struct ifnet *ifp)
+{
+ struct rt2560_softc *sc = ifp->if_softc;
+
+ RAL_LOCK(sc);
+ rt2560_start_locked(ifp);
RAL_UNLOCK(sc);
}
@@ -2122,81 +1977,60 @@ rt2560_watchdog(void *arg)
struct rt2560_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ RAL_LOCK_ASSERT(sc);
+
+ KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
+
+ if (sc->sc_invalid) /* card ejected */
return;
rt2560_encryption_intr(sc);
rt2560_tx_intr(sc);
- if (sc->sc_tx_timer > 0) {
- if (--sc->sc_tx_timer == 0) {
- device_printf(sc->sc_dev, "device timeout\n");
- rt2560_init(sc);
- ifp->if_oerrors++;
- /* watchdog timeout is set in rt2560_init() */
- return;
- }
+ if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ rt2560_init_locked(sc);
+ ifp->if_oerrors++;
+ /* NB: callout is reset in rt2560_init() */
+ return;
}
callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc);
}
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-rt2560_reset(struct ifnet *ifp)
-{
- struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
-
- rt2560_set_chan(sc, ic->ic_curchan);
-
- return 0;
-}
-
static int
rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
-
-
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ RAL_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- RAL_LOCK(sc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2560_update_promisc(sc);
- else
- rt2560_init(sc);
- RAL_UNLOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rt2560_init_locked(sc);
+ startall = 1;
+ } else
+ rt2560_update_promisc(ifp);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2560_stop(sc);
+ rt2560_stop_locked(sc);
}
-
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rt2560_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
+ RAL_UNLOCK(sc);
-
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2219,7 +2053,7 @@ rt2560_bbp_write(struct rt2560_softc *sc, uint8_t reg, uint8_t val)
tmp = RT2560_BBP_WRITE | RT2560_BBP_BUSY | reg << 8 | val;
RAL_WRITE(sc, RT2560_BBPCSR, tmp);
- DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+ DPRINTFN(sc, 15, "BBP R%u <- 0x%02x\n", reg, val);
}
static uint8_t
@@ -2275,19 +2109,21 @@ rt2560_rf_write(struct rt2560_softc *sc, uint8_t reg, uint32_t val)
/* remember last written value in sc */
sc->rf_regs[reg] = val;
- DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
+ DPRINTFN(sc, 15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff);
}
static void
rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t power, tmp;
u_int i, chan;
chan = ieee80211_chan2ieee(ic, c);
- if (chan == 0 || chan == IEEE80211_CHAN_ANY)
- return;
+ KASSERT(chan != 0 && chan != IEEE80211_CHAN_ANY, ("chan 0x%x", chan));
+
+ sc->sc_rates = ieee80211_get_ratetable(c);
if (IEEE80211_IS_CHAN_2GHZ(c))
power = min(sc->txpow[chan - 1], 31);
@@ -2297,7 +2133,7 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
/* adjust txpower using ifconfig settings */
power -= (100 - ic->ic_txpowlimit) / 8;
- DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power));
+ DPRINTFN(sc, 2, "setting channel to %u, txpower to %u\n", chan, power);
switch (sc->rf_rev) {
case RT2560_RF_2522:
@@ -2362,7 +2198,8 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
printf("unknown ral rev=%d\n", sc->rf_rev);
}
- if (ic->ic_state != IEEE80211_S_SCAN) {
+ /* XXX */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
/* set Japan filter bit for channel 14 */
tmp = rt2560_bbp_read(sc, 70);
@@ -2385,6 +2222,11 @@ rt2560_set_channel(struct ieee80211com *ic)
RAL_LOCK(sc);
rt2560_set_chan(sc, ic->ic_curchan);
+
+ sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ sc->sc_rxtap.wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
RAL_UNLOCK(sc);
}
@@ -2406,7 +2248,7 @@ rt2560_disable_rf_tune(struct rt2560_softc *sc)
tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
rt2560_rf_write(sc, RAL_RF3, tmp);
- DPRINTFN(2, ("disabling RF autotune\n"));
+ DPRINTFN(sc, 2, "%s", "disabling RF autotune\n");
}
#endif
@@ -2417,20 +2259,22 @@ rt2560_disable_rf_tune(struct rt2560_softc *sc)
static void
rt2560_enable_tsf_sync(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint16_t logcwmin, preload;
uint32_t tmp;
/* first, disable TSF synchronization */
RAL_WRITE(sc, RT2560_CSR14, 0);
- tmp = 16 * ic->ic_bss->ni_intval;
+ tmp = 16 * vap->iv_bss->ni_intval;
RAL_WRITE(sc, RT2560_CSR12, tmp);
RAL_WRITE(sc, RT2560_CSR13, 0);
logcwmin = 5;
- preload = (ic->ic_opmode == IEEE80211_M_STA) ? 384 : 1024;
+ preload = (vap->iv_opmode == IEEE80211_M_STA) ? 384 : 1024;
tmp = logcwmin << 16 | preload;
RAL_WRITE(sc, RT2560_BCNOCSR, tmp);
@@ -2443,13 +2287,14 @@ rt2560_enable_tsf_sync(struct rt2560_softc *sc)
RT2560_ENABLE_BEACON_GENERATOR;
RAL_WRITE(sc, RT2560_CSR14, tmp);
- DPRINTF(("enabling TSF synchronization\n"));
+ DPRINTF(sc, "%s", "enabling TSF synchronization\n");
}
static void
rt2560_update_plcp(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* no short preamble for 1Mbps */
RAL_WRITE(sc, RT2560_PLCP1MCSR, 0x00700400);
@@ -2466,8 +2311,8 @@ rt2560_update_plcp(struct rt2560_softc *sc)
RAL_WRITE(sc, RT2560_PLCP11MCSR, 0x000b840b);
}
- DPRINTF(("updating PLCP for %s preamble\n",
- (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long"));
+ DPRINTF(sc, "updating PLCP for %s preamble\n",
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long");
}
/*
@@ -2478,7 +2323,7 @@ static void
rt2560_update_slot(struct ifnet *ifp)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint16_t tx_sifs, tx_pifs, tx_difs, eifs;
uint32_t tmp;
@@ -2521,13 +2366,14 @@ rt2560_update_slot(struct ifnet *ifp)
tmp = eifs << 16 | tx_difs;
RAL_WRITE(sc, RT2560_CSR19, tmp);
- DPRINTF(("setting slottime to %uus\n", slottime));
+ DPRINTF(sc, "setting slottime to %uus\n", slottime);
}
static void
rt2560_set_basicrates(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* update basic rate set */
if (ic->ic_curmode == IEEE80211_MODE_11B) {
@@ -2563,7 +2409,7 @@ rt2560_set_bssid(struct rt2560_softc *sc, const uint8_t *bssid)
tmp = bssid[4] | bssid[5] << 8;
RAL_WRITE(sc, RT2560_CSR6, tmp);
- DPRINTF(("setting BSSID to %6D\n", bssid, ":"));
+ DPRINTF(sc, "setting BSSID to %6D\n", bssid, ":");
}
static void
@@ -2577,7 +2423,7 @@ rt2560_set_macaddr(struct rt2560_softc *sc, uint8_t *addr)
tmp = addr[4] | addr[5] << 8;
RAL_WRITE(sc, RT2560_CSR4, tmp);
- DPRINTF(("setting MAC address to %6D\n", addr, ":"));
+ DPRINTF(sc, "setting MAC address to %6D\n", addr, ":");
}
static void
@@ -2597,9 +2443,9 @@ rt2560_get_macaddr(struct rt2560_softc *sc, uint8_t *addr)
}
static void
-rt2560_update_promisc(struct rt2560_softc *sc)
+rt2560_update_promisc(struct ifnet *ifp)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct rt2560_softc *sc = ifp->if_softc;
uint32_t tmp;
tmp = RAL_READ(sc, RT2560_RXCSR0);
@@ -2610,8 +2456,8 @@ rt2560_update_promisc(struct rt2560_softc *sc)
RAL_WRITE(sc, RT2560_RXCSR0, tmp);
- DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
- "entering" : "leaving"));
+ DPRINTF(sc, "%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
}
static const char *
@@ -2669,8 +2515,8 @@ rt2560_read_config(struct rt2560_softc *sc)
sc->rssi_corr = RT2560_DEFAULT_RSSI_CORR;
else
sc->rssi_corr = val & 0xff;
- DPRINTF(("rssi correction %d, calibrate 0x%02x\n",
- sc->rssi_corr, val));
+ DPRINTF(sc, "rssi correction %d, calibrate 0x%02x\n",
+ sc->rssi_corr, val);
}
@@ -2690,10 +2536,11 @@ rt2560_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct rt2560_softc *sc = ifp->if_softc;
+ struct ieee80211vap *vap = ic->ic_scan->ss_vap;
rt2560_enable_tsf_sync(sc);
/* XXX keep local copy */
- rt2560_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rt2560_set_bssid(sc, vap->iv_bss->ni_bssid);
}
static int
@@ -2779,20 +2626,18 @@ rt2560_set_rxantenna(struct rt2560_softc *sc, int antenna)
}
static void
-rt2560_init(void *priv)
+rt2560_init_locked(struct rt2560_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rt2560_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
int i;
+ RAL_LOCK_ASSERT(sc);
+ rt2560_stop_locked(sc);
- rt2560_stop(sc);
-
- RAL_LOCK(sc);
/* setup tx rings */
tmp = RT2560_PRIO_RING_COUNT << 24 |
RT2560_ATIM_RING_COUNT << 16 |
@@ -2866,35 +2711,38 @@ rt2560_init(void *priv)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc);
+#undef N
+}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+static void
+rt2560_init(void *priv)
+{
+ struct rt2560_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ RAL_LOCK(sc);
+ rt2560_init_locked(sc);
RAL_UNLOCK(sc);
-#undef N
+
+ ieee80211_start_all(ic);
}
-void
-rt2560_stop(void *arg)
+static void
+rt2560_stop_locked(struct rt2560_softc *sc)
{
- struct rt2560_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
volatile int *flags = &sc->sc_flags;
- while (*flags & RT2560_F_INPUT_RUNNING) {
- tsleep(sc, 0, "ralrunning", hz/10);
- }
+ RAL_LOCK_ASSERT(sc);
- RAL_LOCK(sc);
+ while (*flags & RT2560_F_INPUT_RUNNING)
+ msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10);
callout_stop(&sc->watchdog_ch);
+ sc->sc_tx_timer = 0;
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
/* abort Tx */
@@ -2917,9 +2765,16 @@ rt2560_stop(void *arg)
rt2560_reset_tx_ring(sc, &sc->bcnq);
rt2560_reset_rx_ring(sc, &sc->rxq);
}
- sc->sc_tx_timer = 0;
sc->sc_flags &= ~(RT2560_F_PRIO_OACTIVE | RT2560_F_DATA_OACTIVE);
+}
+void
+rt2560_stop(void *arg)
+{
+ struct rt2560_softc *sc = arg;
+
+ RAL_LOCK(sc);
+ rt2560_stop_locked(sc);
RAL_UNLOCK(sc);
}
@@ -2942,15 +2797,13 @@ rt2560_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
}
if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->sc_flags |= RT2560_F_PRIO_OACTIVE;
RAL_UNLOCK(sc);
m_freem(m);
ieee80211_free_node(ni);
return ENOBUFS; /* XXX */
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
diff --git a/sys/dev/ral/rt2560reg.h b/sys/dev/ral/rt2560reg.h
index bab455d..8e501b4 100644
--- a/sys/dev/ral/rt2560reg.h
+++ b/sys/dev/ral/rt2560reg.h
@@ -208,6 +208,8 @@ struct rt2560_tx_desc {
#define RT2560_TX_CIPHER_TKIP (3 << 29)
#define RT2560_TX_CIPHER_AES (4 << 29)
+#define RT2560_TX_RETRYCNT(v) (((v) >> 5) & 0x7)
+
uint32_t physaddr;
uint16_t wme;
#define RT2560_LOGCWMAX(x) (((x) & 0xf) << 12)
diff --git a/sys/dev/ral/rt2560var.h b/sys/dev/ral/rt2560var.h
index 1f68311..0841444 100644
--- a/sys/dev/ral/rt2560var.h
+++ b/sys/dev/ral/rt2560var.h
@@ -55,7 +55,8 @@ struct rt2560_tx_data {
bus_dmamap_t map;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ral_rssdesc id;
+ uint8_t rix;
+ int8_t rssi;
};
struct rt2560_tx_ring {
@@ -94,14 +95,22 @@ struct rt2560_rx_ring {
struct rt2560_node {
struct ieee80211_node ni;
- struct ral_rssadapt rssadapt;
+ struct ieee80211_amrr_node amrr;
};
+#define RT2560_NODE(ni) ((struct rt2560_node *)(ni))
+
+struct rt2560_vap {
+ struct ieee80211vap ral_vap;
+ struct ieee80211_beacon_offsets ral_bo;
+ struct ieee80211_amrr amrr;
+
+ int (*ral_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RT2560_VAP(vap) ((struct rt2560_vap *)(vap))
struct rt2560_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
@@ -109,10 +118,12 @@ struct rt2560_softc {
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout rssadapt_ch;
int sc_tx_timer;
int sc_invalid;
+ int sc_debug;
+
+ const struct ieee80211_rate_table *sc_rates;
/*
* The same in both up to here
* ------------------------------------------------
@@ -128,8 +139,6 @@ struct rt2560_softc {
struct rt2560_tx_ring bcnq;
struct rt2560_rx_ring rxq;
- struct ieee80211_beacon_offsets sc_bo;
-
uint32_t rf_regs[4];
uint8_t txpow[14];
@@ -144,22 +153,10 @@ struct rt2560_softc {
int tx_ant;
int nb_ant;
- int dwelltime;
-
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rt2560_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rt2560_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct rt2560_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rt2560_tx_radiotap_header sc_txtap;
int sc_txtap_len;
#define RT2560_F_INPUT_RUNNING 0x1
#define RT2560_F_PRIO_OACTIVE 0x2
@@ -173,5 +170,6 @@ void rt2560_stop(void *);
void rt2560_resume(void *);
void rt2560_intr(void *);
-#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c
index 025646d..c94dbe7 100644
--- a/sys/dev/ral/rt2661.c
+++ b/sys/dev/ral/rt2661.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
+#include <sys/firmware.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -52,8 +53,10 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_amrr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -61,21 +64,29 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip.h>
#include <netinet/if_ether.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2661reg.h>
#include <dev/ral/rt2661var.h>
-#include <dev/ral/rt2661_ucode.h>
+#define RAL_DEBUG
#ifdef RAL_DEBUG
-#define DPRINTF(x) do { if (ral_debug > 0) printf x; } while (0)
-#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
-int ral_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ral, CTLFLAG_RW, &ral_debug, 0, "ral debug level");
+#define DPRINTF(sc, fmt, ...) do { \
+ if (sc->sc_debug > 0) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#define DPRINTFN(sc, n, fmt, ...) do { \
+ if (sc->sc_debug >= (n)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
#else
-#define DPRINTF(x)
-#define DPRINTFN(n, x)
+#define DPRINTF(sc, fmt, ...)
+#define DPRINTFN(sc, n, fmt, ...)
#endif
+static struct ieee80211vap *rt2661_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rt2661_vap_delete(struct ieee80211vap *);
static void rt2661_dma_map_addr(void *, bus_dma_segment_t *, int,
int);
static int rt2661_alloc_tx_ring(struct rt2661_softc *,
@@ -92,8 +103,8 @@ static void rt2661_free_rx_ring(struct rt2661_softc *,
struct rt2661_rx_ring *);
static struct ieee80211_node *rt2661_node_alloc(
struct ieee80211_node_table *);
-static int rt2661_media_change(struct ifnet *);
-static int rt2661_newstate(struct ieee80211com *,
+static void rt2661_newassoc(struct ieee80211_node *, int);
+static int rt2661_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static uint16_t rt2661_eeprom_read(struct rt2661_softc *, uint8_t);
static void rt2661_rx_intr(struct rt2661_softc *);
@@ -103,25 +114,21 @@ static void rt2661_tx_dma_intr(struct rt2661_softc *,
static void rt2661_mcu_beacon_expire(struct rt2661_softc *);
static void rt2661_mcu_wakeup(struct rt2661_softc *);
static void rt2661_mcu_cmd_intr(struct rt2661_softc *);
-static int rt2661_ack_rate(struct ieee80211com *, int);
static void rt2661_scan_start(struct ieee80211com *);
static void rt2661_scan_end(struct ieee80211com *);
static void rt2661_set_channel(struct ieee80211com *);
-static uint16_t rt2661_txtime(int, int, uint32_t);
-static uint8_t rt2661_rxrate(struct rt2661_rx_desc *);
-static uint8_t rt2661_plcp_signal(int);
static void rt2661_setup_tx_desc(struct rt2661_softc *,
struct rt2661_tx_desc *, uint32_t, uint16_t, int,
int, const bus_dma_segment_t *, int, int);
-static struct mbuf * rt2661_get_rts(struct rt2661_softc *,
- struct ieee80211_frame *, uint16_t);
static int rt2661_tx_data(struct rt2661_softc *, struct mbuf *,
struct ieee80211_node *, int);
static int rt2661_tx_mgt(struct rt2661_softc *, struct mbuf *,
struct ieee80211_node *);
+static void rt2661_start_locked(struct ifnet *);
static void rt2661_start(struct ifnet *);
+static int rt2661_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void rt2661_watchdog(void *);
-static int rt2661_reset(struct ifnet *);
static int rt2661_ioctl(struct ifnet *, u_long, caddr_t);
static void rt2661_bbp_write(struct rt2661_softc *, uint8_t,
uint8_t);
@@ -143,23 +150,25 @@ static void rt2661_set_bssid(struct rt2661_softc *,
const uint8_t *);
static void rt2661_set_macaddr(struct rt2661_softc *,
const uint8_t *);
-static void rt2661_update_promisc(struct rt2661_softc *);
+static void rt2661_update_promisc(struct ifnet *);
static int rt2661_wme_update(struct ieee80211com *) __unused;
static void rt2661_update_slot(struct ifnet *);
static const char *rt2661_get_rf(int);
-static void rt2661_read_eeprom(struct rt2661_softc *);
+static void rt2661_read_eeprom(struct rt2661_softc *,
+ struct ieee80211com *);
static int rt2661_bbp_init(struct rt2661_softc *);
+static void rt2661_init_locked(struct rt2661_softc *);
static void rt2661_init(void *);
-static void rt2661_stop(void *);
static void rt2661_stop_locked(struct rt2661_softc *);
-static int rt2661_load_microcode(struct rt2661_softc *,
- const uint8_t *, int);
+static void rt2661_stop(void *);
+static int rt2661_load_microcode(struct rt2661_softc *);
#ifdef notyet
static void rt2661_rx_tune(struct rt2661_softc *);
static void rt2661_radar_start(struct rt2661_softc *);
static int rt2661_radar_stop(struct rt2661_softc *);
#endif
-static int rt2661_prepare_beacon(struct rt2661_softc *);
+static int rt2661_prepare_beacon(struct rt2661_softc *,
+ struct ieee80211vap *);
static void rt2661_enable_tsf_sync(struct rt2661_softc *);
static int rt2661_get_rssi(struct rt2661_softc *, uint8_t);
@@ -190,19 +199,26 @@ int
rt2661_attach(device_t dev, int id)
{
struct rt2661_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
uint32_t val;
- const uint8_t *ucode = NULL;
- int bands, error, ac, ntries, size = 0;
+ int error, ac, ntries;
+ uint8_t bands;
+ sc->sc_id = id;
sc->sc_dev = dev;
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ return ENOMEM;
+ }
+ ic = ifp->if_l2com;
+
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
- callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE);
/* wait for NIC to initialize */
for (ntries = 0; ntries < 1000; ntries++) {
@@ -218,36 +234,12 @@ rt2661_attach(device_t dev, int id)
}
/* retrieve RF rev. no and various other things from EEPROM */
- rt2661_read_eeprom(sc);
+ rt2661_read_eeprom(sc, ic);
device_printf(dev, "MAC/BBP RT%X, RF %s\n", val,
rt2661_get_rf(sc->rf_rev));
/*
- * Load 8051 microcode into NIC.
- */
- switch (id) {
- case 0x0301:
- ucode = rt2561s_ucode;
- size = sizeof rt2561s_ucode;
- break;
- case 0x0302:
- ucode = rt2561_ucode;
- size = sizeof rt2561_ucode;
- break;
- case 0x0401:
- ucode = rt2661_ucode;
- size = sizeof rt2661_ucode;
- break;
- }
-
- error = rt2661_load_microcode(sc, ucode, size);
- if (error != 0) {
- device_printf(sc->sc_dev, "could not load 8051 microcode\n");
- goto fail1;
- }
-
- /*
* Allocate Tx and Rx rings.
*/
for (ac = 0; ac < 4; ac++) {
@@ -272,13 +264,6 @@ rt2661_attach(device_t dev, int id)
goto fail3;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- device_printf(sc->sc_dev, "can not if_alloc()\n");
- error = ENOMEM;
- goto fail4;
- }
-
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -290,50 +275,53 @@ rt2661_attach(device_t dev, int id)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
+ | IEEE80211_C_HOSTAP /* hostap mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
#ifdef notyet
- IEEE80211_C_WME | /* 802.11e */
+ | IEEE80211_C_TXFRAG /* handle tx frags */
+ | IEEE80211_C_WME /* 802.11e */
#endif
- IEEE80211_C_BGSCAN | /* bg scanning support */
- IEEE80211_C_WPA; /* 802.11i */
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rt2661_newassoc;
ic->ic_node_alloc = rt2661_node_alloc;
-/* ic->ic_wme.wme_update = rt2661_wme_update;*/
+#if 0
+ ic->ic_wme.wme_update = rt2661_wme_update;
+#endif
ic->ic_scan_start = rt2661_scan_start;
ic->ic_scan_end = rt2661_scan_end;
ic->ic_set_channel = rt2661_set_channel;
ic->ic_updateslot = rt2661_update_slot;
- ic->ic_reset = rt2661_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_update_promisc = rt2661_update_promisc;
+ ic->ic_raw_xmit = rt2661_raw_xmit;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rt2661_newstate;
- ieee80211_media_init(ic, rt2661_media_change, ieee80211_media_status);
+ ic->ic_vap_create = rt2661_vap_create;
+ ic->ic_vap_delete = rt2661_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -343,27 +331,21 @@ rt2661_attach(device_t dev, int id)
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RT2661_TX_RADIOTAP_PRESENT);
-
- /*
- * Add a few sysctl knobs.
- */
- sc->dwelltime = 200;
-
+#ifdef RAL_DEBUG
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs");
+#endif
if (bootverbose)
ieee80211_announce(ic);
return 0;
-fail4: rt2661_free_rx_ring(sc, &sc->rxq);
fail3: rt2661_free_tx_ring(sc, &sc->mgtq);
fail2: while (--ac >= 0)
rt2661_free_tx_ring(sc, &sc->txq[ac]);
fail1: mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
return error;
}
@@ -371,14 +353,12 @@ int
rt2661_detach(void *xsc)
{
struct rt2661_softc *sc = xsc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK(sc);
rt2661_stop_locked(sc);
- callout_stop(&sc->watchdog_ch);
RAL_UNLOCK(sc);
- callout_stop(&sc->rssadapt_ch);
bpfdetach(ifp);
ieee80211_ifdetach(ic);
@@ -397,6 +377,82 @@ rt2661_detach(void *xsc)
return 0;
}
+static struct ieee80211vap *
+rt2661_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2661_vap *rvp;
+ struct ieee80211vap *vap;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "only 1 vap supported\n");
+ return NULL;
+ }
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ break;
+ case IEEE80211_M_WDS:
+ if (TAILQ_EMPTY(&ic->ic_vaps) ||
+ ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ if_printf(ifp, "wds only supported in ap mode\n");
+ return NULL;
+ }
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ default:
+ if_printf(ifp, "unknown opmode %d\n", opmode);
+ return NULL;
+ }
+ rvp = (struct rt2661_vap *) malloc(sizeof(struct rt2661_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->ral_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ rvp->ral_newstate = vap->iv_newstate;
+ vap->iv_newstate = rt2661_newstate;
+#if 0
+ vap->iv_update_beacon = rt2661_beacon_update;
+#endif
+
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /* ms */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ if (TAILQ_FIRST(&ic->ic_vaps) == vap)
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rt2661_vap_delete(struct ieee80211vap *vap)
+{
+ struct rt2661_vap *rvp = RT2661_VAP(vap);
+
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
void
rt2661_shutdown(void *xsc)
{
@@ -417,13 +473,10 @@ void
rt2661_resume(void *xsc)
{
struct rt2661_softc *sc = xsc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
+ if (ifp->if_flags & IFF_UP)
+ rt2661_init(sc);
}
static void
@@ -732,102 +785,58 @@ rt2661_node_alloc(struct ieee80211_node_table *nt)
return (rn != NULL) ? &rn->ni : NULL;
}
-static int
-rt2661_media_change(struct ifnet *ifp)
-{
- struct rt2661_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rt2661_init(sc);
-
- return 0;
-}
-
-/*
- * This function is called for each node present in the node station table.
- */
-static void
-rt2661_iter_func(void *arg, struct ieee80211_node *ni)
-{
- struct rt2661_node *rn = (struct rt2661_node *)ni;
-
- ral_rssadapt_updatestats(&rn->rssadapt);
-}
-
-/*
- * This function is called periodically (every 100ms) in RUN state to update
- * the rate adaptation statistics.
- */
static void
-rt2661_update_rssadapt(void *arg)
+rt2661_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct rt2661_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- RAL_LOCK(sc);
-
- ieee80211_iterate_nodes(&ic->ic_sta, rt2661_iter_func, arg);
- callout_reset(&sc->rssadapt_ch, hz / 10, rt2661_update_rssadapt, sc);
+ struct ieee80211vap *vap = ni->ni_vap;
- RAL_UNLOCK(sc);
+ ieee80211_amrr_node_init(&RT2661_VAP(vap)->amrr,
+ &RT2661_NODE(ni)->amrr, ni);
}
static int
-rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rt2661_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct rt2661_vap *rvp = RT2661_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct rt2661_softc *sc = ic->ic_ifp->if_softc;
- enum ieee80211_state ostate;
- struct ieee80211_node *ni;
- uint32_t tmp;
- int error = 0;
+ int error;
- ostate = ic->ic_state;
+ if (nstate == IEEE80211_S_INIT && vap->iv_state == IEEE80211_S_RUN) {
+ uint32_t tmp;
- switch (nstate) {
- case IEEE80211_S_INIT:
- callout_stop(&sc->rssadapt_ch);
+ /* abort TSF synchronization */
+ tmp = RAL_READ(sc, RT2661_TXRX_CSR9);
+ RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
+ }
- if (ostate == IEEE80211_S_RUN) {
- /* abort TSF synchronization */
- tmp = RAL_READ(sc, RT2661_TXRX_CSR9);
- RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
- }
- break;
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ error = rvp->ral_newstate(vap, nstate, arg);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rt2661_enable_mrr(sc);
rt2661_set_txpreamble(sc);
rt2661_set_basicrates(sc, &ni->ni_rates);
rt2661_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- if ((error = rt2661_prepare_beacon(sc)) != 0)
- break;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ error = rt2661_prepare_beacon(sc, vap);
+ if (error != 0)
+ return error;
}
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- callout_reset(&sc->rssadapt_ch, hz / 10,
- rt2661_update_rssadapt, sc);
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ /* fake a join to init the tx rate */
+ rt2661_newassoc(ni, 1);
+ }
rt2661_enable_tsf_sync(sc);
}
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- default:
- break;
- }
-
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+ }
+ return error;
}
/*
@@ -891,8 +900,7 @@ rt2661_eeprom_read(struct rt2661_softc *sc, uint8_t addr)
static void
rt2661_tx_intr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2661_tx_ring *txq;
struct rt2661_tx_data *data;
struct rt2661_node *rn;
@@ -922,28 +930,28 @@ rt2661_tx_intr(struct rt2661_softc *sc)
if (ni == NULL)
continue;
- rn = (struct rt2661_node *)ni;
+ rn = RT2661_NODE(ni);
switch (RT2661_TX_RESULT(val)) {
case RT2661_TX_SUCCESS:
retrycnt = RT2661_TX_RETRYCNT(val);
- DPRINTFN(10, ("data frame sent successfully after "
- "%d retries\n", retrycnt));
- if (retrycnt == 0 && data->id.id_node != NULL) {
- ral_rssadapt_raise_rate(ic, &rn->rssadapt,
- &data->id);
- }
+ DPRINTFN(sc, 10, "data frame sent successfully after "
+ "%d retries\n", retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, retrycnt);
ifp->if_opackets++;
break;
case RT2661_TX_RETRY_FAIL:
- DPRINTFN(9, ("sending data frame failed (too much "
- "retries)\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_lower_rate(ic, ni,
- &rn->rssadapt, &data->id);
- }
+ retrycnt = RT2661_TX_RETRYCNT(val);
+
+ DPRINTFN(sc, 9, "%s\n",
+ "sending data frame failed (too much retries)");
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_FAILURE, retrycnt);
ifp->if_oerrors++;
break;
@@ -954,7 +962,7 @@ rt2661_tx_intr(struct rt2661_softc *sc)
ifp->if_oerrors++;
}
- DPRINTFN(15, ("tx done q=%d idx=%u\n", qid, txq->stat));
+ DPRINTFN(sc, 15, "tx done q=%d idx=%u\n", qid, txq->stat);
txq->queued--;
if (++txq->stat >= txq->count) /* faster than % count */
@@ -969,7 +977,8 @@ rt2661_tx_intr(struct rt2661_softc *sc)
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2661_start(ifp);
+
+ rt2661_start_locked(ifp);
}
static void
@@ -995,7 +1004,7 @@ rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2661_TX_VALID);
- DPRINTFN(15, ("tx dma done q=%p idx=%u\n", txq, txq->next));
+ DPRINTFN(sc, 15, "tx dma done q=%p idx=%u\n", txq, txq->next);
if (++txq->next >= txq->count) /* faster than % count */
txq->next = 0;
@@ -1007,14 +1016,13 @@ rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq)
static void
rt2661_rx_intr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2661_rx_desc *desc;
struct rt2661_rx_data *data;
bus_addr_t physaddr;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct rt2661_node *rn;
struct mbuf *mnew, *m;
int error;
@@ -1036,8 +1044,8 @@ rt2661_rx_intr(struct rt2661_softc *sc)
* This should not happen since we did not request
* to receive those frames when we filled TXRX_CSR0.
*/
- DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
- le32toh(desc->flags)));
+ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n",
+ le32toh(desc->flags));
ifp->if_ierrors++;
goto skip;
}
@@ -1098,7 +1106,7 @@ rt2661_rx_intr(struct rt2661_softc *sc)
rssi = rt2661_get_rssi(sc, desc->rssi);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_rx_radiotap_header *tap = &sc->sc_rxtap;
uint32_t tsf_lo, tsf_hi;
@@ -1109,38 +1117,37 @@ rt2661_rx_intr(struct rt2661_softc *sc)
tap->wr_tsf =
htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
tap->wr_flags = 0;
- tap->wr_rate = rt2661_rxrate(desc);
- tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2661_RX_OFDM);
tap->wr_antsignal = rssi < 0 ? 0 : rssi;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
sc->sc_flags |= RAL_INPUT_RUNNING;
RAL_UNLOCK(sc);
wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic,
- (struct ieee80211_frame_min *)wh);
-
- /* Error happened during RSSI conversion. */
- if (rssi < 0)
- rssi = ni->ni_rssi;
/* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, rssi, RT2661_NOISE_FLOOR, 0);
+ ni = ieee80211_find_rxnode(ic,
+ (struct ieee80211_frame_min *)wh);
+ if (ni != NULL) {
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = -30; /* XXX ignored by net80211 */
+
+ (void) ieee80211_input(ni, m, rssi,
+ RT2661_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi,
+ RT2661_NOISE_FLOOR, 0);
- /* give rssi to the rate adatation algorithm */
- rn = (struct rt2661_node *)ni;
RAL_LOCK(sc);
sc->sc_flags &= ~RAL_INPUT_RUNNING;
- ral_rssadapt_input(ic, ni, &rn->rssadapt, rssi);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
skip: desc->flags |= htole32(RT2661_RX_BUSY);
- DPRINTFN(15, ("rx intr idx=%u\n", sc->rxq.cur));
+ DPRINTFN(sc, 15, "rx intr idx=%u\n", sc->rxq.cur);
sc->rxq.cur = (sc->rxq.cur + 1) % RT2661_RX_RING_COUNT;
}
@@ -1238,137 +1245,13 @@ rt2661_intr(void *arg)
RAL_UNLOCK(sc);
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
-#define RAL_SIFS 10 /* us */
-
-/*
- * This function is only used by the Rx radiotap code. It returns the rate at
- * which a given frame was received.
- */
-static uint8_t
-rt2661_rxrate(struct rt2661_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2661_RX_OFDM) {
- /* reverse function of rt2661_plcp_signal */
- switch (desc->rate & 0xf) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-rt2661_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rt2661_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
-
- return txtime;
-}
-
-static uint8_t
-rt2661_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
uint32_t flags, uint16_t xflags, int len, int rate,
const bus_dma_segment_t *segs, int nsegs, int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int i, remainder;
@@ -1393,11 +1276,11 @@ rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
desc->qid = ac;
/* setup PLCP fields */
- desc->plcp_signal = rt2661_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2661_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1428,7 +1311,9 @@ static int
rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
struct ieee80211_frame *wh;
@@ -1441,13 +1326,12 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
desc = &sc->mgtq.desc[sc->mgtq.cur];
data = &sc->mgtq.data[sc->mgtq.cur];
- /* send mgt frames at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1463,27 +1347,27 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
+ /* management frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
wh = mtod(m0, struct ieee80211_frame *);
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2661_TX_NEED_ACK;
- dur = rt2661_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
- RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp in probe responses */
@@ -1500,8 +1384,8 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->mgtq.desc_dmat, sc->mgtq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->mgtq.cur, rate));
+ DPRINTFN(sc, 10, "sending mgt frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->mgtq.cur, rate);
/* kick mgt */
sc->mgtq.queued++;
@@ -1511,67 +1395,108 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
return 0;
}
-/*
- * Build a RTS control frame.
- */
-static struct mbuf *
-rt2661_get_rts(struct rt2661_softc *sc, struct ieee80211_frame *wh,
- uint16_t dur)
+static int
+rt2661_sendprot(struct rt2661_softc *sc, int ac,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
{
- struct ieee80211_frame_rts *rts;
- struct mbuf *m;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct rt2661_tx_ring *txq = &sc->txq[ac];
+ const struct ieee80211_frame *wh;
+ struct rt2661_tx_desc *desc;
+ struct rt2661_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort, error;
+ uint16_t dur;
+ bus_dma_segment_t segs[RT2661_MAX_SCATTER];
+ int nsegs;
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL) {
- sc->sc_ic.ic_stats.is_tx_nobuf++;
- device_printf(sc->sc_dev, "could not allocate RTS frame\n");
- return NULL;
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2661_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2661_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+
+ data = &txq->data[txq->cur];
+ desc = &txq->desc[txq->cur];
+
+ error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, mprot, segs,
+ &nsegs, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not map mbuf (error %d)\n", error);
+ m_freem(mprot);
+ return error;
}
- rts = mtod(m, struct ieee80211_frame_rts *);
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ /* ctl frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
+
+ rt2661_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len,
+ protrate, segs, 1, ac);
- rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
- IEEE80211_FC0_SUBTYPE_RTS;
- rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- *(uint16_t *)rts->i_dur = htole16(dur);
- IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
- IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
+ bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
- m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts);
+ txq->queued++;
+ txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
- return m;
+ return 0;
}
static int
rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni, int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2661_tx_ring *txq = &sc->txq[ac];
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
- struct rt2661_node *rn;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
const struct chanAccParams *cap;
struct mbuf *mnew;
bus_dma_segment_t segs[RT2661_MAX_SCATTER];
uint16_t dur;
- uint32_t flags = 0;
+ uint32_t flags;
int error, nsegs, rate, noack = 0;
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ } else if (m0->m_flags & M_EAPOL) {
+ rate = tp->mgmtrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- struct ieee80211_rateset *rs;
-
- rs = &ni->ni_rates;
- rn = (struct rt2661_node *)ni;
- ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs,
- wh, m0->m_pkthdr.len, NULL, 0);
- rate = rs->rs_rates[ni->ni_txrate];
+ (void) ieee80211_amrr_choose(ni, &RT2661_NODE(ni)->amrr);
+ rate = ni->ni_txrate;
}
rate &= IEEE80211_RATE_VAL;
@@ -1581,7 +1506,7 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
}
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1591,66 +1516,22 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- /*
- * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
- * for directed frames only when the length of the MPDU is greater
- * than the length threshold indicated by [...]" ic_rtsthreshold.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- m0->m_pkthdr.len > ic->ic_rtsthreshold) {
- struct mbuf *m;
- uint16_t dur;
- int rtsrate, ackrate;
-
- rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
- ackrate = rt2661_ack_rate(ic, rate);
-
- dur = rt2661_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
- rt2661_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
- /* XXX: noack (QoS)? */
- rt2661_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
- 3 * RAL_SIFS;
-
- m = rt2661_get_rts(sc, wh, dur);
-
- desc = &txq->desc[txq->cur];
- data = &txq->data[txq->cur];
-
- error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m,
- segs, &nsegs, 0);
- if (error != 0) {
- device_printf(sc->sc_dev,
- "could not map mbuf (error %d)\n", error);
- m_freem(m);
- m_freem(m0);
- return error;
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rt2661_sendprot(sc, ac, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2661_TX_LONG_RETRY | RT2661_TX_IFS;
}
-
- /* avoid multiple free() of the same node for each fragment */
- ieee80211_ref_node(ni);
-
- data->m = m;
- data->ni = ni;
-
- /* RTS frames are not taken into account for rssadapt */
- data->id.id_node = NULL;
-
- rt2661_setup_tx_desc(sc, desc, RT2661_TX_NEED_ACK |
- RT2661_TX_MORE_FRAG, 0, m->m_pkthdr.len, rtsrate, segs,
- nsegs, ac);
-
- bus_dmamap_sync(txq->data_dmat, data->map,
- BUS_DMASYNC_PREWRITE);
-
- txq->queued++;
- txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
-
- /*
- * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
- * asynchronous data frame shall be transmitted after the CTS
- * frame and a SIFS period.
- */
- flags |= RT2661_TX_LONG_RETRY | RT2661_TX_IFS;
}
data = &txq->data[txq->cur];
@@ -1687,7 +1568,7 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1695,26 +1576,25 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
/* remember link conditions for rate adaptation algorithm */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- data->id.id_len = m0->m_pkthdr.len;
- data->id.id_rateidx = ni->ni_txrate;
- data->id.id_node = ni;
- data->id.id_rssi = ni->ni_rssi;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ data->rix = ni->ni_txrate;
+ /* XXX probably need last rssi value and not avg */
+ data->rssi = ic->ic_node_getrssi(ni);
} else
- data->id.id_node = NULL;
+ data->rix = IEEE80211_FIXED_RATE_NONE;
if (!noack && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2661_TX_NEED_ACK;
- dur = rt2661_txtime(RAL_ACK_SIZE, rt2661_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
@@ -1724,8 +1604,8 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, txq->cur, rate));
+ DPRINTFN(sc, 10, "sending data frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, txq->cur, rate);
/* kick Tx */
txq->queued++;
@@ -1736,181 +1616,163 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
}
static void
-rt2661_start(struct ifnet *ifp)
+rt2661_start_locked(struct ifnet *ifp)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
int ac;
- RAL_LOCK(sc);
+ RAL_LOCK_ASSERT(sc);
/* prevent management frames from being sent if we're not ready */
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->sc_invalid) {
- RAL_UNLOCK(sc);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->sc_invalid)
return;
- }
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->mgtq.queued >= RT2661_MGT_RING_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
- if (rt2661_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1) {
+ /* there is no place left in this ring */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
+ if (rt2661_tx_data(sc, m, ni, ac) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
+ sc->sc_tx_timer = 5;
+ }
+}
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
+static void
+rt2661_start(struct ifnet *ifp)
+{
+ struct rt2661_softc *sc = ifp->if_softc;
- /* no QoS encapsulation for EAPOL frames */
- ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
- M_WME_GETAC(m0) : WME_AC_BE;
+ RAL_LOCK(sc);
+ rt2661_start_locked(ifp);
+ RAL_UNLOCK(sc);
+}
- if (sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- ieee80211_free_node(ni);
- break;
- }
+static int
+rt2661_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2661_softc *sc = ifp->if_softc;
- BPF_MTAP(ifp, m0);
+ RAL_LOCK(sc);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->mgtq.queued >= RT2661_MGT_RING_COUNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
+ ifp->if_opackets++;
- if (rt2661_tx_data(sc, m0, ni, ac) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
- }
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (rt2661_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ sc->sc_tx_timer = 5;
- sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
- callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
- }
+ RAL_UNLOCK(sc);
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
RAL_UNLOCK(sc);
+ return EIO; /* XXX */
}
static void
rt2661_watchdog(void *arg)
{
struct rt2661_softc *sc = (struct rt2661_softc *)arg;
+ struct ifnet *ifp = sc->sc_ifp;
- if (sc->sc_tx_timer > 0 && !sc->sc_invalid) {
- if (--sc->sc_tx_timer == 0) {
- device_printf(sc->sc_dev, "device timeout\n");
- rt2661_init(sc);
- sc->sc_ifp->if_oerrors++;
- return;
- }
- callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
- }
-}
+ RAL_LOCK_ASSERT(sc);
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-rt2661_reset(struct ifnet *ifp)
-{
- struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
+ KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
- rt2661_set_chan(sc, ic->ic_curchan);
+ if (sc->sc_invalid) /* card ejected */
+ return;
- return 0;
+ if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ rt2661_init_locked(sc);
+ ifp->if_oerrors++;
+ /* NB: callout is reset in rt2661_init() */
+ return;
+ }
+ callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
}
static int
rt2661_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ RAL_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2661_update_promisc(sc);
- else
- rt2661_init(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rt2661_init_locked(sc);
+ startall = 1;
+ } else
+ rt2661_update_promisc(ifp);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2661_stop(sc);
+ rt2661_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rt2661_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
+ RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1933,7 +1795,7 @@ rt2661_bbp_write(struct rt2661_softc *sc, uint8_t reg, uint8_t val)
tmp = RT2661_BBP_BUSY | (reg & 0x7f) << 8 | val;
RAL_WRITE(sc, RT2661_PHY_CSR3, tmp);
- DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+ DPRINTFN(sc, 15, "BBP R%u <- 0x%02x\n", reg, val);
}
static uint8_t
@@ -1989,7 +1851,7 @@ rt2661_rf_write(struct rt2661_softc *sc, uint8_t reg, uint32_t val)
/* remember last written value in sc */
sc->rf_regs[reg] = val;
- DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff));
+ DPRINTFN(sc, 15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff);
}
static int
@@ -2035,13 +1897,14 @@ rt2661_select_antenna(struct rt2661_softc *sc)
static void
rt2661_enable_mrr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
tmp &= ~RT2661_MRR_CCK_FALLBACK;
- if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
tmp |= RT2661_MRR_CCK_FALLBACK;
tmp |= RT2661_MRR_ENABLED;
@@ -2051,29 +1914,26 @@ rt2661_enable_mrr(struct rt2661_softc *sc)
static void
rt2661_set_txpreamble(struct rt2661_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
tmp &= ~RT2661_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RT2661_SHORT_PREAMBLE;
RAL_WRITE(sc, RT2661_TXRX_CSR4, tmp);
}
-/*
- * Supported rates for 802.11g. XXX should use ic_sup_rates.
- */
-static const struct ieee80211_rateset rt2661_rateset_11g =
- { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
-
static void
rt2661_set_basicrates(struct rt2661_softc *sc,
const struct ieee80211_rateset *rs)
{
#define RV(r) ((r) & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t mask = 0;
uint8_t rate;
int i, j;
@@ -2095,7 +1955,7 @@ rt2661_set_basicrates(struct rt2661_softc *sc,
RAL_WRITE(sc, RT2661_TXRX_CSR5, mask);
- DPRINTF(("Setting basic rate mask to 0x%x\n", mask));
+ DPRINTF(sc, "Setting basic rate mask to 0x%x\n", mask);
#undef RV
}
@@ -2148,15 +2008,17 @@ rt2661_select_band(struct rt2661_softc *sc, struct ieee80211_channel *c)
static void
rt2661_set_chan(struct rt2661_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct rfprog *rfprog;
uint8_t bbp3, bbp94 = RT2661_BBPR94_DEFAULT;
int8_t power;
u_int i, chan;
chan = ieee80211_chan2ieee(ic, c);
- if (chan == 0 || chan == IEEE80211_CHAN_ANY)
- return;
+ KASSERT(chan != 0 && chan != IEEE80211_CHAN_ANY, ("chan 0x%x", chan));
+
+ sc->sc_rates = ieee80211_get_ratetable(c);
/* select the appropriate RF settings based on what EEPROM says */
rfprog = (sc->rfprog == 0) ? rt2661_rf5225_1 : rt2661_rf5225_2;
@@ -2244,9 +2106,9 @@ rt2661_set_macaddr(struct rt2661_softc *sc, const uint8_t *addr)
}
static void
-rt2661_update_promisc(struct rt2661_softc *sc)
+rt2661_update_promisc(struct ifnet *ifp)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct rt2661_softc *sc = ifp->if_softc;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
@@ -2257,8 +2119,8 @@ rt2661_update_promisc(struct rt2661_softc *sc)
RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
- DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
- "entering" : "leaving"));
+ DPRINTF(sc, "%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
}
/*
@@ -2311,7 +2173,7 @@ static void
rt2661_update_slot(struct ifnet *ifp)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint32_t tmp;
@@ -2335,9 +2197,8 @@ rt2661_get_rf(int rev)
}
static void
-rt2661_read_eeprom(struct rt2661_softc *sc)
+rt2661_read_eeprom(struct rt2661_softc *sc, struct ieee80211com *ic)
{
- struct ieee80211com *ic = &sc->sc_ic;
uint16_t val;
int i;
@@ -2362,14 +2223,14 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
sc->tx_ant = (val >> 2) & 0x3;
sc->nb_ant = val & 0x3;
- DPRINTF(("RF revision=%d\n", sc->rf_rev));
+ DPRINTF(sc, "RF revision=%d\n", sc->rf_rev);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_CONFIG2);
sc->ext_5ghz_lna = (val >> 6) & 0x1;
sc->ext_2ghz_lna = (val >> 4) & 0x1;
- DPRINTF(("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
- sc->ext_2ghz_lna, sc->ext_5ghz_lna));
+ DPRINTF(sc, "External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+ sc->ext_2ghz_lna, sc->ext_5ghz_lna);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_2GHZ_OFFSET);
if ((val & 0xff) != 0xff)
@@ -2393,8 +2254,8 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
if (sc->ext_5ghz_lna)
sc->rssi_5ghz_corr -= 14;
- DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
- sc->rssi_2ghz_corr, sc->rssi_5ghz_corr));
+ DPRINTF(sc, "RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+ sc->rssi_2ghz_corr, sc->rssi_5ghz_corr);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_FREQ_OFFSET);
if ((val >> 8) != 0xff)
@@ -2402,17 +2263,17 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
if ((val & 0xff) != 0xff)
sc->rffreq = val & 0xff;
- DPRINTF(("RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq));
+ DPRINTF(sc, "RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq);
/* read Tx power for all a/b/g channels */
for (i = 0; i < 19; i++) {
val = rt2661_eeprom_read(sc, RT2661_EEPROM_TXPOWER + i);
sc->txpow[i * 2] = (int8_t)(val >> 8); /* signed */
- DPRINTF(("Channel=%d Tx power=%d\n",
- rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]));
+ DPRINTF(sc, "Channel=%d Tx power=%d\n",
+ rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]);
sc->txpow[i * 2 + 1] = (int8_t)(val & 0xff); /* signed */
- DPRINTF(("Channel=%d Tx power=%d\n",
- rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]));
+ DPRINTF(sc, "Channel=%d Tx power=%d\n",
+ rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]);
}
/* read vendor-specific BBP values */
@@ -2422,8 +2283,8 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
continue; /* skip invalid entries */
sc->bbp_prom[i].reg = val >> 8;
sc->bbp_prom[i].val = val & 0xff;
- DPRINTF(("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
- sc->bbp_prom[i].val));
+ DPRINTF(sc, "BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+ sc->bbp_prom[i].val);
}
}
@@ -2464,16 +2325,26 @@ rt2661_bbp_init(struct rt2661_softc *sc)
}
static void
-rt2661_init(void *priv)
+rt2661_init_locked(struct rt2661_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rt2661_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp, sta[3];
- int i, ntries;
+ int i, error, ntries;
- RAL_LOCK(sc);
+ RAL_LOCK_ASSERT(sc);
+
+ if ((sc->sc_flags & RAL_FW_LOADED) == 0) {
+ error = rt2661_load_microcode(sc);
+ if (error != 0) {
+ if_printf(ifp,
+ "%s: could not load 8051 microcode, error %d\n",
+ __func__, error);
+ return;
+ }
+ sc->sc_flags |= RAL_FW_LOADED;
+ }
rt2661_stop_locked(sc);
@@ -2536,13 +2407,11 @@ rt2661_init(void *priv)
if (ntries == 1000) {
printf("timeout waiting for BBP/RF to wakeup\n");
rt2661_stop_locked(sc);
- RAL_UNLOCK(sc);
return;
}
if (rt2661_bbp_init(sc) != 0) {
rt2661_stop_locked(sc);
- RAL_UNLOCK(sc);
return;
}
@@ -2582,49 +2451,44 @@ rt2661_init(void *priv)
/* kick Rx */
RAL_WRITE(sc, RT2661_RX_CNTL_CSR, 1);
- RAL_UNLOCK(sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
-
+ callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
#undef N
}
-void
-rt2661_stop(void *priv)
+static void
+rt2661_init(void *priv)
{
struct rt2661_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK(sc);
- rt2661_stop_locked(sc);
+ rt2661_init_locked(sc);
RAL_UNLOCK(sc);
+
+ ieee80211_start_all(ic);
}
void
rt2661_stop_locked(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
volatile int *flags = &sc->sc_flags;
- while (*flags & RAL_INPUT_RUNNING) {
+ while (*flags & RAL_INPUT_RUNNING)
msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10);
- }
+
+ callout_stop(&sc->watchdog_ch);
+ sc->sc_tx_timer = 0;
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
+
/* abort Tx (for all 5 Tx rings) */
RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16);
@@ -2654,11 +2518,48 @@ rt2661_stop_locked(struct rt2661_softc *sc)
}
}
+void
+rt2661_stop(void *priv)
+{
+ struct rt2661_softc *sc = priv;
+
+ RAL_LOCK(sc);
+ rt2661_stop_locked(sc);
+ RAL_UNLOCK(sc);
+}
+
static int
-rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
+rt2661_load_microcode(struct rt2661_softc *sc)
{
- int ntries;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct firmware *fp;
+ const char *imagename;
+ int ntries, error;
+
+ RAL_LOCK_ASSERT(sc);
+
+ switch (sc->sc_id) {
+ case 0x0301: imagename = "rt2561sfw"; break;
+ case 0x0302: imagename = "rt2561fw"; break;
+ case 0x0401: imagename = "rt2661fw"; break;
+ default:
+ if_printf(ifp, "%s: unexpected pci device id 0x%x, "
+ "don't know how to retrieve firmware\n",
+ __func__, sc->sc_id);
+ return EINVAL;
+ }
+ RAL_UNLOCK(sc);
+ fp = firmware_get(imagename);
+ RAL_LOCK(sc);
+ if (fp == NULL) {
+ if_printf(ifp, "%s: unable to retrieve firmware image %s\n",
+ __func__, imagename);
+ return EINVAL;
+ }
+ /*
+ * Load 8051 microcode into NIC.
+ */
/* reset 8051 */
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
@@ -2669,7 +2570,7 @@ rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
/* write 8051's microcode */
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET | RT2661_MCU_SEL);
- RAL_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, ucode, size);
+ RAL_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, fp->data, fp->datasize);
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
/* kick 8051's ass */
@@ -2682,10 +2583,14 @@ rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
DELAY(100);
}
if (ntries == 500) {
- printf("timeout waiting for MCU to initialize\n");
- return EIO;
- }
- return 0;
+ if_printf(ifp, "%s: timeout waiting for MCU to initialize\n",
+ __func__);
+ error = EIO;
+ } else
+ error = 0;
+
+ firmware_put(fp, FIRMWARE_UNLOAD);
+ return error;
}
#ifdef notyet
@@ -2808,22 +2713,22 @@ rt2661_radar_stop(struct rt2661_softc *sc)
#endif
static int
-rt2661_prepare_beacon(struct rt2661_softc *sc)
+rt2661_prepare_beacon(struct rt2661_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_beacon_offsets bo;
struct rt2661_tx_desc desc;
struct mbuf *m0;
int rate;
- m0 = ieee80211_beacon_alloc(ic->ic_bss, &bo);
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &bo);
if (m0 == NULL) {
device_printf(sc->sc_dev, "could not allocate beacon frame\n");
return ENOBUFS;
}
/* send beacons at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 12 : 2;
+ rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan) ? 12 : 2;
rt2661_setup_tx_desc(sc, &desc, RT2661_TX_TIMESTAMP, RT2661_TX_HWSEQ,
m0->m_pkthdr.len, rate, NULL, 0, RT2661_QID_MGT);
@@ -2847,10 +2752,12 @@ rt2661_prepare_beacon(struct rt2661_softc *sc)
static void
rt2661_enable_tsf_sync(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Change default 16ms TBTT adjustment to 8ms.
* Must be done before enabling beacon generation.
@@ -2861,10 +2768,10 @@ rt2661_enable_tsf_sync(struct rt2661_softc *sc)
tmp = RAL_READ(sc, RT2661_TXRX_CSR9) & 0xff000000;
/* set beacon interval (in 1/16ms unit) */
- tmp |= ic->ic_bss->ni_intval * 16;
+ tmp |= vap->iv_bss->ni_intval * 16;
tmp |= RT2661_TSF_TICKING | RT2661_ENABLE_TBTT;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
tmp |= RT2661_TSF_MODE(1);
else
tmp |= RT2661_TSF_MODE(2) | RT2661_GENERATE_BEACON;
@@ -2937,10 +2844,11 @@ rt2661_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct rt2661_softc *sc = ifp->if_softc;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
rt2661_enable_tsf_sync(sc);
/* XXX keep local copy */
- rt2661_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rt2661_set_bssid(sc, vap->iv_bss->ni_bssid);
}
static void
@@ -2951,6 +2859,11 @@ rt2661_set_channel(struct ieee80211com *ic)
RAL_LOCK(sc);
rt2661_set_chan(sc, ic->ic_curchan);
+
+ sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ sc->sc_rxtap.wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
RAL_UNLOCK(sc);
}
diff --git a/sys/dev/ral/rt2661var.h b/sys/dev/ral/rt2661var.h
index 9f12a15..0c15840 100644
--- a/sys/dev/ral/rt2661var.h
+++ b/sys/dev/ral/rt2661var.h
@@ -51,7 +51,8 @@ struct rt2661_tx_data {
bus_dmamap_t map;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ral_rssdesc id;
+ uint8_t rix;
+ int8_t rssi;
};
struct rt2661_tx_ring {
@@ -87,14 +88,21 @@ struct rt2661_rx_ring {
struct rt2661_node {
struct ieee80211_node ni;
- struct ral_rssadapt rssadapt;
+ struct ieee80211_amrr_node amrr;
};
+#define RT2661_NODE(ni) ((struct rt2661_node *)(ni))
+
+struct rt2661_vap {
+ struct ieee80211vap ral_vap;
+ struct ieee80211_amrr amrr;
+
+ int (*ral_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RT2661_VAP(vap) ((struct rt2661_vap *)(vap))
struct rt2661_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
@@ -102,15 +110,21 @@ struct rt2661_softc {
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout rssadapt_ch;
int sc_tx_timer;
int sc_invalid;
+ int sc_debug;
+
+ const struct ieee80211_rate_table *sc_rates;
/*
* The same in both up to here
* ------------------------------------------------
*/
+ int sc_flags;
+#define RAL_FW_LOADED 0x1
+#define RAL_INPUT_RUNNING 0x2
+ int sc_id;
struct ieee80211_channel *sc_curchan;
uint8_t rf_rev;
@@ -148,23 +162,10 @@ struct rt2661_softc {
int dwelltime;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rt2661_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rt2661_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
-
- union {
- struct rt2661_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rt2661_tx_radiotap_header sc_txtap;
int sc_txtap_len;
-#define RAL_INPUT_RUNNING 1
- int sc_flags;
};
int rt2661_attach(device_t, int);
@@ -174,5 +175,6 @@ void rt2661_suspend(void *);
void rt2661_resume(void *);
void rt2661_intr(void *);
-#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/dev/usb/if_rum.c b/sys/dev/usb/if_rum.c
index 7657d10..ec46c84 100644
--- a/sys/dev/usb/if_rum.c
+++ b/sys/dev/usb/if_rum.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
@@ -128,23 +129,23 @@ MODULE_DEPEND(rum, wlan, 1, 1, 1);
MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1);
MODULE_DEPEND(rum, usb, 1, 1, 1);
+static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_vap_delete(struct ieee80211vap *);
static int rum_alloc_tx_list(struct rum_softc *);
static void rum_free_tx_list(struct rum_softc *);
static int rum_alloc_rx_list(struct rum_softc *);
static void rum_free_rx_list(struct rum_softc *);
-static int rum_media_change(struct ifnet *);
static void rum_task(void *);
static void rum_scantask(void *);
-static int rum_newstate(struct ieee80211com *,
+static int rum_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static void rum_txeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
static void rum_rxeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
-static int rum_rxrate(struct rum_rx_desc *);
-static int rum_ack_rate(struct ieee80211com *, int);
-static uint16_t rum_txtime(int, int, uint32_t);
-static uint8_t rum_plcp_signal(int);
static void rum_setup_tx_desc(struct rum_softc *,
struct rum_tx_desc *, uint32_t, uint16_t, int,
int);
@@ -185,13 +186,17 @@ static void rum_update_promisc(struct rum_softc *);
static const char *rum_get_rf(int);
static void rum_read_eeprom(struct rum_softc *);
static int rum_bbp_init(struct rum_softc *);
+static void rum_init_locked(struct rum_softc *);
static void rum_init(void *);
static void rum_stop(void *);
static int rum_load_microcode(struct rum_softc *, const u_char *,
size_t);
-static int rum_prepare_beacon(struct rum_softc *);
+static int rum_prepare_beacon(struct rum_softc *,
+ struct ieee80211vap *);
static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
+static struct ieee80211_node *rum_node_alloc(struct ieee80211_node_table *);
+static void rum_newassoc(struct ieee80211_node *, int);
static void rum_scan_start(struct ieee80211com *);
static void rum_scan_end(struct ieee80211com *);
static void rum_set_channel(struct ieee80211com *);
@@ -378,21 +383,21 @@ rum_attach(device_t self)
{
struct rum_softc *sc = device_get_softc(self);
struct usb_attach_arg *uaa = device_get_ivars(self);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
const uint8_t *ucode = NULL;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
- int i, ntries, size, bands;
+ int i, ntries, size;
+ uint8_t bands;
uint32_t tmp;
sc->sc_udev = uaa->device;
sc->sc_dev = self;
if (usbd_set_config_no(sc->sc_udev, RT2573_CONFIG_NO, 0) != 0) {
- printf("%s: could not set configuration no\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not set configuration no\n");
return ENXIO;
}
@@ -400,8 +405,7 @@ rum_attach(device_t self)
error = usbd_device2interface_handle(sc->sc_udev, RT2573_IFACE_INDEX,
&sc->sc_iface);
if (error != 0) {
- printf("%s: could not get interface handle\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not get interface handle\n");
return ENXIO;
}
@@ -414,8 +418,8 @@ rum_attach(device_t self)
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
if (ed == NULL) {
- printf("%s: no endpoint descriptor for iface %d\n",
- device_get_nameunit(sc->sc_dev), i);
+ device_printf(self,
+ "no endpoint descriptor for iface %d\n", i);
return ENXIO;
}
@@ -427,18 +431,23 @@ rum_attach(device_t self)
sc->sc_tx_no = ed->bEndpointAddress;
}
if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
- printf("%s: missing endpoint\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "missing endpoint\n");
return ENXIO;
}
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(self, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
usb_init_task(&sc->sc_task, rum_task, sc);
usb_init_task(&sc->sc_scantask, rum_scantask, sc);
callout_init(&sc->watchdog_ch, 0);
- callout_init(&sc->amrr_ch, 0);
/* retrieve RT2573 rev. no */
for (ntries = 0; ntries < 1000; ntries++) {
@@ -447,32 +456,22 @@ rum_attach(device_t self)
DELAY(1000);
}
if (ntries == 1000) {
- printf("%s: timeout waiting for chip to settle\n",
- device_get_nameunit(sc->sc_dev));
- return ENXIO;
+ device_printf(self, "timeout waiting for chip to settle\n");
+ goto bad;
}
/* retrieve MAC address and various other things from EEPROM */
rum_read_eeprom(sc);
- printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
- device_get_nameunit(sc->sc_dev), tmp, rum_get_rf(sc->rf_rev));
+ device_printf(self, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
+ tmp, rum_get_rf(sc->rf_rev));
ucode = rt2573_ucode;
size = sizeof rt2573_ucode;
error = rum_load_microcode(sc, ucode, size);
if (error != 0) {
- device_printf(sc->sc_dev, "could not load 8051 microcode\n");
- mtx_destroy(&sc->sc_mtx);
- return ENXIO;
- }
-
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- printf("%s: can not if_alloc()\n",
- device_get_nameunit(sc->sc_dev));
- mtx_destroy(&sc->sc_mtx);
- return ENXIO;
+ device_printf(self, "could not load 8051 microcode\n");
+ goto bad;
}
ifp->if_softc = sc;
@@ -488,82 +487,47 @@ rum_attach(device_t self)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
- IEEE80211_C_BGSCAN | /* bg scanning supported */
- IEEE80211_C_WPA; /* 802.11i */
+ IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
-
- if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) {
- struct ieee80211_channel *c;
-
- /* set supported .11a channels */
- for (i = 34; i <= 46; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 36; i <= 64; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 100; i <= 140; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 149; i <= 165; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- }
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rum_newassoc;
+ ic->ic_raw_xmit = rum_raw_xmit;
+ ic->ic_node_alloc = rum_node_alloc;
ic->ic_scan_start = rum_scan_start;
ic->ic_scan_end = rum_scan_end;
ic->ic_set_channel = rum_set_channel;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_vap_create = rum_vap_create;
+ ic->ic_vap_delete = rum_vap_delete;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rum_newstate;
- ic->ic_raw_xmit = rum_raw_xmit;
- ieee80211_media_init(ic, rum_media_change, ieee80211_media_status);
-
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN,
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
- sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT);
- sc->sc_txtap_len = sizeof sc->sc_txtapu;
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT);
@@ -571,20 +535,26 @@ rum_attach(device_t self)
ieee80211_announce(ic);
return 0;
+bad:
+ mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
+ return ENXIO;
}
static int
rum_detach(device_t self)
{
struct rum_softc *sc = device_get_softc(self);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
rum_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_task);
usb_rem_task(sc->sc_udev, &sc->sc_scantask);
callout_stop(&sc->watchdog_ch);
- callout_stop(&sc->amrr_ch);
if (sc->amrr_xfer != NULL) {
usbd_free_xfer(sc->amrr_xfer);
@@ -603,22 +573,66 @@ rum_detach(device_t self)
rum_free_rx_list(sc);
rum_free_tx_list(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
return 0;
}
+static struct ieee80211vap *
+rum_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rum_vap *rvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = rum_newstate;
+
+ callout_init(&rvp->amrr_ch, 0);
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rum_vap_delete(struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ callout_stop(&rvp->amrr_ch);
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
static int
rum_alloc_tx_list(struct rum_softc *sc)
{
struct rum_tx_data *data;
int i, error;
- sc->tx_queued = 0;
+ sc->tx_queued = sc->tx_cur = 0;
for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
data = &sc->tx_data[i];
@@ -627,16 +641,16 @@ rum_alloc_tx_list(struct rum_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate tx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
error = ENOMEM;
goto fail;
}
data->buf = usbd_alloc_buffer(data->xfer,
RT2573_TX_DESC_SIZE + MCLBYTES);
if (data->buf == NULL) {
- printf("%s: could not allocate tx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
error = ENOMEM;
goto fail;
}
@@ -684,22 +698,22 @@ rum_alloc_rx_list(struct rum_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate rx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
error = ENOMEM;
goto fail;
}
if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
- printf("%s: could not allocate rx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
error = ENOMEM;
goto fail;
}
data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
if (data->m == NULL) {
- printf("%s: could not allocate rx mbuf\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
error = ENOMEM;
goto fail;
}
@@ -733,39 +747,20 @@ rum_free_rx_list(struct rum_softc *sc)
}
}
-static int
-rum_media_change(struct ifnet *ifp)
-{
- struct rum_softc *sc = ifp->if_softc;
- int error;
-
- RUM_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET) {
- RUM_UNLOCK(sc);
- return error;
- }
-
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rum_init(sc);
-
- RUM_UNLOCK(sc);
-
- return 0;
-}
-
static void
rum_task(void *arg)
{
struct rum_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rum_vap *rvp = RUM_VAP(vap);
+ const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
uint32_t tmp;
- ostate = ic->ic_state;
+ ostate = vap->iv_state;
RUM_LOCK(sc);
@@ -779,9 +774,9 @@ rum_task(void *arg)
break;
case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rum_update_slot(ic->ic_ifp);
rum_enable_mrr(sc);
rum_set_txpreamble(sc);
@@ -789,16 +784,16 @@ rum_task(void *arg)
rum_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS)
- rum_prepare_beacon(sc);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ rum_prepare_beacon(sc, vap);
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
rum_enable_tsf_sync(sc);
- /* enable automatic rate adaptation in STA mode */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
rum_amrr_start(sc, ni);
break;
default:
@@ -807,40 +802,42 @@ rum_task(void *arg)
RUM_UNLOCK(sc);
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+ IEEE80211_LOCK(ic);
+ rvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static int
-rum_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct rum_softc *sc = ic->ic_ifp->if_softc;
- callout_stop(&sc->amrr_ch);
+ callout_stop(&rvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ rvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
- return 0;
+ return EINPROGRESS;
+ }
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RUM_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RUM_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RUM_CTS_SIZE 14 /* 10 + 4(FCS) */
-
static void
rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct rum_tx_data *data = priv;
struct rum_softc *sc = data->sc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (data->m != NULL && data->m->m_flags & M_TXCB)
ieee80211_process_callback(data->ni, data->m, 0/*XXX*/);
@@ -849,8 +846,8 @@ rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
- printf("%s: could not transmit buffer: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
@@ -879,10 +876,9 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct rum_rx_data *data = priv;
struct rum_softc *sc = data->sc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_rx_desc *desc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
struct mbuf *mnew, *m;
int len, rssi;
@@ -934,31 +930,29 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
rssi = rum_get_rssi(sc, desc->rssi);
- wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* Error happened during RSSI conversion. */
- if (rssi < 0)
- rssi = ni->ni_rssi;
-
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
- tap->wr_rate = rum_rxrate(desc);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2573_RX_OFDM);
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = rssi;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, rssi, RT2573_NOISE_FLOOR, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = -30; /* XXX ignored by net80211 */
+ (void) ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0);
DPRINTFN(15, ("rx done\n"));
@@ -968,125 +962,12 @@ skip: /* setup a new transfer */
usbd_transfer(xfer);
}
-/*
- * This function is only used by the Rx radiotap code.
- */
-static int
-rum_rxrate(struct rum_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2573_RX_OFDM) {
- /* reverse function of rum_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- */
-static int
-rum_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rum_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RUM_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
- return txtime;
-}
-
-static uint8_t
-rum_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
uint32_t flags, uint16_t xflags, int len, int rate)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1100,11 +981,11 @@ rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10));
/* setup PLCP fields */
- desc->plcp_signal = rum_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RUM_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2573_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1128,43 +1009,108 @@ rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
#define RUM_TX_TIMEOUT 5000
static int
+rum_sendprot(struct rum_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2573_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2573_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct rum_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len,
+ data->buf + RT2573_TX_DESC_SIZE);
+ rum_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RT2573_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
usbd_status error;
- int xferlen, rate;
-
- data = &sc->tx_data[0];
- desc = (struct rum_tx_desc *)data->buf;
-
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ int xferlen;
+ data = &sc->tx_data[sc->tx_cur];
data->m = m0;
data->ni = ni;
+ desc = (struct rum_tx_desc *)data->buf;
wh = mtod(m0, struct ieee80211_frame *);
-
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
+ wh = mtod(m0, struct ieee80211_frame *);
}
- wh = mtod(m0, struct ieee80211_frame *);
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2573_TX_NEED_ACK;
- dur = rum_txtime(RUM_ACK_SIZE, rum_ack_rate(ic, rate),
- ic->ic_flags) + sc->sifs;
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1174,20 +1120,20 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RT2573_TX_TIMESTAMP;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- tap->wt_rate = rate;
+ tap->wt_rate = tp->mgmtrate;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
- rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
+ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate);
/* align end on a 4-bytes boundary */
xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3;
@@ -1200,7 +1146,7 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
xferlen += 4;
DPRINTFN(10, ("sending mgt frame len=%d rate=%d xfer len=%d\n",
- m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate, xferlen));
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate, xferlen));
usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
@@ -1214,6 +1160,7 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1222,14 +1169,17 @@ static int
rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
uint32_t flags;
usbd_status error;
int xferlen, rate;
- data = &sc->tx_data[0];
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct rum_tx_desc *)data->buf;
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
@@ -1238,8 +1188,22 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2573_TX_NEED_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rum_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1248,16 +1212,12 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RT2573_TX_NEED_ACK;
-
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
/* XXX need to setup descriptor ourself */
rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
@@ -1284,6 +1244,7 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
return error;
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1291,10 +1252,13 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
static int
rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
@@ -1303,15 +1267,18 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
- else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-
- rate &= IEEE80211_RATE_VAL;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else {
+ (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1321,7 +1288,24 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
}
- data = &sc->tx_data[0];
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rum_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct rum_tx_desc *)data->buf;
data->m = m0;
@@ -1331,12 +1315,12 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RT2573_TX_NEED_ACK;
flags |= RT2573_TX_MORE_FRAG;
- dur = rum_txtime(RUM_ACK_SIZE, rum_ack_rate(ic, rate),
- ic->ic_flags) + sc->sifs;
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1345,7 +1329,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
@@ -1376,6 +1360,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1384,77 +1369,30 @@ static void
rum_start(struct ifnet *ifp)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rum_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rum_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (rum_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
}
}
@@ -1484,37 +1422,35 @@ static int
rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
RUM_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rum_update_promisc(sc);
- else
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
rum_init(sc);
+ startall = 1;
+ } else
+ rum_update_promisc(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
rum_stop(sc);
}
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rum_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
}
-
RUM_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1532,8 +1468,8 @@ rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read EEPROM: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
}
}
@@ -1561,8 +1497,9 @@ rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not multi read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev,
+ "could not multi read MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1588,8 +1525,9 @@ rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not multi write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev,
+ "could not multi write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1604,8 +1542,7 @@ rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to BBP\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to BBP\n");
return;
}
@@ -1624,8 +1561,7 @@ rum_bbp_read(struct rum_softc *sc, uint8_t reg)
break;
}
if (ntries == 5) {
- printf("%s: could not read BBP\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1639,7 +1575,7 @@ rum_bbp_read(struct rum_softc *sc, uint8_t reg)
DELAY(1);
}
- printf("%s: could not read BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1654,8 +1590,7 @@ rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to RF\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to RF\n");
return;
}
@@ -1697,13 +1632,14 @@ rum_select_antenna(struct rum_softc *sc)
static void
rum_enable_mrr(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR4);
tmp &= ~RT2573_MRR_CCK_FALLBACK;
- if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
tmp |= RT2573_MRR_CCK_FALLBACK;
tmp |= RT2573_MRR_ENABLED;
@@ -1713,12 +1649,14 @@ rum_enable_mrr(struct rum_softc *sc)
static void
rum_set_txpreamble(struct rum_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR4);
tmp &= ~RT2573_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RT2573_SHORT_PREAMBLE;
rum_write(sc, RT2573_TXRX_CSR4, tmp);
@@ -1727,13 +1665,14 @@ rum_set_txpreamble(struct rum_softc *sc)
static void
rum_set_basicrates(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* update basic rate set */
if (ic->ic_curmode == IEEE80211_MODE_11B) {
/* 11b basic rates: 1, 2Mbps */
rum_write(sc, RT2573_TXRX_CSR5, 0x3);
- } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
/* 11a basic rates: 6, 12, 24Mbps */
rum_write(sc, RT2573_TXRX_CSR5, 0x150);
} else {
@@ -1787,15 +1726,13 @@ rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c)
else
tmp |= RT2573_PA_PE_5GHZ;
rum_write(sc, RT2573_PHY_CSR0, tmp);
-
- /* 802.11a uses a 16 microseconds short interframe space */
- sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10;
}
static void
rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct rfprog *rfprog;
uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT;
int8_t power;
@@ -1868,10 +1805,12 @@ rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
static void
rum_enable_tsf_sync(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Change default 16ms TBTT adjustment to 8ms.
* Must be done before enabling beacon generation.
@@ -1882,10 +1821,10 @@ rum_enable_tsf_sync(struct rum_softc *sc)
tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
/* set beacon interval (in 1/16ms unit) */
- tmp |= ic->ic_bss->ni_intval * 16;
+ tmp |= vap->iv_bss->ni_intval * 16;
tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
tmp |= RT2573_TSF_MODE(1);
else
tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON;
@@ -1897,7 +1836,7 @@ static void
rum_update_slot(struct ifnet *ifp)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint32_t tmp;
@@ -1937,7 +1876,7 @@ rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
static void
rum_update_promisc(struct rum_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR0);
@@ -1967,7 +1906,8 @@ rum_get_rf(int rev)
static void
rum_read_eeprom(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t val;
#ifdef RUM_DEBUG
int i;
@@ -2082,12 +2022,11 @@ rum_bbp_init(struct rum_softc *sc)
}
static void
-rum_init(void *priv)
+rum_init_locked(struct rum_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rum_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_rx_data *data;
uint32_t tmp;
usbd_status error;
@@ -2111,8 +2050,8 @@ rum_init(void *priv)
DELAY(1000);
}
if (ntries == 1000) {
- printf("%s: timeout waiting for BBP/RF to wakeup\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
goto fail;
}
@@ -2138,8 +2077,7 @@ rum_init(void *priv)
*/
sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->amrr_xfer == NULL) {
- printf("%s: could not allocate AMRR xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
goto fail;
}
@@ -2149,15 +2087,15 @@ rum_init(void *priv)
error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
&sc->sc_tx_pipeh);
if (error != 0) {
- printf("%s: could not open Tx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
&sc->sc_rx_pipeh);
if (error != 0) {
- printf("%s: could not open Rx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
@@ -2166,14 +2104,12 @@ rum_init(void *priv)
*/
error = rum_alloc_tx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Tx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
goto fail;
}
error = rum_alloc_rx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Rx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
goto fail;
}
@@ -2204,13 +2140,6 @@ rum_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: rum_stop(sc);
@@ -2218,18 +2147,30 @@ fail: rum_stop(sc);
}
static void
+rum_init(void *priv)
+{
+ struct rum_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RUM_LOCK(sc);
+ rum_init_locked(sc);
+ RUM_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
rum_stop(void *priv)
{
struct rum_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
/* disable Rx */
tmp = rum_read(sc, RT2573_TXRX_CSR0);
rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
@@ -2277,30 +2218,28 @@ rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not run firmware: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not run firmware: %s\n",
+ usbd_errstr(error));
}
return error;
}
static int
-rum_prepare_beacon(struct rum_softc *sc)
+rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
struct rum_tx_desc desc;
struct mbuf *m0;
- int rate;
- m0 = ieee80211_beacon_alloc(ic->ic_bss, &sc->sc_bo);
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo);
if (m0 == NULL) {
return ENOBUFS;
}
- /* send beacons at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
-
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ,
- m0->m_pkthdr.len, rate);
+ m0->m_pkthdr.len, tp->mgmtrate);
/* copy the first 24 bytes of Tx descriptor into NIC memory */
rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24);
@@ -2318,8 +2257,7 @@ static int
rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
struct rum_softc *sc = ifp->if_softc;
/* prevent management frames from being sent if we're not ready */
@@ -2328,16 +2266,13 @@ rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
ieee80211_free_node(ni);
return ENETDOWN;
}
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
m_freem(m);
ieee80211_free_node(ni);
return EIO;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
@@ -2368,26 +2303,22 @@ bad:
static void
rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni)
{
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_vap *rvp = RUM_VAP(vap);
/* clear statistic registers (STA_CSR0 to STA_CSR5) */
rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
- ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
-
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni);
- callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc);
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
}
static void
rum_amrr_timeout(void *arg)
{
- struct rum_softc *sc = (struct rum_softc *)arg;
+ struct ieee80211vap *vap = arg;
+ struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc;
usb_device_request_t req;
/*
@@ -2399,7 +2330,7 @@ rum_amrr_timeout(void *arg)
USETW(req.wIndex, RT2573_STA_CSR0);
USETW(req.wLength, sizeof sc->sta);
- usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, sc,
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
rum_amrr_update);
(void)usbd_transfer(sc->amrr_xfer);
@@ -2409,8 +2340,11 @@ static void
rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
- struct rum_softc *sc = (struct rum_softc *)priv;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ieee80211vap *vap = priv;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct rum_softc *sc = ifp->if_softc;
+ int ok, fail;
if (status != USBD_NORMAL_COMPLETION) {
device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
@@ -2418,21 +2352,34 @@ rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
return;
}
- /* count TX retry-fail as Tx errors */
- ifp->if_oerrors += le32toh(sc->sta[5]) >> 16;
+ ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */
+ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */
+ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn,
+ ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
- sc->amn.amn_retrycnt =
- (le32toh(sc->sta[4]) >> 16) + /* TX one-retry ok count */
- (le32toh(sc->sta[5]) & 0xffff) + /* TX more-retry ok count */
- (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
+}
- sc->amn.amn_txcnt =
- sc->amn.amn_retrycnt +
- (le32toh(sc->sta[4]) & 0xffff); /* TX no-retry ok count */
+/* ARGUSED */
+static struct ieee80211_node *
+rum_node_alloc(struct ieee80211_node_table *nt __unused)
+{
+ struct rum_node *rn;
- ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
+ rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return rn != NULL ? &rn->ni : NULL;
+}
- callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc);
+static void
+rum_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
}
static void
@@ -2469,14 +2416,17 @@ rum_set_channel(struct ieee80211com *ic)
/* do it in a process context */
sc->sc_scan_action = RUM_SET_CHANNEL;
usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
}
static void
rum_scantask(void *arg)
{
struct rum_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
RUM_LOCK(sc);
@@ -2492,7 +2442,7 @@ rum_scantask(void *arg)
case RUM_SCAN_END:
rum_enable_tsf_sync(sc);
/* XXX keep local copy */
- rum_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rum_set_bssid(sc, vap->iv_bss->ni_bssid);
break;
case RUM_SET_CHANNEL:
@@ -2513,6 +2463,8 @@ rum_scantask(void *arg)
static int
rum_get_rssi(struct rum_softc *sc, uint8_t raw)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int lna, agc, rssi;
lna = (raw >> 5) & 0x3;
@@ -2530,7 +2482,7 @@ rum_get_rssi(struct rum_softc *sc, uint8_t raw)
rssi = (2 * agc) - RT2573_NOISE_FLOOR;
- if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)) {
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
rssi += sc->rssi_2ghz_corr;
if (lna == 1)
diff --git a/sys/dev/usb/if_rumvar.h b/sys/dev/usb/if_rumvar.h
index e7d6d15..59980c0 100644
--- a/sys/dev/usb/if_rumvar.h
+++ b/sys/dev/usb/if_rumvar.h
@@ -18,7 +18,7 @@
*/
#define RUM_RX_LIST_COUNT 1
-#define RUM_TX_LIST_COUNT 1
+#define RUM_TX_LIST_COUNT 8
struct rum_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
@@ -69,11 +69,26 @@ struct rum_rx_data {
struct mbuf *m;
};
+struct rum_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define RUM_NODE(ni) ((struct rum_node *)(ni))
+
+struct rum_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RUM_VAP(vap) ((struct rum_vap *)(vap))
+
struct rum_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
+ const struct ieee80211_rate_table *sc_rates;
device_t sc_dev;
usbd_device_handle sc_udev;
@@ -94,9 +109,6 @@ struct rum_softc {
int sc_arg;
struct usb_task sc_task;
- struct ieee80211_amrr amrr;
- struct ieee80211_amrr_node amn;
-
struct usb_task sc_scantask;
int sc_scan_action;
#define RUM_SCAN_START 0
@@ -106,13 +118,11 @@ struct rum_softc {
struct rum_rx_data rx_data[RUM_RX_LIST_COUNT];
struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
int tx_queued;
-
- struct ieee80211_beacon_offsets sc_bo;
+ int tx_cur;
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout amrr_ch;
int sc_tx_timer;
@@ -133,23 +143,12 @@ struct rum_softc {
int ext_5ghz_lna;
int rssi_2ghz_corr;
int rssi_5ghz_corr;
- int sifs;
uint8_t bbp17;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rum_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rum_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct rum_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rum_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/usb/if_ural.c b/sys/dev/usb/if_ural.c
index c2b063f..40a225b 100644
--- a/sys/dev/usb/if_ural.c
+++ b/sys/dev/usb/if_ural.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
@@ -115,23 +116,23 @@ MODULE_DEPEND(ural, wlan, 1, 1, 1);
MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1);
MODULE_DEPEND(ural, usb, 1, 1, 1);
+static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_vap_delete(struct ieee80211vap *);
static int ural_alloc_tx_list(struct ural_softc *);
static void ural_free_tx_list(struct ural_softc *);
static int ural_alloc_rx_list(struct ural_softc *);
static void ural_free_rx_list(struct ural_softc *);
-static int ural_media_change(struct ifnet *);
static void ural_task(void *);
static void ural_scantask(void *);
-static int ural_newstate(struct ieee80211com *,
+static int ural_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
-static int ural_rxrate(struct ural_rx_desc *);
static void ural_txeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
static void ural_rxeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
-static int ural_ack_rate(struct ieee80211com *, int);
-static uint16_t ural_txtime(int, int, uint32_t);
-static uint8_t ural_plcp_signal(int);
static void ural_setup_tx_desc(struct ural_softc *,
struct ural_tx_desc *, uint32_t, int, int);
static int ural_tx_bcn(struct ural_softc *, struct mbuf *,
@@ -142,7 +143,6 @@ static int ural_tx_data(struct ural_softc *, struct mbuf *,
struct ieee80211_node *);
static void ural_start(struct ifnet *);
static void ural_watchdog(void *);
-static int ural_reset(struct ifnet *);
static int ural_ioctl(struct ifnet *, u_long, caddr_t);
static void ural_set_testmode(struct ural_softc *);
static void ural_eeprom_read(struct ural_softc *, uint16_t, void *,
@@ -156,6 +156,8 @@ static void ural_write_multi(struct ural_softc *, uint16_t, void *,
static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
static uint8_t ural_bbp_read(struct ural_softc *, uint8_t);
static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
+static struct ieee80211_node *ural_node_alloc(struct ieee80211_node_table *);
+static void ural_newassoc(struct ieee80211_node *, int);
static void ural_scan_start(struct ieee80211com *);
static void ural_scan_end(struct ieee80211com *);
static void ural_set_channel(struct ieee80211com *);
@@ -165,7 +167,8 @@ static void ural_disable_rf_tune(struct ural_softc *);
static void ural_enable_tsf_sync(struct ural_softc *);
static void ural_update_slot(struct ifnet *);
static void ural_set_txpreamble(struct ural_softc *);
-static void ural_set_basicrates(struct ural_softc *);
+static void ural_set_basicrates(struct ural_softc *,
+ const struct ieee80211_channel *);
static void ural_set_bssid(struct ural_softc *, const uint8_t *);
static void ural_set_macaddr(struct ural_softc *, uint8_t *);
static void ural_update_promisc(struct ural_softc *);
@@ -174,6 +177,7 @@ static void ural_read_eeprom(struct ural_softc *);
static int ural_bbp_init(struct ural_softc *);
static void ural_set_txantenna(struct ural_softc *, int);
static void ural_set_rxantenna(struct ural_softc *, int);
+static void ural_init_locked(struct ural_softc *);
static void ural_init(void *);
static void ural_stop(void *);
static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
@@ -387,18 +391,18 @@ ural_attach(device_t self)
struct ural_softc *sc = device_get_softc(self);
struct usb_attach_arg *uaa = device_get_ivars(self);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
- int i, bands;
+ int i;
+ uint8_t bands;
sc->sc_udev = uaa->device;
sc->sc_dev = self;
if (usbd_set_config_no(sc->sc_udev, RAL_CONFIG_NO, 0) != 0) {
- printf("%s: could not set configuration no\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not set configuration no\n");
return ENXIO;
}
@@ -406,8 +410,7 @@ ural_attach(device_t self)
error = usbd_device2interface_handle(sc->sc_udev, RAL_IFACE_INDEX,
&sc->sc_iface);
if (error != 0) {
- printf("%s: could not get interface handle\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not get interface handle\n");
return ENXIO;
}
@@ -420,8 +423,8 @@ ural_attach(device_t self)
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
if (ed == NULL) {
- printf("%s: no endpoint descriptor for %d\n",
- device_get_nameunit(sc->sc_dev), i);
+ device_printf(self, "no endpoint descriptor for %d\n",
+ i);
return ENXIO;
}
@@ -433,10 +436,16 @@ ural_attach(device_t self)
sc->sc_tx_no = ed->bEndpointAddress;
}
if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
- printf("%s: missing endpoint\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "missing endpoint\n");
+ return ENXIO;
+ }
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
return ENXIO;
}
+ ic = ifp->if_l2com;
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
@@ -444,7 +453,6 @@ ural_attach(device_t self)
usb_init_task(&sc->sc_task, ural_task, sc);
usb_init_task(&sc->sc_scantask, ural_scantask, sc);
callout_init(&sc->watchdog_ch, 0);
- callout_init(&sc->amrr_ch, 0);
/* retrieve RT2570 rev. no */
sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
@@ -452,16 +460,8 @@ ural_attach(device_t self)
/* retrieve MAC address and various other things from EEPROM */
ural_read_eeprom(sc);
- printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
- device_get_nameunit(sc->sc_dev), sc->asic_rev,
- ural_get_rf(sc->rf_rev));
-
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- printf("%s: can not if_alloc()\n",
- device_get_nameunit(sc->sc_dev));
- return ENXIO;
- }
+ device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
+ sc->asic_rev, ural_get_rf(sc->rf_rev));
ifp->if_softc = sc;
if_initname(ifp, "ural", device_get_unit(sc->sc_dev));
@@ -476,8 +476,6 @@ ural_attach(device_t self)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
@@ -496,34 +494,29 @@ ural_attach(device_t self)
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RAL_RF_5222)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_reset = ural_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_newassoc = ural_newassoc;
+ ic->ic_raw_xmit = ural_raw_xmit;
+ ic->ic_node_alloc = ural_node_alloc;
ic->ic_scan_start = ural_scan_start;
ic->ic_scan_end = ural_scan_end;
ic->ic_set_channel = ural_set_channel;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ural_newstate;
- ic->ic_raw_xmit = ural_raw_xmit;
- ieee80211_media_init(ic, ural_media_change, ieee80211_media_status);
+ ic->ic_vap_create = ural_vap_create;
+ ic->ic_vap_delete = ural_vap_delete;
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
- sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT);
- sc->sc_txtap_len = sizeof sc->sc_txtapu;
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT);
@@ -537,13 +530,16 @@ static int
ural_detach(device_t self)
{
struct ural_softc *sc = device_get_softc(self);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ural_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
callout_stop(&sc->watchdog_ch);
- callout_stop(&sc->amrr_ch);
if (sc->amrr_xfer != NULL) {
usbd_free_xfer(sc->amrr_xfer);
@@ -563,22 +559,66 @@ ural_detach(device_t self)
ural_free_rx_list(sc);
ural_free_tx_list(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
return 0;
}
+static struct ieee80211vap *
+ural_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ural_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return NULL;
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ural_newstate;
+
+ callout_init(&uvp->amrr_ch, 0);
+ ieee80211_amrr_init(&uvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ural_vap_delete(struct ieee80211vap *vap)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ callout_stop(&uvp->amrr_ch);
+ ieee80211_amrr_cleanup(&uvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
static int
ural_alloc_tx_list(struct ural_softc *sc)
{
struct ural_tx_data *data;
int i, error;
- sc->tx_queued = 0;
+ sc->tx_queued = sc->tx_cur = 0;
for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
data = &sc->tx_data[i];
@@ -587,8 +627,8 @@ ural_alloc_tx_list(struct ural_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate tx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
error = ENOMEM;
goto fail;
}
@@ -596,8 +636,8 @@ ural_alloc_tx_list(struct ural_softc *sc)
data->buf = usbd_alloc_buffer(data->xfer,
RAL_TX_DESC_SIZE + MCLBYTES);
if (data->buf == NULL) {
- printf("%s: could not allocate tx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
error = ENOMEM;
goto fail;
}
@@ -643,23 +683,23 @@ ural_alloc_rx_list(struct ural_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate rx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
error = ENOMEM;
goto fail;
}
if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
- printf("%s: could not allocate rx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
error = ENOMEM;
goto fail;
}
data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
if (data->m == NULL) {
- printf("%s: could not allocate rx mbuf\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
error = ENOMEM;
goto fail;
}
@@ -694,39 +734,20 @@ ural_free_rx_list(struct ural_softc *sc)
}
}
-static int
-ural_media_change(struct ifnet *ifp)
-{
- struct ural_softc *sc = ifp->if_softc;
- int error;
-
- RAL_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET) {
- RAL_UNLOCK(sc);
- return error;
- }
-
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- ural_init(sc);
-
- RAL_UNLOCK(sc);
-
- return 0;
-}
-
static void
ural_task(void *xarg)
{
struct ural_softc *sc = xarg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ural_vap *uvp = URAL_VAP(vap);
+ const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
struct mbuf *m;
- ostate = ic->ic_state;
+ ostate = vap->iv_state;
RAL_LOCK(sc);
switch (sc->sc_state) {
@@ -741,27 +762,27 @@ ural_task(void *xarg)
break;
case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
ural_update_slot(ic->ic_ifp);
ural_set_txpreamble(sc);
- ural_set_basicrates(sc);
+ ural_set_basicrates(sc, ic->ic_bsschan);
ural_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- m = ieee80211_beacon_alloc(ni, &sc->sc_bo);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &uvp->bo);
if (m == NULL) {
- printf("%s: could not allocate beacon\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate beacon\n");
return;
}
if (ural_tx_bcn(sc, m, ni) != 0) {
- printf("%s: could not send beacon\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not send beacon\n");
return;
}
}
@@ -769,12 +790,12 @@ ural_task(void *xarg)
/* make tx led blink on tx (controlled by ASIC) */
ural_write(sc, RAL_MAC_CSR20, 1);
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
ural_enable_tsf_sync(sc);
- /* enable automatic rate adaptation in STA mode */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
ural_amrr_start(sc, ni);
break;
@@ -784,15 +805,21 @@ ural_task(void *xarg)
}
RAL_UNLOCK(sc);
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static void
ural_scantask(void *arg)
{
struct ural_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
RAL_LOCK(sc);
if (sc->sc_scan_action == URAL_SCAN_START) {
@@ -806,77 +833,42 @@ ural_scantask(void *arg)
} else {
ural_enable_tsf_sync(sc);
/* XXX keep local copy */
- ural_set_bssid(sc, ic->ic_bss->ni_bssid);
+ ural_set_bssid(sc, vap->iv_bss->ni_bssid);
}
RAL_UNLOCK(sc);
}
static int
-ural_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ural_softc *sc = ic->ic_ifp->if_softc;
- callout_stop(&sc->amrr_ch);
+ callout_stop(&uvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ uvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
- return 0;
+ return EINPROGRESS;
+ }
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
-#define RAL_SIFS 10 /* us */
-
#define RAL_RXTX_TURNAROUND 5 /* us */
-/*
- * This function is only used by the Rx radiotap code.
- */
-static int
-ural_rxrate(struct ural_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RAL_RX_OFDM) {
- /* reverse function of ural_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
static void
ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ural_tx_data *data = priv;
struct ural_softc *sc = data->sc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (data->m->m_flags & M_TXCB)
ieee80211_process_callback(data->ni, data->m,
@@ -885,8 +877,8 @@ ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
- printf("%s: could not transmit buffer: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
@@ -916,13 +908,12 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ural_rx_data *data = priv;
struct ural_softc *sc = data->sc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_rx_desc *desc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
struct mbuf *mnew, *m;
- int len;
+ int len, rssi;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
@@ -970,30 +961,30 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff;
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
- tap->wr_rate = ural_rxrate(desc);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RAL_RX_OFDM);
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = URAL_RSSI(desc->rssi);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
/* Strip trailing 802.11 MAC FCS. */
m_adj(m, -IEEE80211_CRC_LEN);
- wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, URAL_RSSI(desc->rssi), RAL_NOISE_FLOOR, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ rssi = URAL_RSSI(desc->rssi);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0);
DPRINTFN(15, ("rx done\n"));
@@ -1003,95 +994,12 @@ skip: /* setup a new transfer */
usbd_transfer(xfer);
}
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-ural_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-ural_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
- return txtime;
-}
-
-static uint8_t
-ural_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
uint32_t flags, int len, int rate)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1103,11 +1011,11 @@ ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame)));
/* setup PLCP fields */
- desc->plcp_signal = ural_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RAL_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1136,14 +1044,17 @@ ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
static int
ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
struct ural_tx_desc *desc;
usbd_xfer_handle xfer;
- uint8_t cmd = 0;
+ uint8_t cmd;
usbd_status error;
uint8_t *buf;
- int xferlen, rate;
+ int xferlen;
- rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == NULL)
@@ -1158,6 +1069,7 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
return ENOMEM;
}
+ cmd = 0;
usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, &cmd, sizeof cmd,
USBD_FORCE_SHORT_XFER, RAL_TX_TIMEOUT, NULL);
@@ -1171,10 +1083,10 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
m_copydata(m0, 0, m0->m_pkthdr.len, buf + RAL_TX_DESC_SIZE);
ural_setup_tx_desc(sc, desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP,
- m0->m_pkthdr.len, rate);
+ m0->m_pkthdr.len, tp->mgmtrate);
DPRINTFN(10, ("sending beacon frame len=%u rate=%u xfer len=%u\n",
- m0->m_pkthdr.len, rate, xferlen));
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, xferlen,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, NULL);
@@ -1188,40 +1100,43 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
static int
ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct ieee80211_txparam *tp;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
struct ieee80211_frame *wh;
struct ieee80211_key *k;
- uint32_t flags = 0;
+ uint32_t flags;
uint16_t dur;
usbd_status error;
- int xferlen, rate;
+ int xferlen;
- data = &sc->tx_data[0];
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
wh = mtod(m0, struct ieee80211_frame *);
-
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
+ wh = mtod(m0, struct ieee80211_frame *);
}
data->m = m0;
data->ni = ni;
- wh = mtod(m0, struct ieee80211_frame *);
-
+ flags = 0;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RAL_TX_ACK;
- dur = ural_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1232,20 +1147,20 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RAL_TX_TIMESTAMP;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- tap->wt_rate = rate;
+ tap->wt_rate = tp->mgmtrate;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
- ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
+ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, tp->mgmtrate);
/* align end on a 2-bytes boundary */
xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
@@ -1258,7 +1173,7 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
xferlen += 2;
DPRINTFN(10, ("sending mgt frame len=%u rate=%u xfer len=%u\n",
- m0->m_pkthdr.len, rate, xferlen));
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT,
@@ -1273,6 +1188,71 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+ural_sendprot(struct ural_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RAL_TX_RETRY(7);
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RAL_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RAL_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1281,14 +1261,17 @@ static int
ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
uint32_t flags;
usbd_status error;
int xferlen, rate;
- data = &sc->tx_data[0];
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
@@ -1297,8 +1280,22 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RAL_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = ural_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1307,16 +1304,12 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RAL_TX_ACK;
-
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
/* XXX need to setup descriptor ourself */
ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
@@ -1347,6 +1340,7 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1354,10 +1348,13 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
static int
ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
@@ -1366,25 +1363,42 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-
- rate &= IEEE80211_RATE_VAL;
+ rate = ni->ni_txrate;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
-
/* packet header may have moved, reset our local pointer */
wh = mtod(m0, struct ieee80211_frame *);
}
- data = &sc->tx_data[0];
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = ural_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
data->m = m0;
@@ -1394,12 +1408,12 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RAL_TX_ACK;
flags |= RAL_TX_RETRY(7);
- dur = ural_txtime(RAL_ACK_SIZE, ural_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1408,7 +1422,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
@@ -1440,6 +1454,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1448,77 +1463,30 @@ static void
ural_start(struct ifnet *ifp)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
struct ieee80211_node *ni;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= RAL_TX_LIST_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ural_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= RAL_TX_LIST_COUNT) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ural_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RAL_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (ural_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc);
}
}
@@ -1544,61 +1512,40 @@ ural_watchdog(void *arg)
RAL_UNLOCK(sc);
}
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-ural_reset(struct ifnet *ifp)
-{
- struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
-
- ural_set_chan(sc, ic->ic_curchan);
-
- return 0;
-}
-
static int
ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 1;
RAL_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ ural_init_locked(sc);
+ startall = 1;
+ } else
ural_update_promisc(sc);
- else
- ural_init(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ural_stop(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- ural_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1616,8 +1563,8 @@ ural_set_testmode(struct ural_softc *sc)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not set test mode: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not set test mode: %s\n",
+ usbd_errstr(error));
}
}
@@ -1635,8 +1582,8 @@ ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read EEPROM: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
}
}
@@ -1655,8 +1602,8 @@ ural_read(struct ural_softc *sc, uint16_t reg)
error = usbd_do_request(sc->sc_udev, &req, &val);
if (error != 0) {
- printf("%s: could not read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
return 0;
}
@@ -1677,8 +1624,8 @@ ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1696,8 +1643,8 @@ ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1715,8 +1662,8 @@ ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1731,7 +1678,7 @@ ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to BBP\n");
return;
}
@@ -1753,7 +1700,7 @@ ural_bbp_read(struct ural_softc *sc, uint8_t reg)
break;
}
if (ntries == 5) {
- printf("%s: could not read BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1771,7 +1718,7 @@ ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to RF\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to RF\n");
return;
}
@@ -1785,6 +1732,24 @@ ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
}
+/* ARGUSED */
+static struct ieee80211_node *
+ural_node_alloc(struct ieee80211_node_table *nt __unused)
+{
+ struct ural_node *un;
+
+ un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return un != NULL ? &un->ni : NULL;
+}
+
+static void
+ural_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni);
+}
+
static void
ural_scan_start(struct ieee80211com *ic)
{
@@ -1822,12 +1787,15 @@ ural_set_channel(struct ieee80211com *ic)
/* do it in a process context */
sc->sc_scan_action = URAL_SET_CHANNEL;
usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
}
static void
ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t power, tmp;
u_int i, chan;
@@ -1924,17 +1892,9 @@ ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
ural_disable_rf_tune(sc);
}
+ /* XXX doesn't belong here */
/* update basic rate set */
- if (IEEE80211_IS_CHAN_B(c)) {
- /* 11b basic rates: 1, 2Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x3);
- } else if (IEEE80211_IS_CHAN_A(c)) {
- /* 11a basic rates: 6, 12, 24Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x150);
- } else {
- /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x15f);
- }
+ ural_set_basicrates(sc, c);
}
/*
@@ -1963,13 +1923,15 @@ ural_disable_rf_tune(struct ural_softc *sc)
static void
ural_enable_tsf_sync(struct ural_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint16_t logcwmin, preload, tmp;
/* first, disable TSF synchronization */
ural_write(sc, RAL_TXRX_CSR19, 0);
- tmp = (16 * ic->ic_bss->ni_intval) << 4;
+ tmp = (16 * vap->iv_bss->ni_intval) << 4;
ural_write(sc, RAL_TXRX_CSR18, tmp);
logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
@@ -1992,7 +1954,7 @@ static void
ural_update_slot(struct ifnet *ifp)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t slottime, sifs, eifs;
slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
@@ -2017,32 +1979,33 @@ ural_update_slot(struct ifnet *ifp)
static void
ural_set_txpreamble(struct ural_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t tmp;
tmp = ural_read(sc, RAL_TXRX_CSR10);
tmp &= ~RAL_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RAL_SHORT_PREAMBLE;
ural_write(sc, RAL_TXRX_CSR10, tmp);
}
static void
-ural_set_basicrates(struct ural_softc *sc)
+ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
-
+ /* XXX wrong, take from rate set */
/* update basic rate set */
- if (ic->ic_curmode == IEEE80211_MODE_11B) {
- /* 11b basic rates: 1, 2Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x3);
- } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
/* 11a basic rates: 6, 12, 24Mbps */
ural_write(sc, RAL_TXRX_CSR11, 0x150);
- } else {
+ } else if (IEEE80211_IS_CHAN_ANYG(c)) {
/* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+ } else {
+ /* 11b basic rates: 1, 2Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x3);
}
}
@@ -2083,7 +2046,7 @@ ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
static void
ural_update_promisc(struct ural_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
tmp = ural_read(sc, RAL_TXRX_CSR2);
@@ -2116,7 +2079,8 @@ ural_get_rf(int rev)
static void
ural_read_eeprom(struct ural_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t val;
ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
@@ -2222,12 +2186,11 @@ ural_set_rxantenna(struct ural_softc *sc, int antenna)
}
static void
-ural_init(void *priv)
+ural_init_locked(struct ural_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct ural_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_rx_data *data;
uint16_t tmp;
usbd_status error;
@@ -2251,8 +2214,8 @@ ural_init(void *priv)
DELAY(1000);
}
if (ntries == 100) {
- printf("%s: timeout waiting for BBP/RF to wakeup\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
goto fail;
}
@@ -2281,8 +2244,7 @@ ural_init(void *priv)
*/
sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->amrr_xfer == NULL) {
- printf("%s: could not allocate AMRR xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
goto fail;
}
@@ -2292,16 +2254,16 @@ ural_init(void *priv)
error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
&sc->sc_tx_pipeh);
if (error != 0) {
- printf("%s: could not open Tx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
&sc->sc_rx_pipeh);
if (error != 0) {
- printf("%s: could not open Rx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
@@ -2310,15 +2272,13 @@ ural_init(void *priv)
*/
error = ural_alloc_tx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Tx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
goto fail;
}
error = ural_alloc_rx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Rx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
goto fail;
}
@@ -2346,13 +2306,6 @@ ural_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: ural_stop(sc);
@@ -2360,17 +2313,29 @@ fail: ural_stop(sc);
}
static void
+ural_init(void *priv)
+{
+ struct ural_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RAL_LOCK(sc);
+ ural_init_locked(sc);
+ RAL_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
ural_stop(void *priv)
{
struct ural_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
/* disable Rx */
ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);
@@ -2420,9 +2385,6 @@ ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return EIO;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
@@ -2453,27 +2415,22 @@ bad:
static void
ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni)
{
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ural_vap *uvp = URAL_VAP(vap);
/* clear statistic registers (STA_CSR0 to STA_CSR10) */
ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
- ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
-
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
-
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni);
- callout_reset(&sc->amrr_ch, hz, ural_amrr_timeout, sc);
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
}
static void
ural_amrr_timeout(void *arg)
{
- struct ural_softc *sc = (struct ural_softc *)arg;
+ struct ieee80211vap *vap = arg;
+ struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc;
usb_device_request_t req;
/*
@@ -2485,7 +2442,7 @@ ural_amrr_timeout(void *arg)
USETW(req.wIndex, RAL_STA_CSR0);
USETW(req.wLength, sizeof sc->sta);
- usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, sc,
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
ural_amrr_update);
(void)usbd_transfer(sc->amrr_xfer);
@@ -2495,8 +2452,12 @@ static void
ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
- struct ural_softc *sc = (struct ural_softc *)priv;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ieee80211vap *vap = priv;
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni = vap->iv_bss;
+ int ok, fail;
if (status != USBD_NORMAL_COMPLETION) {
device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
@@ -2504,19 +2465,15 @@ ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
return;
}
- /* count TX retry-fail as Tx errors */
- ifp->if_oerrors += sc->sta[9];
-
- sc->amn.amn_retrycnt =
- sc->sta[7] + /* TX one-retry ok count */
- sc->sta[8] + /* TX more-retry ok count */
- sc->sta[9]; /* TX retry-fail count */
+ ok = sc->sta[7] + /* TX ok w/o retry */
+ sc->sta[8]; /* TX ok w/ retry */
+ fail = sc->sta[9]; /* TX retry-fail count */
- sc->amn.amn_txcnt =
- sc->amn.amn_retrycnt +
- sc->sta[6]; /* TX no-retry ok count */
+ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn,
+ ok+fail, ok, sc->sta[8] + fail);
+ (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn);
- ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
- callout_reset(&sc->amrr_ch, hz, ural_amrr_timeout, sc);
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
}
diff --git a/sys/dev/usb/if_uralvar.h b/sys/dev/usb/if_uralvar.h
index 665b05b..39aef9e 100644
--- a/sys/dev/usb/if_uralvar.h
+++ b/sys/dev/usb/if_uralvar.h
@@ -18,7 +18,7 @@
*/
#define RAL_RX_LIST_COUNT 1
-#define RAL_TX_LIST_COUNT 1
+#define RAL_TX_LIST_COUNT 8
#define URAL_SCAN_START 1
#define URAL_SCAN_END 2
@@ -74,15 +74,31 @@ struct ural_rx_data {
struct mbuf *m;
};
+struct ural_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define URAL_NODE(ni) ((struct ural_node *)(ni))
+
+struct ural_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URAL_VAP(vap) ((struct ural_vap *)(vap))
+
struct ural_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
usbd_device_handle sc_udev;
usbd_interface_handle sc_iface;
+ const struct ieee80211_rate_table *sc_rates;
+
int sc_rx_no;
int sc_tx_no;
@@ -100,19 +116,14 @@ struct ural_softc {
struct usb_task sc_task;
struct usb_task sc_scantask;
- struct ieee80211_amrr amrr;
- struct ieee80211_amrr_node amn;
-
struct ural_rx_data rx_data[RAL_RX_LIST_COUNT];
struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
int tx_queued;
-
- struct ieee80211_beacon_offsets sc_bo;
+ int tx_cur;
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout amrr_ch;
int sc_tx_timer;
uint16_t sta[11];
@@ -130,20 +141,10 @@ struct ural_softc {
int tx_ant;
int nb_ant;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct ural_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct ural_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct ural_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct ural_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/usb/if_zyd.c b/sys/dev/usb/if_zyd.c
index 509d33c..bf51177 100644
--- a/sys/dev/usb/if_zyd.c
+++ b/sys/dev/usb/if_zyd.c
@@ -47,9 +47,8 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
-#include <net80211/ieee80211_proto.h>
-#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -67,10 +66,7 @@
#include <dev/usb/if_zydreg.h>
#include <dev/usb/if_zydfw.h>
-#ifdef USB_DEBUG
#define ZYD_DEBUG
-#endif
-
#ifdef ZYD_DEBUG
#define DPRINTF(x) do { if (zyddebug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (zyddebug > (n)) printf x; } while (0)
@@ -154,6 +150,11 @@ static device_probe_t zyd_match;
static device_attach_t zyd_attach;
static device_detach_t zyd_detach;
+static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void zyd_vap_delete(struct ieee80211vap *);
static int zyd_attachhook(struct zyd_softc *);
static int zyd_complete_attach(struct zyd_softc *);
static int zyd_open_pipes(struct zyd_softc *);
@@ -163,9 +164,8 @@ static void zyd_free_tx_list(struct zyd_softc *);
static int zyd_alloc_rx_list(struct zyd_softc *);
static void zyd_free_rx_list(struct zyd_softc *);
static struct ieee80211_node *zyd_node_alloc(struct ieee80211_node_table *);
-static int zyd_media_change(struct ifnet *);
static void zyd_task(void *);
-static int zyd_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int,
void *, int, u_int);
static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *);
@@ -205,11 +205,11 @@ static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *);
static int zyd_set_bssid(struct zyd_softc *, const uint8_t *);
static int zyd_switch_radio(struct zyd_softc *, int);
static void zyd_set_led(struct zyd_softc *, int, int);
-static void zyd_set_multi(struct zyd_softc *);
+static void zyd_set_multi(void *);
+static void zyd_update_mcast(struct ifnet *);
static int zyd_set_rxfilter(struct zyd_softc *);
static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *);
static int zyd_set_beacon_interval(struct zyd_softc *, int);
-static uint8_t zyd_plcp_signal(int);
static void zyd_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
static void zyd_rx_data(struct zyd_softc *, const uint8_t *, uint16_t);
static void zyd_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
@@ -219,13 +219,14 @@ static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *,
static int zyd_tx_data(struct zyd_softc *, struct mbuf *,
struct ieee80211_node *);
static void zyd_start(struct ifnet *);
+static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void zyd_watchdog(void *);
static int zyd_ioctl(struct ifnet *, u_long, caddr_t);
+static void zyd_init_locked(struct zyd_softc *);
static void zyd_init(void *);
static void zyd_stop(struct zyd_softc *, int);
static int zyd_loadfirmware(struct zyd_softc *, u_char *, size_t);
-static void zyd_iter_func(void *, struct ieee80211_node *);
-static void zyd_amrr_timeout(void *);
static void zyd_newassoc(struct ieee80211_node *, int);
static void zyd_scantask(void *);
static void zyd_scan_start(struct ieee80211com *);
@@ -282,7 +283,7 @@ zyd_attach(device_t dev)
sc->sc_dev = dev;
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
return ENXIO;
@@ -325,18 +326,18 @@ bad:
static int
zyd_complete_attach(struct zyd_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
usbd_status error;
- int bands;
+ uint8_t bands;
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
usb_init_task(&sc->sc_scantask, zyd_scantask, sc);
usb_init_task(&sc->sc_task, zyd_task, sc);
+ usb_init_task(&sc->sc_mcasttask, zyd_set_multi, sc);
- callout_init(&sc->sc_amrr_ch, 0);
callout_init(&sc->sc_watchdog_ch, 0);
error = usbd_set_config_no(sc->sc_udev, ZYD_CONFIG_NO, 1);
@@ -381,10 +382,11 @@ zyd_complete_attach(struct zyd_softc *sc)
sc->fw_rev >> 8, sc->fw_rev & 0xff, zyd_rf_name(sc->rf_rev),
sc->pa_rev, ether_sprintf(ic->ic_myaddr));
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ic->ic_myaddr);
+
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_opmode = IEEE80211_M_STA;
/* set device capabilities */
ic->ic_caps =
@@ -398,29 +400,22 @@ zyd_complete_attach(struct zyd_softc *sc)
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_node_alloc = zyd_node_alloc;
ic->ic_newassoc = zyd_newassoc;
-
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_raw_xmit = zyd_raw_xmit;
+ ic->ic_node_alloc = zyd_node_alloc;
ic->ic_scan_start = zyd_scan_start;
ic->ic_scan_end = zyd_scan_end;
ic->ic_set_channel = zyd_set_channel;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = zyd_newstate;
- ieee80211_media_init(ic, zyd_media_change, ieee80211_media_status);
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ ic->ic_vap_create = zyd_vap_create;
+ ic->ic_vap_delete = zyd_vap_delete;
+ ic->ic_update_mcast = zyd_update_mcast;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -447,8 +442,8 @@ static int
zyd_detach(device_t dev)
{
struct zyd_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
if (!device_is_attached(dev))
return 0;
@@ -457,17 +452,16 @@ zyd_detach(device_t dev)
ifp->if_flags &= ~IFF_UP;
zyd_stop(sc, 1);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_scantask);
usb_rem_task(sc->sc_udev, &sc->sc_task);
- callout_stop(&sc->sc_amrr_ch);
callout_stop(&sc->sc_watchdog_ch);
zyd_close_pipes(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
@@ -475,6 +469,51 @@ zyd_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+zyd_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct zyd_vap *zvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (zvp == NULL)
+ return NULL;
+ vap = &zvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ zvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = zyd_newstate;
+
+ ieee80211_amrr_init(&zvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+zyd_vap_delete(struct ieee80211vap *vap)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ ieee80211_amrr_cleanup(&zvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(zvp, M_80211_VAP);
+}
+
static int
zyd_open_pipes(struct zyd_softc *sc)
{
@@ -666,86 +705,70 @@ zyd_node_alloc(struct ieee80211_node_table *nt __unused)
return zn != NULL ? &zn->ni : NULL;
}
-static int
-zyd_media_change(struct ifnet *ifp)
-{
- struct zyd_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) == IFF_UP &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) == IFF_DRV_RUNNING)
- zyd_init(sc);
-
- return 0;
-}
-
static void
zyd_task(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- enum ieee80211_state ostate;
-
- ostate = ic->ic_state;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct zyd_vap *zvp = ZYD_VAP(vap);
switch (sc->sc_state) {
case IEEE80211_S_RUN:
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_node *ni = vap->iv_bss;
zyd_set_chan(sc, ic->ic_curchan);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
/* turn link LED on */
zyd_set_led(sc, ZYD_LED1, 1);
/* make data LED blink upon Tx */
zyd_write32(sc, sc->fwbase + ZYD_FW_LINK_STATUS, 1);
- zyd_set_bssid(sc, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ zyd_set_bssid(sc, sc->sc_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* fake a join to init the tx rate */
zyd_newassoc(ni, 1);
}
-
- /* start automatic rate control timer */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- callout_reset(&sc->sc_amrr_ch, hz,
- zyd_amrr_timeout, sc);
-
break;
}
default:
break;
}
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+ IEEE80211_LOCK(ic);
+ zvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static int
-zyd_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct zyd_softc *sc = ic->ic_ifp->if_softc;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- callout_stop(&sc->sc_amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ zvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
-
- return 0;
+ return EINPROGRESS;
+ }
}
static int
@@ -1582,7 +1605,8 @@ fail: return error;
static int
zyd_read_eeprom(struct zyd_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
uint16_t val;
int i;
@@ -1607,6 +1631,7 @@ zyd_read_eeprom(struct zyd_softc *sc)
(void)zyd_read32(sc, ZYD_EEPROM_SUBID, &tmp);
sc->regdomain = tmp >> 16;
DPRINTF(("regulatory domain %x\n", sc->regdomain));
+ /* XXX propagate to net80211 after mapping to SKU */
/* read Tx power calibration tables */
for (i = 0; i < 7; i++) {
@@ -1687,10 +1712,11 @@ zyd_set_led(struct zyd_softc *sc, int which, int on)
}
static void
-zyd_set_multi(struct zyd_softc *sc)
+zyd_set_multi(void *arg)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifmultiaddr *ifma;
uint32_t low, high;
uint8_t v;
@@ -1725,12 +1751,22 @@ zyd_set_multi(struct zyd_softc *sc)
zyd_write32(sc, ZYD_MAC_GHTBH, high);
}
+static void
+zyd_update_mcast(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+
+ usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER);
+}
+
static int
zyd_set_rxfilter(struct zyd_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t rxfilter;
- switch (sc->sc_ic.ic_opmode) {
+ switch (ic->ic_opmode) {
case IEEE80211_M_STA:
rxfilter = ZYD_FILTER_BSS;
break;
@@ -1751,7 +1787,8 @@ zyd_set_rxfilter(struct zyd_softc *sc)
static void
zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct zyd_rf *rf = &sc->sc_rf;
uint32_t tmp;
u_int chan;
@@ -1809,31 +1846,6 @@ zyd_set_beacon_interval(struct zyd_softc *sc, int bintval)
return 0;
}
-static uint8_t
-zyd_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
@@ -1857,8 +1869,9 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (le16toh(cmd->code) == ZYD_NOTIF_RETRYSTATUS) {
struct zyd_notif_retry *retry =
(struct zyd_notif_retry *)cmd->data;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ieee80211_node *ni;
DPRINTF(("retry intr: rate=0x%x addr=%s count=%d (0x%x)\n",
@@ -1870,15 +1883,12 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
* retry statistics. In BSS mode, this node is the AP we're
* associated to so no lookup is actually needed.
*/
- if (ic->ic_opmode != IEEE80211_M_STA) {
- ni = ieee80211_find_node(&ic->ic_sta, retry->macaddr);
- if (ni == NULL)
- return; /* just ignore */
- } else
- ni = ic->ic_bss;
-
- ((struct zyd_node *)ni)->amn.amn_retrycnt++;
-
+ ni = ieee80211_find_txnode(vap, retry->macaddr);
+ if (ni != NULL) {
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_FAILURE, 1);
+ ieee80211_free_node(ni);
+ }
if (le16toh(retry->count) & 0x100)
ifp->if_oerrors++; /* too many retries */
} else if (le16toh(cmd->code) == ZYD_NOTIF_IORD) {
@@ -1918,30 +1928,16 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
}
}
-static __inline uint8_t
-zyd_plcp2ieee(int signal, int isofdm)
-{
- if (isofdm) {
- static const uint8_t ofdmrates[16] =
- { 0, 0, 0, 0, 0, 0, 0, 96, 48, 24, 12, 108, 72, 36, 18 };
- return ofdmrates[signal & 0xf];
- } else {
- static const uint8_t cckrates[16] =
- { 0, 0, 0, 0, 4, 0, 0, 11, 0, 0, 2, 0, 0, 0, 22, 0 };
- return cckrates[signal & 0xf];
- }
-}
-
static void
zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_node *ni;
const struct zyd_plcphdr *plcp;
const struct zyd_rx_stat *stat;
struct mbuf *m;
- int rlen;
+ int rlen, rssi, nf;
if (len < ZYD_MIN_FRAGSZ) {
DPRINTF(("%s: frame too short (length=%d)\n",
@@ -1980,7 +1976,7 @@ zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
m->m_pkthdr.len = m->m_len = rlen;
bcopy((const uint8_t *)(plcp + 1), mtod(m, uint8_t *), rlen);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1989,20 +1985,23 @@ zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
/* XXX toss, no way to express errors */
if (stat->flags & ZYD_RX_DECRYPTERR)
tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
- tap->wr_rate =
- zyd_plcp2ieee(plcp->signal, stat->flags & ZYD_RX_OFDM);
+ tap->wr_rate = ieee80211_plcp2rate(plcp->signal,
+ stat->flags & ZYD_RX_OFDM);
tap->wr_antsignal = stat->rssi + -95;
tap->wr_antnoise = -95; /* XXX */
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
- ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
- ieee80211_input(ic, m, ni,
- stat->rssi > 63 ? 127 : 2 * stat->rssi, -95/*XXX*/, 0);
+ rssi = stat->rssi > 63 ? 127 : 2 * stat->rssi;
+ nf = -95; /* XXX */
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf, 0);
}
static void
@@ -2067,7 +2066,8 @@ skip: /* setup a new transfer */
static int
zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = sc->sc_ifp;
struct zyd_tx_desc *desc;
struct zyd_tx_data *data;
@@ -2085,7 +2085,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -2106,7 +2106,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->flags = ZYD_TX_FLAG_BACKOFF;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
/* multicast frames are not sent at OFDM rates in 802.11b/g */
- if (totlen > ic->ic_rtsthreshold) {
+ if (totlen > vap->iv_rtsthreshold) {
desc->flags |= ZYD_TX_FLAG_RTS;
} else if (ZYD_RATE_IS_OFDM(rate) &&
(ic->ic_flags & IEEE80211_F_USEPROT)) {
@@ -2123,7 +2123,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
(IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
- desc->phy = zyd_plcp_signal(rate);
+ desc->phy = ieee80211_rate2plcp(rate);
if (ZYD_RATE_IS_OFDM(rate)) {
desc->phy |= ZYD_TX_PHY_OFDM;
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
@@ -2145,13 +2145,13 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->plcp_service |= ZYD_PLCP_LENGEXT;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len,
@@ -2200,7 +2200,8 @@ zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
ni = data->ni;
/* update rate control statistics */
- ((struct zyd_node *)ni)->amn.amn_txcnt++;
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_SUCCESS, 0);
/*
* Do any tx complete callback. Note this must
@@ -2227,11 +2228,13 @@ zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
static int
zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = sc->sc_ifp;
struct zyd_tx_desc *desc;
struct zyd_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
int xferlen, totlen, rate;
uint16_t pktlen;
@@ -2242,17 +2245,19 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc = (struct zyd_tx_desc *)data->buf;
desc->flags = ZYD_TX_FLAG_BACKOFF;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- rate = ic->ic_mcast_rate;
+ rate = tp->mcastrate;
desc->flags |= ZYD_TX_FLAG_MULTICAST;
- } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
- else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
- rate &= IEEE80211_RATE_VAL;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
+ } else {
+ (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -2273,7 +2278,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
/* multicast frames are not sent at OFDM rates in 802.11b/g */
- if (totlen > ic->ic_rtsthreshold) {
+ if (totlen > vap->iv_rtsthreshold) {
desc->flags |= ZYD_TX_FLAG_RTS;
} else if (ZYD_RATE_IS_OFDM(rate) &&
(ic->ic_flags & IEEE80211_F_USEPROT)) {
@@ -2289,7 +2294,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
(IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
- desc->phy = zyd_plcp_signal(rate);
+ desc->phy = ieee80211_rate2plcp(rate);
if (ZYD_RATE_IS_OFDM(rate)) {
desc->phy |= ZYD_TX_PHY_OFDM;
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
@@ -2311,7 +2316,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->plcp_service |= ZYD_PLCP_LENGEXT;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -2319,7 +2324,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len,
@@ -2348,81 +2353,81 @@ static void
zyd_start(struct ifnet *ifp)
{
struct zyd_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ether_header *eh;
struct ieee80211_node *ni;
- struct mbuf *m0;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (zyd_tx_mgt(sc, m0, ni) != 0)
- break;
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_POLL(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IFQ_DEQUEUE(&ifp->if_snd, m0);
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof(struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof(struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- if (bpf_peers_present(ifp->if_bpf))
- bpf_mtap(ifp->if_bpf, m0);
- if ((m0 = ieee80211_encap(ic, m0, ni)) == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (zyd_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (zyd_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
sc->tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
}
}
+static int
+zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
+
+ ifp->if_opackets++;
+
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (zyd_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ sc->tx_timer = 5;
+ callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
+
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
+}
+
static void
zyd_watchdog(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (sc->tx_timer > 0) {
if (--sc->tx_timer == 0) {
@@ -2439,11 +2444,11 @@ static int
zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct zyd_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
ZYD_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
@@ -2451,43 +2456,36 @@ zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if ((ifp->if_flags ^ sc->sc_if_flags) &
(IFF_ALLMULTI | IFF_PROMISC))
zyd_set_multi(sc);
- } else
- zyd_init(sc);
+ } else {
+ zyd_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
zyd_stop(sc, 1);
}
sc->sc_if_flags = ifp->if_flags;
break;
-
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- zyd_set_multi(sc);
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
-
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) == IFF_UP &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) == IFF_DRV_RUNNING)
- zyd_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
ZYD_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
static void
-zyd_init(void *priv)
+zyd_init_locked(struct zyd_softc *sc)
{
- struct zyd_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i, error;
zyd_stop(sc, 0);
@@ -2569,13 +2567,6 @@ zyd_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: zyd_stop(sc, 1);
@@ -2583,12 +2574,24 @@ fail: zyd_stop(sc, 1);
}
static void
-zyd_stop(struct zyd_softc *sc, int disable)
+zyd_init(void *priv)
{
+ struct zyd_softc *sc = priv;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* free all nodes */
+ ZYD_LOCK(sc);
+ zyd_init_locked(sc);
+ ZYD_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+zyd_stop(struct zyd_softc *sc, int disable)
+{
+ struct ifnet *ifp = sc->sc_ifp;
sc->tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
@@ -2661,43 +2664,11 @@ zyd_loadfirmware(struct zyd_softc *sc, u_char *fw, size_t size)
}
static void
-zyd_iter_func(void *arg, struct ieee80211_node *ni)
-{
- struct zyd_softc *sc = arg;
- struct zyd_node *zn = (struct zyd_node *)ni;
-
- ieee80211_amrr_choose(&sc->amrr, ni, &zn->amn);
-}
-
-static void
-zyd_amrr_timeout(void *arg)
-{
- struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- ZYD_LOCK(sc);
- if (ic->ic_opmode == IEEE80211_M_STA)
- zyd_iter_func(sc, ic->ic_bss);
- else
- ieee80211_iterate_nodes(&ic->ic_sta, zyd_iter_func, sc);
- ZYD_UNLOCK(sc);
-
- callout_reset(&sc->sc_amrr_ch, hz, zyd_amrr_timeout, sc);
-}
-
-static void
zyd_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct zyd_softc *sc = ni->ni_ic->ic_ifp->if_softc;
- int i;
-
- ieee80211_amrr_node_init(&sc->amrr, &((struct zyd_node *)ni)->amn);
+ struct ieee80211vap *vap = ni->ni_vap;
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni);
}
static void
@@ -2740,18 +2711,20 @@ static void
zyd_scantask(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ZYD_LOCK(sc);
switch (sc->sc_scan_action) {
case ZYD_SCAN_START:
+ /* want broadcast address while scanning */
zyd_set_bssid(sc, ifp->if_broadcastaddr);
break;
case ZYD_SCAN_END:
- zyd_set_bssid(sc, ic->ic_bss->ni_bssid);
+ /* restore previous bssid */
+ zyd_set_bssid(sc, sc->sc_bssid);
break;
case ZYD_SET_CHANNEL:
diff --git a/sys/dev/usb/if_zydreg.h b/sys/dev/usb/if_zydreg.h
index fc736ae..4d9c8fd 100644
--- a/sys/dev/usb/if_zydreg.h
+++ b/sys/dev/usb/if_zydreg.h
@@ -1122,6 +1122,7 @@ struct zyd_node {
struct ieee80211_node ni; /* must be the first */
struct ieee80211_amrr_node amn;
};
+#define ZYD_NODE(ni) ((struct zyd_node *)(ni))
struct zyd_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
@@ -1173,12 +1174,18 @@ struct rq {
STAILQ_ENTRY(rq) rq;
};
+struct zyd_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout amrr_ch;
+ struct ieee80211_amrr amrr;
+};
+#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
+
struct zyd_softc {
device_t sc_dev;
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
struct zyd_rf sc_rf;
struct usb_task sc_task;
@@ -1187,22 +1194,20 @@ struct zyd_softc {
#define ZYD_SCAN_START 0
#define ZYD_SCAN_END 1
#define ZYD_SET_CHANNEL 2
+ struct usb_task sc_mcasttask;
usbd_device_handle sc_udev;
usbd_interface_handle sc_iface;
int sc_flags;
int sc_if_flags;
#define ZD1211_FWLOADED (1 << 0)
-
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
enum ieee80211_state sc_state;
int sc_arg;
struct mtx sc_mtx;
- struct callout sc_amrr_ch;
struct callout sc_watchdog_ch;
- struct ieee80211_amrr amrr;
-
STAILQ_HEAD(rqh, rq) sc_rqh;
uint16_t fwbase;
@@ -1233,8 +1238,6 @@ struct zyd_softc {
int tx_timer;
- struct bpf_if *sc_drvbpf;
-
struct zyd_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
diff --git a/sys/dev/wi/if_wavelan_ieee.h b/sys/dev/wi/if_wavelan_ieee.h
index 0a04bed..0061e63 100644
--- a/sys/dev/wi/if_wavelan_ieee.h
+++ b/sys/dev/wi/if_wavelan_ieee.h
@@ -241,10 +241,13 @@ struct wi_counters {
#define WI_RID_CNFAUTHMODE 0xFC2A
#define WI_RID_ROAMING_MODE 0xFC2D
#define WI_RID_OWN_BEACON_INT 0xFC33 /* beacon xmit time for BSS creation */
+#define WI_RID_ENH_SECURITY 0xFC43 /* enhanced security (AP mode) */
#define WI_RID_CNF_DBM_ADJUST 0xFC46
#define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */
+#define WI_RID_WPA_DATA 0xFC48 /* WPA IE */
#define WI_RID_BASIC_RATE 0xFCB3
#define WI_RID_SUPPORT_RATE 0xFCB4
+#define WI_RID_WPA_HANDLING 0xFCBB /* WPA handling procedures */
/*
* Network parameters, dynamic configuration entities
diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c
index 9ff16a4..d750ab1 100644
--- a/sys/dev/wi/if_wi.c
+++ b/sys/dev/wi/if_wi.c
@@ -1,5 +1,3 @@
-/* $NetBSD: wi.c,v 1.109 2003/01/09 08:52:19 dyoung Exp $ */
-
/*-
* Copyright (c) 1997, 1998, 1999
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
@@ -64,11 +62,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#define WI_HERMES_AUTOINC_WAR /* Work around data write autoinc bug. */
#define WI_HERMES_STATS_WAR /* Work around stats counter bug. */
-#define NBPFILTER 1
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/endian.h>
@@ -83,6 +78,7 @@ __FBSDID("$FreeBSD$");
#include <sys/random.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -112,33 +108,41 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wireg.h>
#include <dev/wi/if_wivar.h>
+static struct ieee80211vap *wi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void wi_vap_delete(struct ieee80211vap *vap);
+static void wi_stop_locked(struct wi_softc *sc, int disable);
static void wi_start_locked(struct ifnet *);
static void wi_start(struct ifnet *);
static int wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr,
struct mbuf *m0);
static int wi_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
-static int wi_reset(struct ifnet *);
+static int wi_newstate_sta(struct ieee80211vap *, enum ieee80211_state, int);
+static int wi_newstate_hostap(struct ieee80211vap *, enum ieee80211_state, int);
+static void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+ int subtype, int rssi, int noise, u_int32_t rstamp);
+static int wi_reset(struct wi_softc *);
static void wi_watchdog(void *);
static int wi_ioctl(struct ifnet *, u_long, caddr_t);
-static int wi_media_change(struct ifnet *);
static void wi_media_status(struct ifnet *, struct ifmediareq *);
static void wi_rx_intr(struct wi_softc *);
static void wi_tx_intr(struct wi_softc *);
static void wi_tx_ex_intr(struct wi_softc *);
-static void wi_info_intr(struct wi_softc *);
-static int wi_key_alloc(struct ieee80211com *, const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
+static void wi_status_connected(void *, int);
+static void wi_status_disconnected(void *, int);
+static void wi_status_oor(void *, int);
+static void wi_status_assoc_failed(void *, int);
+static void wi_info_intr(struct wi_softc *);
-#if 0
-static int wi_get_cfg(struct ifnet *, u_long, caddr_t);
-static int wi_set_cfg(struct ifnet *, u_long, caddr_t);
-#endif
-static int wi_write_txrate(struct wi_softc *);
-static int wi_write_wep(struct wi_softc *);
+static int wi_write_txrate(struct wi_softc *, struct ieee80211vap *);
+static int wi_write_wep(struct wi_softc *, struct ieee80211vap *);
static int wi_write_multi(struct wi_softc *);
+static void wi_update_mcast(struct ifnet *);
static int wi_alloc_fid(struct wi_softc *, int, int *);
static void wi_read_nicid(struct wi_softc *);
static int wi_write_ssid(struct wi_softc *, int, u_int8_t *, int);
@@ -150,33 +154,11 @@ static int wi_write_bap(struct wi_softc *, int, int, void *, int);
static int wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int);
static int wi_read_rid(struct wi_softc *, int, void *, int *);
static int wi_write_rid(struct wi_softc *, int, void *, int);
-
-static int wi_newstate(struct ieee80211com *, enum ieee80211_state, int);
-
-static int wi_scan_ap(struct wi_softc *, u_int16_t, u_int16_t);
-static void wi_scan_result(struct wi_softc *, int, int);
-
-static void wi_dump_pkt(struct wi_frame *, struct ieee80211_node *, int rssi);
-
-#if 0
-static int wi_get_debug(struct wi_softc *, struct wi_req *);
-static int wi_set_debug(struct wi_softc *, struct wi_req *);
-#endif
-
-/* support to download firmware for symbol CF card */
-static int wi_symbol_write_firm(struct wi_softc *, const void *, int,
- const void *, int);
-static int wi_symbol_set_hcr(struct wi_softc *, int);
+static int wi_write_appie(struct wi_softc *, int, const struct ieee80211_appie *);
static void wi_scan_start(struct ieee80211com *);
-static void wi_scan_curchan(struct ieee80211com *, unsigned long);
-static void wi_scan_mindwell(struct ieee80211com *);
static void wi_scan_end(struct ieee80211com *);
static void wi_set_channel(struct ieee80211com *);
-static void wi_update_slot(struct ifnet *);
-static struct ieee80211_node *wi_node_alloc(struct ieee80211_node_table *);
-static int wi_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data);
-static int wi_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data);
static __inline int
wi_write_val(struct wi_softc *sc, int rid, u_int16_t val)
@@ -199,15 +181,9 @@ SYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate,
static int wi_debug = 0;
SYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug,
0, "control debugging printfs");
-
#define DPRINTF(X) if (wi_debug) printf X
-#define DPRINTF2(X) if (wi_debug > 1) printf X
-#define IFF_DUMPPKTS(_ifp) \
- (((_ifp)->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
#else
#define DPRINTF(X)
-#define DPRINTF2(X)
-#define IFF_DUMPPKTS(_ifp) 0
#endif
#define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO)
@@ -255,7 +231,7 @@ int
wi_attach(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
int i, nrates, buflen;
u_int16_t val;
@@ -266,13 +242,13 @@ wi_attach(device_t dev)
};
int error;
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc\n");
wi_free(dev);
- return (ENOSPC);
+ return ENOSPC;
}
- ifp->if_softc = sc;
+ ic = ifp->if_l2com;
/*
* NB: no locking is needed here; don't put it here
@@ -284,18 +260,40 @@ wi_attach(device_t dev)
if (error) {
device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
wi_free(dev);
- return (error);
+ return error;
}
- mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
- MTX_DEF | MTX_RECURSE);
- callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
-
sc->sc_firmware_type = WI_NOTYPE;
sc->wi_cmd_count = 500;
/* Reset the NIC. */
- if (wi_reset(ifp) != 0)
+ if (wi_reset(sc) != 0) {
+ wi_free(dev);
return ENXIO; /* XXX */
+ }
+
+ /* Read NIC identification */
+ wi_read_nicid(sc);
+ switch (sc->sc_firmware_type) {
+ case WI_LUCENT:
+ if (sc->sc_sta_firmware_ver < 60006)
+ goto reject;
+ break;
+ case WI_INTERSIL:
+ if (sc->sc_sta_firmware_ver < 800)
+ goto reject;
+ break;
+ default:
+ reject:
+ device_printf(dev, "Sorry, this card is not supported "
+ "(type %d, firmware ver %d)\n",
+ sc->sc_firmware_type, sc->sc_sta_firmware_ver);
+ wi_free(dev);
+ return EOPNOTSUPP;
+ }
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
/*
* Read the station address.
@@ -320,9 +318,7 @@ wi_attach(device_t dev)
return (error);
}
- /* Read NIC identification */
- wi_read_nicid(sc);
-
+ 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_ioctl = wi_ioctl;
@@ -335,11 +331,9 @@ wi_attach(device_t dev)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
- ic->ic_state = IEEE80211_S_INIT;
ic->ic_caps = IEEE80211_C_PMGT
- | IEEE80211_C_WEP /* everyone supports WEP */
+ | IEEE80211_C_MONITOR
;
- ic->ic_max_aid = WI_MAX_AID;
/*
* Query the card for available channels and setup the
@@ -360,28 +354,7 @@ wi_attach(device_t dev)
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
c->ic_flags = IEEE80211_CHAN_B;
c->ic_ieee = i;
- }
-
- /*
- * Read the default channel from the NIC. This may vary
- * depending on the country where the NIC was purchased, so
- * we can't hard-code a default and expect it to work for
- * everyone.
- *
- * If no channel is specified, let the 802.11 code select.
- */
- buflen = sizeof(val);
- if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0) {
- val = le16toh(val);
- ic->ic_bsschan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(val, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (ic->ic_bsschan == NULL)
- ic->ic_bsschan = &ic->ic_channels[0];
- } else {
- device_printf(dev,
- "WI_RID_OWN_CHNL failed, using first channel!\n");
- ic->ic_bsschan = &ic->ic_channels[0];
+ /* XXX txpowers? */
}
/*
@@ -390,31 +363,18 @@ wi_attach(device_t dev)
switch (sc->sc_firmware_type) {
case WI_LUCENT:
sc->sc_ntxbuf = 1;
- sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
-#ifdef WI_HERMES_AUTOINC_WAR
- /* XXX: not confirmed, but never seen for recent firmware */
- if (sc->sc_sta_firmware_ver < 40000) {
- sc->sc_flags |= WI_FLAGS_BUG_AUTOINC;
- }
-#endif
- if (sc->sc_sta_firmware_ver >= 60000)
- sc->sc_flags |= WI_FLAGS_HAS_MOR;
- if (sc->sc_sta_firmware_ver >= 60006) {
- ic->ic_caps |= IEEE80211_C_IBSS;
- ic->ic_caps |= IEEE80211_C_MONITOR;
- }
- sc->sc_ibss_port = htole16(1);
+ ic->ic_caps |= IEEE80211_C_IBSS;
+ sc->sc_ibss_port = WI_PORTTYPE_BSS;
+ sc->sc_monitor_port = WI_PORTTYPE_ADHOC;
sc->sc_min_rssi = WI_LUCENT_MIN_RSSI;
sc->sc_max_rssi = WI_LUCENT_MAX_RSSI;
sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET;
break;
-
case WI_INTERSIL:
sc->sc_ntxbuf = WI_NTXBUF;
- sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR;
- sc->sc_flags |= WI_FLAGS_HAS_ROAMING;
- sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
+ sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR
+ | WI_FLAGS_HAS_ROAMING;
/*
* Old firmware are slow, so give peace a chance.
*/
@@ -422,31 +382,26 @@ wi_attach(device_t dev)
sc->wi_cmd_count = 5000;
if (sc->sc_sta_firmware_ver > 10101)
sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST;
- if (sc->sc_sta_firmware_ver >= 800) {
- ic->ic_caps |= IEEE80211_C_IBSS;
- ic->ic_caps |= IEEE80211_C_MONITOR;
- }
+ ic->ic_caps |= IEEE80211_C_IBSS;
/*
* version 0.8.3 and newer are the only ones that are known
* to currently work. Earlier versions can be made to work,
- * at least according to the Linux driver.
+ * at least according to the Linux driver but we require
+ * monitor mode so this is irrelevant.
*/
- if (sc->sc_sta_firmware_ver >= 803)
- ic->ic_caps |= IEEE80211_C_HOSTAP;
- sc->sc_ibss_port = htole16(0);
-
- sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
- sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
- sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
- break;
-
- case WI_SYMBOL:
- sc->sc_ntxbuf = 1;
- sc->sc_flags |= WI_FLAGS_HAS_DIVERSITY;
- if (sc->sc_sta_firmware_ver >= 25000)
- ic->ic_caps |= IEEE80211_C_IBSS;
- sc->sc_ibss_port = htole16(4);
+ ic->ic_caps |= IEEE80211_C_HOSTAP;
+ if (sc->sc_sta_firmware_ver >= 10603)
+ sc->sc_flags |= WI_FLAGS_HAS_ENHSECURITY;
+ if (sc->sc_sta_firmware_ver >= 10700) {
+ /*
+ * 1.7.0+ have the necessary support for sta mode WPA.
+ */
+ sc->sc_flags |= WI_FLAGS_HAS_WPASUPPORT;
+ ic->ic_caps |= IEEE80211_C_WPA;
+ }
+ sc->sc_ibss_port = WI_PORTTYPE_IBSS;
+ sc->sc_monitor_port = WI_PORTTYPE_APSILENT;
sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
@@ -459,7 +414,7 @@ wi_attach(device_t dev)
buflen = sizeof(val);
if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 &&
val != htole16(0))
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
/* Find supported rates. */
buflen = sizeof(ratebuf);
@@ -482,48 +437,21 @@ wi_attach(device_t dev)
sc->sc_dbm_offset = le16toh(val);
}
- sc->sc_max_datalen = 2304;
- sc->sc_system_scale = 1;
- sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN;
- sc->sc_roaming_mode = 1;
- sc->wi_channel = IEEE80211_CHAN_ANYC;
sc->sc_portnum = WI_DEFAULT_PORT;
- sc->sc_authtype = WI_DEFAULT_AUTHTYPE;
-
- bzero(sc->sc_nodename, sizeof(sc->sc_nodename));
- sc->sc_nodelen = sizeof(WI_DEFAULT_NODENAME) - 1;
- bcopy(WI_DEFAULT_NODENAME, sc->sc_nodename, sc->sc_nodelen);
+ TASK_INIT(&sc->sc_oor_task, 0, wi_status_oor, ic);
- bzero(sc->sc_net_name, sizeof(sc->sc_net_name));
- bcopy(WI_DEFAULT_NETNAME, sc->sc_net_name,
- sizeof(WI_DEFAULT_NETNAME) - 1);
-
- /*
- * Call MI attach routine.
- */
ieee80211_ifattach(ic);
- /* override state transition method */
- sc->sc_newstate = ic->ic_newstate;
- sc->sc_key_alloc = ic->ic_crypto.cs_key_alloc;
- ic->ic_crypto.cs_key_alloc = wi_key_alloc;
- ic->ic_newstate = wi_newstate;
ic->ic_raw_xmit = wi_raw_xmit;
-
ic->ic_scan_start = wi_scan_start;
- ic->ic_scan_curchan = wi_scan_curchan;
- ic->ic_scan_mindwell = wi_scan_mindwell;
ic->ic_scan_end = wi_scan_end;
ic->ic_set_channel = wi_set_channel;
- ic->ic_node_alloc = wi_node_alloc;
- ic->ic_updateslot = wi_update_slot;
- ic->ic_reset = wi_reset;
- ieee80211_media_init(ic, wi_media_change, wi_media_status);
+ ic->ic_vap_create = wi_vap_create;
+ ic->ic_vap_delete = wi_vap_delete;
+ ic->ic_update_mcast = wi_update_mcast;
-#if NBPFILTER > 0
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th));
/*
* Initialize constant fields.
* XXX make header lengths a multiple of 32-bits so subsequent
@@ -540,7 +468,6 @@ wi_attach(device_t dev)
sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t));
sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
sc->sc_rx_th.wr_ihdr.it_present = htole32(WI_RX_RADIOTAP_PRESENT);
-#endif
if (bootverbose)
ieee80211_announce(ic);
@@ -553,19 +480,18 @@ wi_detach(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
WI_LOCK(sc);
/* check if device was removed */
sc->wi_gone |= !bus_child_present(dev);
- wi_stop(ifp, 0);
+ wi_stop_locked(sc, 0);
WI_UNLOCK(sc);
-
-#if NBPFILTER > 0
bpfdetach(ifp);
-#endif
- ieee80211_ifdetach(&sc->sc_ic);
+ ieee80211_ifdetach(ic);
+
bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
if_free(sc->sc_ifp);
wi_free(dev);
@@ -573,60 +499,82 @@ wi_detach(device_t dev)
return (0);
}
-#ifdef __NetBSD__
-int
-wi_activate(struct device *self, enum devact act)
+static struct ieee80211vap *
+wi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct wi_softc *sc = (struct wi_softc *)self;
- int rv = 0, s;
+ struct wi_softc *sc = ic->ic_ifp->if_softc;
+ struct wi_vap *wvp;
+ struct ieee80211vap *vap;
- s = splnet();
- switch (act) {
- case DVACT_ACTIVATE:
- rv = EOPNOTSUPP;
- break;
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ wvp = (struct wi_vap *) malloc(sizeof(struct wi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (wvp == NULL)
+ return NULL;
- case DVACT_DEACTIVATE:
- if_deactivate(sc->sc_ifp);
- break;
- }
- splx(s);
- return rv;
-}
+ vap = &wvp->wv_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
-void
-wi_power(struct wi_softc *sc, int why)
-{
- struct ifnet *ifp = sc->sc_ifp;
- int s;
+ vap->iv_max_aid = WI_MAX_AID;
- s = splnet();
- switch (why) {
- case PWR_SUSPEND:
- case PWR_STANDBY:
- wi_stop(ifp, 1);
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ sc->sc_porttype = WI_PORTTYPE_BSS;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_sta;
+ /* need to filter mgt frames to avoid confusing state machine */
+ wvp->wv_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = wi_recv_mgmt;
break;
- case PWR_RESUME:
- if (ifp->if_flags & IFF_UP) {
- wi_init(sc);
- (void)wi_intr(sc);
- }
+ case IEEE80211_M_IBSS:
+ sc->sc_porttype = sc->sc_ibss_port;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_sta;
+ break;
+ case IEEE80211_M_AHDEMO:
+ sc->sc_porttype = WI_PORTTYPE_ADHOC;
+ break;
+ case IEEE80211_M_HOSTAP:
+ sc->sc_porttype = WI_PORTTYPE_HOSTAP;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_hostap;
+ break;
+ case IEEE80211_M_MONITOR:
+ sc->sc_porttype = sc->sc_monitor_port;
break;
- case PWR_SOFTSUSPEND:
- case PWR_SOFTSTANDBY:
- case PWR_SOFTRESUME:
+ default:
break;
}
- splx(s);
+
+ TASK_INIT(&wvp->wv_connected_task, 0, wi_status_connected, vap);
+ TASK_INIT(&wvp->wv_disconnected_task, 0, wi_status_disconnected, vap);
+ TASK_INIT(&wvp->wv_assoc_failed_task, 0, wi_status_assoc_failed, vap);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+wi_vap_delete(struct ieee80211vap *vap)
+{
+ struct wi_vap *wvp = WI_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(wvp, M_80211_VAP);
}
-#endif /* __NetBSD__ */
void
wi_shutdown(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- wi_stop(sc->sc_ifp, 1);
+ wi_stop(sc, 1);
}
void
@@ -658,7 +606,6 @@ wi_intr(void *arg)
if (status & WI_EV_INFO)
wi_info_intr(sc);
if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
- (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0 &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
wi_start_locked(ifp);
@@ -670,257 +617,344 @@ wi_intr(void *arg)
return;
}
+static void
+wi_enable(struct wi_softc *sc)
+{
+ /* Enable interrupts */
+ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
+
+ /* enable port */
+ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
+ sc->sc_enabled = 1;
+}
+
+static int
+wi_setup_locked(struct wi_softc *sc, int porttype, int mode,
+ uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ int i;
+
+ wi_reset(sc);
+
+ wi_write_val(sc, WI_RID_PORTTYPE, porttype);
+ wi_write_val(sc, WI_RID_CREATE_IBSS, mode);
+ wi_write_val(sc, WI_RID_MAX_DATALEN, 2304);
+ /* XXX IEEE80211_BPF_NOACK wants 0 */
+ wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 2);
+ if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
+ wi_write_val(sc, WI_RID_ROAMING_MODE, 3); /* NB: disabled */
+
+ wi_write_rid(sc, WI_RID_MAC_NODE, mac, IEEE80211_ADDR_LEN);
+
+ /* Allocate fids for the card */
+ sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
+ for (i = 0; i < sc->sc_ntxbuf; i++) {
+ int error = wi_alloc_fid(sc, sc->sc_buflen,
+ &sc->sc_txd[i].d_fid);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "tx buffer allocation failed (error %u)\n",
+ error);
+ return error;
+ }
+ sc->sc_txd[i].d_len = 0;
+ }
+ sc->sc_txcur = sc->sc_txnext = 0;
+
+ return 0;
+}
+
+static void
+wi_init_locked(struct wi_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ int wasenabled;
+
+ WI_LOCK_ASSERT(sc);
+
+ wasenabled = sc->sc_enabled;
+ if (wasenabled)
+ wi_stop_locked(sc, 1);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ if (wi_setup_locked(sc, sc->sc_porttype, 3, ic->ic_myaddr) != 0) {
+ if_printf(ifp, "interface not running\n");
+ wi_stop_locked(sc, 1);
+ return;
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
+
+ wi_enable(sc); /* Enable desired port */
+}
+
void
wi_init(void *arg)
{
struct wi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
- struct wi_joinreq join;
- struct ieee80211_channel *chan;
- int i;
- int error = 0, wasenabled;
+ struct ieee80211com *ic = ifp->if_l2com;
+ WI_LOCK(sc);
+ wi_init_locked(sc);
+ WI_UNLOCK(sc);
+ ieee80211_start_all(ic);
+}
- if (sc->wi_gone)
- return;
+static void
+wi_stop_locked(struct wi_softc *sc, int disable)
+{
+ struct ifnet *ifp = sc->sc_ifp;
- if ((wasenabled = sc->sc_enabled))
- wi_stop(ifp, 1);
+ WI_LOCK_ASSERT(sc);
- WI_LOCK(sc);
- wi_reset(ifp);
+ if (sc->sc_enabled && !sc->wi_gone) {
+ CSR_WRITE_2(sc, WI_INT_EN, 0);
+ wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
+ if (disable)
+ sc->sc_enabled = 0;
+ } else if (sc->wi_gone && disable) /* gone --> not enabled */
+ sc->sc_enabled = 0;
- /* common 802.11 configuration */
- ic->ic_flags &= ~IEEE80211_F_IBSSON;
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_BSS);
- break;
- case IEEE80211_M_IBSS:
- wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_ibss_port);
- ic->ic_flags |= IEEE80211_F_IBSSON;
- break;
- case IEEE80211_M_AHDEMO:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC);
- break;
- case IEEE80211_M_HOSTAP:
- /*
- * For PRISM cards, override the empty SSID, because in
- * HostAP mode the controller will lock up otherwise.
- */
- if (sc->sc_firmware_type == WI_INTERSIL &&
- ic->ic_des_ssid[0].len == 0) {
- ic->ic_des_ssid[0].ssid[0] = ' ';
- ic->ic_des_ssid[0].len = 1;
- }
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP);
- break;
- case IEEE80211_M_MONITOR:
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC);
- break;
-
- case WI_INTERSIL:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_APSILENT);
- break;
- }
+ callout_stop(&sc->sc_watchdog);
+ sc->sc_tx_timer = 0;
+ sc->sc_false_syns = 0;
- wi_cmd(sc, WI_CMD_DEBUG | (WI_TEST_MONITOR << 8), 0, 0, 0);
- break;
- case IEEE80211_M_WDS:
- /* XXXX */
- break;
- }
+ ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
+}
- /* Intersil interprets this RID as joining ESS even in IBSS mode */
- if (sc->sc_firmware_type == WI_LUCENT &&
- (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_ssid[0].len > 0)
- wi_write_val(sc, WI_RID_CREATE_IBSS, 1);
- else
- wi_write_val(sc, WI_RID_CREATE_IBSS, 0);
- wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
- wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- wi_write_val(sc, WI_RID_OWN_CHNL,
- ieee80211_chan2ieee(ic, ic->ic_bsschan));
- wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
+void
+wi_stop(struct wi_softc *sc, int disable)
+{
+ WI_LOCK(sc);
+ wi_stop_locked(sc, disable);
+ WI_UNLOCK(sc);
+}
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN);
+static void
+wi_set_channel(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
- if (ic->ic_caps & IEEE80211_C_PMGT)
- wi_write_val(sc, WI_RID_PM_ENABLED,
- (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
+ DPRINTF(("%s: channel %d, %sscanning\n", __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ic->ic_flags & IEEE80211_F_SCAN ? "" : "!"));
- /* not yet common 802.11 configuration */
- wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen);
- wi_write_val(sc, WI_RID_RTS_THRESH, ic->ic_rtsthreshold);
- if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
- wi_write_val(sc, WI_RID_FRAG_THRESH, ic->ic_fragthreshold);
+ WI_LOCK(sc);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, ic->ic_curchan));
- /* driver specific 802.11 configuration */
- if (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)
- wi_write_val(sc, WI_RID_SYSTEM_SCALE, sc->sc_system_scale);
- if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
- wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
- if (sc->sc_flags & WI_FLAGS_HAS_MOR)
- wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
- wi_write_txrate(sc);
- wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
- wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 0); /* for IEEE80211_BPF_NOACK */
+ sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
+ htole16(ic->ic_curchan->ic_freq);
+ sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
+ htole16(ic->ic_curchan->ic_flags);
+ WI_UNLOCK(sc);
+}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_bintval);
- wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */
- wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */
- wi_write_val(sc, WI_RID_DTIM_PERIOD, ic->ic_dtim_period);
- }
+static void
+wi_scan_start(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ DPRINTF(("%s\n", __func__));
+
+ WI_LOCK(sc);
/*
- * Initialize promisc mode.
- * Being in the Host-AP mode causes a great
- * deal of pain if primisc mode is set.
- * Therefore we avoid confusing the firmware
- * and always reset promisc mode in Host-AP
- * mode. Host-AP sees all the packets anyway.
+ * Switch device to monitor mode.
*/
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- (ifp->if_flags & IFF_PROMISC) != 0) {
- wi_write_val(sc, WI_RID_PROMISC, 1);
- } else {
- wi_write_val(sc, WI_RID_PROMISC, 0);
+ wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_monitor_port);
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
+ wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
}
+ /* force full dwell time to compensate for firmware overhead */
+ ss->ss_mindwell = ss->ss_maxdwell = msecs_to_ticks(400);
+ WI_UNLOCK(sc);
- /* Configure WEP. */
- if (ic->ic_caps & IEEE80211_C_WEP) {
- sc->sc_cnfauthmode = ic->ic_bss->ni_authmode;
- wi_write_wep(sc);
- } else
- sc->sc_encryption = 0;
+}
+
+static void
+wi_scan_end(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
- /* Set multicast filter. */
- wi_write_multi(sc);
+ DPRINTF(("%s: restore port type %d\n", __func__, sc->sc_porttype));
- /* Allocate fids for the card */
- if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) {
- sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
- if (sc->sc_firmware_type == WI_SYMBOL)
- sc->sc_buflen = 1585; /* XXX */
- for (i = 0; i < sc->sc_ntxbuf; i++) {
- error = wi_alloc_fid(sc, sc->sc_buflen,
- &sc->sc_txd[i].d_fid);
- if (error) {
- device_printf(sc->sc_dev,
- "tx buffer allocation failed (error %u)\n",
- error);
- goto out;
- }
- sc->sc_txd[i].d_len = 0;
- }
+ WI_LOCK(sc);
+ wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_porttype);
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
+ wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
}
- sc->sc_txcur = sc->sc_txnext = 0;
+ WI_UNLOCK(sc);
+}
- /* Enable desired port */
- wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
+static void
+wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+ int subtype, int rssi, int noise, u_int32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
- sc->sc_enabled = 1;
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_HOSTAP) {
- chan = (sc->wi_channel == IEEE80211_CHAN_ANYC) ?
- ic->ic_curchan : sc->wi_channel;
- ieee80211_create_ibss(ic, chan);
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ /* NB: filter frames that trigger state changes */
+ return;
}
- /* Enable interrupts */
- CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
+ WI_VAP(vap)->wv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+}
- if (!wasenabled &&
- ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- /* XXX: some card need to be re-enabled for hostap */
- wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
- wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
- }
+static int
+wi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_node *bss;
+ struct wi_softc *sc = ifp->if_softc;
+
+ DPRINTF(("%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]));
+
+ if (nstate == IEEE80211_S_AUTH) {
+ WI_LOCK(sc);
+ wi_setup_locked(sc, WI_PORTTYPE_BSS, 3, vap->iv_myaddr);
+
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
+ wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
+ wi_write_val(sc, WI_RID_PM_ENABLED, 1);
+ }
+ wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
+ if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
+ wi_write_val(sc, WI_RID_FRAG_THRESH,
+ vap->iv_fragthreshold);
+ wi_write_txrate(sc, vap);
+
+ bss = vap->iv_bss;
+ wi_write_ssid(sc, WI_RID_DESIRED_SSID, bss->ni_essid, bss->ni_esslen);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+
+ /* Configure WEP. */
+ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
+ wi_write_wep(sc, vap);
+ else
+ sc->sc_encryption = 0;
+
+ if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
+ (vap->iv_flags & IEEE80211_F_WPA)) {
+ wi_write_val(sc, WI_RID_WPA_HANDLING, 1);
+ if (vap->iv_appie_wpa != NULL)
+ wi_write_appie(sc, WI_RID_WPA_DATA,
+ vap->iv_appie_wpa);
+ }
+
+ wi_enable(sc); /* enable port */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ((ic->ic_flags & IEEE80211_F_DESBSSID) ||
- ic->ic_des_chan != IEEE80211_CHAN_ANYC)) {
- memset(&join, 0, sizeof(join));
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(&join.wi_bssid, ic->ic_des_bssid);
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC)
- join.wi_chan = htole16(
- ieee80211_chan2ieee(ic, ic->ic_des_chan));
/* Lucent firmware does not support the JOIN RID. */
- if (sc->sc_firmware_type != WI_LUCENT)
- wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
- }
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ struct wi_joinreq join;
- callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
+ memset(&join, 0, sizeof(join));
+ IEEE80211_ADDR_COPY(&join.wi_bssid, bss->ni_bssid);
+ join.wi_chan = htole16(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
+ }
+ WI_UNLOCK(sc);
- WI_UNLOCK(sc);
- return;
-out:
- if (error) {
- if_printf(ifp, "interface not running\n");
- wi_stop(ifp, 1);
+ /*
+ * NB: don't go through 802.11 layer, it'll send auth frame;
+ * instead we drive the state machine from the link status
+ * notification we get on association.
+ */
+ vap->iv_state = nstate;
+ return EINPROGRESS;
}
- WI_UNLOCK(sc);
- DPRINTF(("wi_init: return %d\n", error));
- return;
+ return WI_VAP(vap)->wv_newstate(vap, nstate, arg);
}
-void
-wi_stop(struct ifnet *ifp, int disable)
+static int
+wi_newstate_hostap(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_node *bss;
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+
+ DPRINTF(("%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]));
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ error = WI_VAP(vap)->wv_newstate(vap, nstate, arg);
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ WI_LOCK(sc);
+ wi_setup_locked(sc, WI_PORTTYPE_HOSTAP, 0, vap->iv_myaddr);
- DELAY(100000);
- WI_LOCK(sc);
- if (sc->sc_enabled && !sc->wi_gone) {
- CSR_WRITE_2(sc, WI_INT_EN, 0);
- wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
- if (disable) {
-#ifdef __NetBSD__
- if (sc->sc_disable)
- (*sc->sc_disable)(sc);
-#endif
- sc->sc_enabled = 0;
+ bss = vap->iv_bss;
+ wi_write_ssid(sc, WI_RID_OWN_SSID,
+ bss->ni_essid, bss->ni_esslen);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ wi_write_val(sc, WI_RID_BASIC_RATE, 0x3);
+ wi_write_val(sc, WI_RID_SUPPORT_RATE, 0xf);
+ wi_write_txrate(sc, vap);
+
+ wi_write_val(sc, WI_RID_OWN_BEACON_INT, bss->ni_intval);
+ wi_write_val(sc, WI_RID_DTIM_PERIOD, vap->iv_dtim_period);
+
+ wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
+ if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
+ wi_write_val(sc, WI_RID_FRAG_THRESH,
+ vap->iv_fragthreshold);
+
+ if ((sc->sc_flags & WI_FLAGS_HAS_ENHSECURITY) &&
+ (vap->iv_flags & IEEE80211_F_HIDESSID)) {
+ /*
+ * bit 0 means hide SSID in beacons,
+ * bit 1 means don't respond to bcast probe req
+ */
+ wi_write_val(sc, WI_RID_ENH_SECURITY, 0x3);
}
- } else if (sc->wi_gone && disable) /* gone --> not enabled */
- sc->sc_enabled = 0;
- callout_stop(&sc->sc_watchdog); /* XXX drain */
- sc->sc_tx_timer = 0;
- sc->sc_scan_timer = 0;
- sc->sc_false_syns = 0;
- sc->sc_naps = 0;
- ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
+ if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
+ (vap->iv_flags & IEEE80211_F_WPA) &&
+ vap->iv_appie_wpa != NULL)
+ wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa);
- WI_UNLOCK(sc);
+ wi_write_val(sc, WI_RID_PROMISC, 0);
+
+ /* Configure WEP. */
+ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
+ wi_write_wep(sc, vap);
+ else
+ sc->sc_encryption = 0;
+
+ wi_enable(sc); /* enable port */
+ WI_UNLOCK(sc);
+ }
+ return error;
}
static void
wi_start_locked(struct ifnet *ifp)
{
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni;
struct ieee80211_frame *wh;
- struct ether_header *eh;
struct mbuf *m0;
+ struct ieee80211_key *k;
struct wi_frame frmhdr;
int cur;
@@ -928,83 +962,34 @@ wi_start_locked(struct ifnet *ifp)
if (sc->wi_gone)
return;
- if (sc->sc_flags & WI_FLAGS_OUTRANGE)
- return;
memset(&frmhdr, 0, sizeof(frmhdr));
cur = sc->sc_txnext;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->sc_txd[cur].d_len != 0) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
- (caddr_t)&frmhdr.wi_ehdr);
- frmhdr.wi_ehdr.ether_type = 0;
- wh = mtod(m0, struct ieee80211_frame *);
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->sc_txd[cur].d_len != 0) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- if (m0->m_len < sizeof(struct ether_header) &&
- (m0 = m_pullup(m0, sizeof(struct ether_header))) == NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- ifp->if_opackets++;
- m_copydata(m0, 0, ETHER_HDR_LEN,
- (caddr_t)&frmhdr.wi_ehdr);
-#if NBPFILTER > 0
- BPF_MTAP(ifp, m0);
-#endif
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+ if (sc->sc_txd[cur].d_len != 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ /* NB: copy before 802.11 header is prepended */
+ m_copydata(m0, 0, ETHER_HDR_LEN,
+ (caddr_t)&frmhdr.wi_ehdr);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ifp->if_oerrors++;
- ieee80211_free_node(ni);
- continue;
- }
- wh = mtod(m0, struct ieee80211_frame *);
+ ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
+ m0 = ieee80211_encap(ni, m0);
+ if (m0 == NULL) {
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ continue;
}
-#if NBPFILTER > 0
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-#endif
- frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
- /* XXX check key for SWCRYPT instead of using operating mode */
- if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
- (sc->sc_encryption & HOST_ENCRYPT)) {
- struct ieee80211_key *k;
- k = ieee80211_crypto_encap(ic, ni, m0);
+ wh = mtod(m0, struct ieee80211_frame *);
+ frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
ieee80211_free_node(ni);
m_freem(m0);
@@ -1012,24 +997,23 @@ wi_start_locked(struct ifnet *ifp)
}
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
}
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
- sc->sc_tx_th.wt_rate =
- ni->ni_rates.rs_rates[ni->ni_txrate];
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ sc->sc_tx_th.wt_rate = ni->ni_txrate;
+ bpf_mtap2(ifp->if_bpf,
+ &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
-#endif
+
m_copydata(m0, 0, sizeof(struct ieee80211_frame),
(caddr_t)&frmhdr.wi_whdr);
m_adj(m0, sizeof(struct ieee80211_frame));
frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, -1);
ieee80211_free_node(ni);
if (wi_start_tx(ifp, &frmhdr, m0))
continue;
+
sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
+ ifp->if_opackets++;
}
}
@@ -1078,6 +1062,7 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = ic->ic_ifp;
struct wi_softc *sc = ifp->if_softc;
+ struct ieee80211_key *k;
struct ieee80211_frame *wh;
struct wi_frame frmhdr;
int cur;
@@ -1089,11 +1074,6 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
rc = ENETDOWN;
goto out;
}
- if (sc->sc_flags & WI_FLAGS_OUTRANGE) {
- rc = ENETDOWN;
- goto out;
- }
-
memset(&frmhdr, 0, sizeof(frmhdr));
cur = sc->sc_txnext;
if (sc->sc_txd[cur].d_len != 0) {
@@ -1108,42 +1088,26 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
frmhdr.wi_ehdr.ether_type = 0;
wh = mtod(m0, struct ieee80211_frame *);
-#if NBPFILTER > 0
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-#endif
frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
if (params && (params->ibp_flags & IEEE80211_BPF_NOACK))
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
- /* XXX check key for SWCRYPT instead of using operating mode */
if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
- (sc->sc_encryption & HOST_ENCRYPT)) {
- if (!params ||
- (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO))) {
- struct ieee80211_key *k;
-
- k = ieee80211_crypto_encap(ic, ni, m0);
- if (k == NULL) {
- rc = ENOMEM;
- goto out;
- }
- frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
+ (!params || (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO)))) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ rc = ENOMEM;
+ goto out;
}
+ frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
}
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
- sc->sc_tx_th.wt_rate =
- ni->ni_rates.rs_rates[ni->ni_txrate];
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ if (bpf_peers_present(ifp->if_bpf)) {
+ sc->sc_tx_th.wt_rate = ni->ni_txrate;
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
-#endif
m_copydata(m0, 0, sizeof(struct ieee80211_frame),
(caddr_t)&frmhdr.wi_whdr);
m_adj(m0, sizeof(struct ieee80211_frame));
frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, -1);
if (wi_start_tx(ifp, &frmhdr, m0) < 0) {
m0 = NULL;
rc = EIO;
@@ -1162,32 +1126,21 @@ out:
}
static int
-wi_reset(struct ifnet *ifp)
+wi_reset(struct wi_softc *sc)
{
- struct wi_softc *sc = ifp->if_softc;
#define WI_INIT_TRIES 3
- int i;
- int error = 0;
- int tries;
-
- /* Symbol firmware cannot be initialized more than once */
- if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_reset)
- return (0);
- if (sc->sc_firmware_type == WI_SYMBOL)
- tries = 1;
- else
- tries = WI_INIT_TRIES;
+ int i, error = 0;
- for (i = 0; i < tries; i++) {
- if ((error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0)) == 0)
+ for (i = 0; i < WI_INIT_TRIES; i++) {
+ error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0);
+ if (error == 0)
break;
DELAY(WI_DELAY * 1000);
}
sc->sc_reset = 1;
-
- if (i == tries) {
- if_printf(ifp, "init failed\n");
- return (error);
+ if (i == WI_INIT_TRIES) {
+ if_printf(sc->sc_ifp, "reset failed\n");
+ return error;
}
CSR_WRITE_2(sc, WI_INT_EN, 0);
@@ -1196,7 +1149,7 @@ wi_reset(struct ifnet *ifp)
/* Calibrate timer. */
wi_write_val(sc, WI_RID_TICK_TIME, 8);
- return (0);
+ return 0;
#undef WI_INIT_TRIES
}
@@ -1206,28 +1159,17 @@ wi_watchdog(void *arg)
struct wi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
+ WI_LOCK_ASSERT(sc);
+
if (!sc->sc_enabled)
return;
- if (sc->sc_tx_timer) {
- if (--sc->sc_tx_timer == 0) {
- if_printf(ifp, "device timeout\n");
- ifp->if_oerrors++;
- wi_init(ifp->if_softc);
- return;
- }
- }
-
- if (sc->sc_scan_timer) {
- if (--sc->sc_scan_timer <= WI_SCAN_WAIT - WI_SCAN_INQWAIT &&
- sc->sc_firmware_type == WI_INTERSIL) {
- DPRINTF(("wi_watchdog: inquire scan\n"));
- wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
- }
+ if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ ifp->if_oerrors++;
+ wi_init_locked(ifp->if_softc);
+ return;
}
-
- /* TODO: rate control */
-
callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
}
@@ -1235,17 +1177,11 @@ static int
wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
- struct thread *td = curthread;
-#if 0
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
-#endif
-
- if (sc->wi_gone)
- return (ENODEV);
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ WI_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
/*
@@ -1253,234 +1189,54 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* changing is the promisc flag, try to short-circuit a call to
* wi_init() by just setting PROMISC in the hardware.
*/
- WI_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if (ifp->if_flags & IFF_PROMISC &&
- !(sc->sc_if_flags & IFF_PROMISC)) {
- wi_write_val(sc, WI_RID_PROMISC, 1);
- } else if (!(ifp->if_flags & IFF_PROMISC) &&
- sc->sc_if_flags & IFF_PROMISC) {
- wi_write_val(sc, WI_RID_PROMISC, 0);
+ if ((ifp->if_flags ^ sc->sc_if_flags) & IFF_PROMISC) {
+ wi_write_val(sc, WI_RID_PROMISC,
+ (ifp->if_flags & IFF_PROMISC) != 0);
} else {
- wi_init(sc);
+ wi_init_locked(sc);
+ startall = 1;
}
} else {
- wi_init(sc);
+ wi_init_locked(sc);
+ startall = 1;
}
} else {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- wi_stop(ifp, 1);
- }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ wi_stop_locked(sc, 1);
sc->wi_gone = 0;
}
sc->sc_if_flags = ifp->if_flags;
- WI_UNLOCK(sc);
- error = 0;
- break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- WI_LOCK(sc);
- error = wi_write_multi(sc);
- WI_UNLOCK(sc);
- break;
-#if 0
- case SIOCGIFGENERIC:
- WI_LOCK(sc);
- error = wi_get_cfg(ifp, cmd, data);
- WI_UNLOCK(sc);
- break;
- case SIOCSIFGENERIC:
- error = priv_check(td, PRIV_DRIVER);
- if (error == 0)
- error = wi_set_cfg(ifp, cmd, data);
- break;
- case SIOCGPRISM2DEBUG:
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- break;
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) ||
- sc->sc_firmware_type == WI_LUCENT) {
- error = EIO;
- break;
- }
- error = wi_get_debug(sc, &wreq);
- if (error == 0)
- error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
- break;
- case SIOCSPRISM2DEBUG:
- if ((error = priv_check(td, PRIV_DRIVER)))
- return (error);
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- break;
- WI_LOCK(sc);
- error = wi_set_debug(sc, &wreq);
- WI_UNLOCK(sc);
- break;
-#endif
- case SIOCG80211:
- error = wi_ioctl_get(ifp, cmd, data);
- break;
- case SIOCS80211:
- error = priv_check(td, PRIV_NET80211_MANAGE);
- if (error)
- break;
- error = wi_ioctl_set(ifp, cmd, data);
-
-
- break;
- default:
- error = ieee80211_ioctl(ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
- break;
- }
- return (error);
-}
-
-static int
-wi_ioctl_get(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- int error;
- struct wi_softc *sc;
- struct ieee80211req *ireq;
- struct ieee80211com *ic;
-
-
- sc = ifp->if_softc;
- ic = &sc->sc_ic;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_STATIONNAME:
- ireq->i_len = sc->sc_nodelen + 1;
- error = copyout(sc->sc_nodename, ireq->i_data,
- ireq->i_len);
break;
- default:
- error = ieee80211_ioctl(ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
-
- break;
- }
-
- return (error);
-}
-
-static int
-wi_ioctl_set(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- int error;
- struct wi_softc *sc;
- struct ieee80211req *ireq;
- u_int8_t nodename[IEEE80211_NWID_LEN];
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_STATIONNAME:
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- memset(nodename, 0, IEEE80211_NWID_LEN);
- error = copyin(ireq->i_data, nodename, ireq->i_len);
- if (error)
- break;
- WI_LOCK(sc);
- if (sc->sc_enabled) {
- error = wi_write_ssid(sc, WI_RID_NODENAME,
- nodename, ireq->i_len);
- }
- if (error == 0) {
- memcpy(sc->sc_nodename, nodename,
- IEEE80211_NWID_LEN);
- sc->sc_nodelen = ireq->i_len;
- }
- WI_UNLOCK(sc);
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
default:
- error = ieee80211_ioctl(&sc->sc_ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
+ error = ether_ioctl(ifp, cmd, data);
break;
}
+ WI_UNLOCK(sc);
- return (error);
-}
-
-static struct ieee80211_node *
-wi_node_alloc(struct ieee80211_node_table *nt)
-{
- struct wi_node *rn;
-
- rn = malloc(sizeof (struct wi_node), M_80211_NODE,
- M_NOWAIT | M_ZERO);
-
- return (rn != NULL) ? &rn->ni : NULL;
-}
-
-static int
-wi_media_change(struct ifnet *ifp)
-{
- struct wi_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
static void
wi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct wi_softc *sc = ic->ic_ifp->if_softc;
u_int16_t val;
int rate, len;
- if (sc->wi_gone) { /* hardware gone (e.g. ejected) */
- imr->ifm_active = IFM_IEEE80211 | IFM_NONE;
- imr->ifm_status = 0;
- return;
- }
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (!sc->sc_enabled) { /* port !enabled, have no status */
- imr->ifm_active |= IFM_NONE;
- imr->ifm_status = IFM_AVALID;
- return;
- }
- if (ic->ic_state == IEEE80211_S_RUN &&
- (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0)
- imr->ifm_status |= IFM_ACTIVE;
len = sizeof(val);
- if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
+ if (sc->sc_enabled &&
+ wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
len == sizeof(val)) {
/* convert to 802.11 rate */
val = le16toh(val);
@@ -1494,36 +1250,18 @@ wi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
else if (rate == 8*2)
rate = 22; /* 11Mbps */
}
- } else
- rate = 0;
- imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_AHDEMO:
- imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- case IEEE80211_M_WDS:
- /* XXXX */
- break;
+ vap->iv_bss->ni_txrate = rate;
}
+ ieee80211_media_status(ifp, imr);
}
static void
wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
return;
@@ -1553,93 +1291,11 @@ wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
#endif
}
-static void
-wi_rx_monitor(struct wi_softc *sc, int fid)
-{
- struct ifnet *ifp = sc->sc_ifp;
- struct wi_frame *rx_frame;
- struct mbuf *m;
- int datlen, hdrlen;
-
- /* first allocate mbuf for packet storage */
- m = m_getcl(M_DONTWAIT, MT_DATA, 0);
- if (m == NULL) {
- ifp->if_ierrors++;
- return;
- }
-
- m->m_pkthdr.rcvif = ifp;
-
- /* now read wi_frame first so we know how much data to read */
- if (wi_read_bap(sc, fid, 0, mtod(m, caddr_t), sizeof(*rx_frame))) {
- ifp->if_ierrors++;
- goto done;
- }
-
- rx_frame = mtod(m, struct wi_frame *);
-
- switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) {
- case 7:
- switch (rx_frame->wi_whdr.i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
- case IEEE80211_FC0_TYPE_DATA:
- hdrlen = WI_DATA_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- case IEEE80211_FC0_TYPE_MGT:
- hdrlen = WI_MGMT_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- case IEEE80211_FC0_TYPE_CTL:
- /*
- * prism2 cards don't pass control packets
- * down properly or consistently, so we'll only
- * pass down the header.
- */
- hdrlen = WI_CTL_HDRLEN;
- datlen = 0;
- break;
- default:
- if_printf(ifp, "received packet of unknown type "
- "on port 7\n");
- ifp->if_ierrors++;
- goto done;
- }
- break;
- case 0:
- hdrlen = WI_DATA_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- default:
- if_printf(ifp, "received packet on invalid "
- "port (wi_status=0x%x)\n", rx_frame->wi_status);
- ifp->if_ierrors++;
- goto done;
- }
-
- if (hdrlen + datlen + 2 > MCLBYTES) {
- if_printf(ifp, "oversized packet received "
- "(wi_dat_len=%d, wi_status=0x%x)\n",
- datlen, rx_frame->wi_status);
- ifp->if_ierrors++;
- goto done;
- }
-
- if (wi_read_bap(sc, fid, hdrlen, mtod(m, caddr_t) + hdrlen,
- datlen + 2) == 0) {
- m->m_pkthdr.len = m->m_len = hdrlen + datlen;
- ifp->if_ipackets++;
- BPF_MTAP(ifp, m); /* Handle BPF listeners. */
- } else
- ifp->if_ierrors++;
-done:
- m_freem(m);
-}
-
-static void
+static __noinline void
wi_rx_intr(struct wi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wi_frame frmhdr;
struct mbuf *m;
struct ieee80211_frame *wh;
@@ -1651,16 +1307,6 @@ wi_rx_intr(struct wi_softc *sc)
fid = CSR_READ_2(sc, WI_RX_FID);
- if (sc->wi_debug.wi_monitor) {
- /*
- * If we are in monitor mode just
- * read the data from the device.
- */
- wi_rx_monitor(sc, fid);
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
- return;
- }
-
/* First read in the frame header */
if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) {
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
@@ -1669,9 +1315,6 @@ wi_rx_intr(struct wi_softc *sc)
return;
}
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, frmhdr.wi_rx_signal);
-
/*
* Drop undecryptable or packets with receive errors here
*/
@@ -1703,24 +1346,16 @@ wi_rx_intr(struct wi_softc *sc)
len = 0;
}
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (off + len > MHLEN)
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m == NULL) {
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
ifp->if_ierrors++;
DPRINTF(("wi_rx_intr: MGET failed\n"));
return;
}
- if (off + len > MHLEN) {
- MCLGET(m, M_DONTWAIT);
- if ((m->m_flags & M_EXT) == 0) {
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
- m_freem(m);
- ifp->if_ierrors++;
- DPRINTF(("wi_rx_intr: MCLGET failed\n"));
- return;
- }
- }
-
m->m_data += off - sizeof(struct ieee80211_frame);
memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame));
wi_read_bap(sc, fid, sizeof(frmhdr),
@@ -1730,8 +1365,7 @@ wi_rx_intr(struct wi_softc *sc)
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
/* XXX replace divide by table */
sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5;
sc->sc_rx_th.wr_antsignal = frmhdr.wi_rx_signal;
@@ -1739,49 +1373,30 @@ wi_rx_intr(struct wi_softc *sc)
sc->sc_rx_th.wr_flags = 0;
if (frmhdr.wi_status & WI_STAT_PCF)
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_CFP;
- /* XXX IEEE80211_RADIOTAP_F_WEP */
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_rx_th, sc->sc_rx_th_len, m);
- }
-#endif
- wh = mtod(m, struct ieee80211_frame *);
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- /*
- * WEP is decrypted by hardware and the IV
- * is stripped. Clear WEP bit so we don't
- * try to process it in ieee80211_input.
- * XXX fix for TKIP, et. al.
- */
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ if (m->m_flags & M_WEP)
+ sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP;
+ bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
}
/* synchronize driver's BSSID with firmware's BSSID */
+ wh = mtod(m, struct ieee80211_frame *);
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
wi_sync_bssid(sc, wh->i_addr3);
WI_UNLOCK(sc);
- /*
- * Locate the node for sender, track state, and
- * then pass this node (referenced) up to the 802.11
- * layer for its use.
- */
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh);
- /*
- * Send frame up for processing.
- */
- ieee80211_input(ic, m, ni, rssi, -95/*XXXXwi_rx_silence?*/, rstamp);
- /*
- * The frame may have caused the node to be marked for
- * reclamation (e.g. in response to a DEAUTH message)
- * so use free_node here instead of unref_node.
- */
- ieee80211_free_node(ni);
+
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, -95/*XXX*/, rstamp);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, -95/*XXX*/, rstamp);
WI_LOCK(sc);
}
-static void
+static __noinline void
wi_tx_ex_intr(struct wi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
@@ -1792,7 +1407,6 @@ wi_tx_ex_intr(struct wi_softc *sc)
/* Read in the frame header */
if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
u_int16_t status = le16toh(frmhdr.wi_status);
-
/*
* Spontaneous station disconnects appear as xmit
* errors. Don't announce them and/or count them
@@ -1825,7 +1439,7 @@ wi_tx_ex_intr(struct wi_softc *sc)
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
}
-static void
+static __noinline void
wi_tx_intr(struct wi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
@@ -1860,10 +1474,52 @@ wi_tx_intr(struct wi_softc *sc)
}
static void
+wi_status_connected(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ WI_VAP(vap)->wv_newstate(vap, IEEE80211_S_RUN, 0);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+wi_status_disconnected(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_deauth++;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+}
+
+static void
+wi_status_oor(void *arg, int pending)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_beacon_miss(ic);
+}
+
+static void
+wi_status_assoc_failed(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT);
+}
+
+static __noinline void
wi_info_intr(struct wi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct wi_vap *wvp = WI_VAP(vap);
int i, fid, len, off;
u_int16_t ltbuf[2];
u_int16_t stat;
@@ -1873,42 +1529,35 @@ wi_info_intr(struct wi_softc *sc)
wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf));
switch (le16toh(ltbuf[1])) {
-
case WI_INFO_LINK_STAT:
wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat));
DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat)));
switch (le16toh(stat)) {
case WI_INFO_LINK_STAT_CONNECTED:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- if (ic->ic_state == IEEE80211_S_RUN &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
break;
- /* FALLTHROUGH */
+ /* fall thru... */
case WI_INFO_LINK_STAT_AP_CHG:
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ taskqueue_enqueue(taskqueue_swi, &wvp->wv_connected_task);
break;
case WI_INFO_LINK_STAT_AP_INR:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
+ break;
+ case WI_INFO_LINK_STAT_DISCONNECTED:
+ /* we dropped off the net; e.g. due to deauth/disassoc */
+ taskqueue_enqueue(taskqueue_swi, &wvp->wv_disconnected_task);
break;
case WI_INFO_LINK_STAT_AP_OOR:
- if (sc->sc_firmware_type == WI_SYMBOL &&
- sc->sc_scan_timer > 0) {
- if (wi_cmd(sc, WI_CMD_INQUIRE,
- WI_INFO_HOST_SCAN_RESULTS, 0, 0) != 0)
- sc->sc_scan_timer = 0;
- break;
- }
- if (ic->ic_opmode == IEEE80211_M_STA)
- sc->sc_flags |= WI_FLAGS_OUTRANGE;
+ /* XXX does this need to be per-vap? */
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_oor_task);
break;
- case WI_INFO_LINK_STAT_DISCONNECTED:
case WI_INFO_LINK_STAT_ASSOC_FAILED:
- if (ic->ic_opmode == IEEE80211_M_STA)
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ taskqueue_enqueue(taskqueue_swi,
+ &wvp->wv_assoc_failed_task);
break;
}
break;
-
case WI_INFO_COUNTERS:
/* some card versions have a larger stats structure */
len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4);
@@ -1926,13 +1575,6 @@ wi_info_intr(struct wi_softc *sc)
sc->sc_stats.wi_tx_multi_retries +
sc->sc_stats.wi_tx_retry_limit;
break;
-
- case WI_INFO_SCAN_RESULTS:
- case WI_INFO_HOST_SCAN_RESULTS:
- wi_scan_result(sc, fid, le16toh(ltbuf[0]));
- ieee80211_scan_done(ic);
- break;
-
default:
DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid,
le16toh(ltbuf[1]), le16toh(ltbuf[0])));
@@ -1973,6 +1615,12 @@ allmulti:
}
static void
+wi_update_mcast(struct ifnet *ifp)
+{
+ wi_write_multi(ifp->if_softc);
+}
+
+static void
wi_read_nicid(struct wi_softc *sc)
{
struct wi_card_ident *id;
@@ -2063,502 +1711,37 @@ wi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen)
return wi_write_rid(sc, rid, &ssid, sizeof(ssid));
}
-#if 0
-static int
-wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
- struct wi_scan_res *res;
- size_t reslen;
- int len, n, error, mif, val, off, i;
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- len = (wreq.wi_len - 1) * 2;
- if (len < sizeof(u_int16_t))
- return ENOSPC;
- if (len > sizeof(wreq.wi_val))
- len = sizeof(wreq.wi_val);
-
- switch (wreq.wi_type) {
-
- case WI_RID_IFACE_STATS:
- memcpy(wreq.wi_val, &sc->sc_stats, sizeof(sc->sc_stats));
- if (len < sizeof(sc->sc_stats))
- error = ENOSPC;
- else
- len = sizeof(sc->sc_stats);
- break;
-
- case WI_RID_ENCRYPTION:
- case WI_RID_TX_CRYPT_KEY:
- case WI_RID_DEFLT_CRYPT_KEYS:
- case WI_RID_TX_RATE:
- return ieee80211_ioctl(ic, cmd, data);
-
- case WI_RID_MICROWAVE_OVEN:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_microwave_oven);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_DBM_ADJUST:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_DBMADJUST)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_dbm_offset);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_ROAMING_MODE:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_ROAMING)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_roaming_mode);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_SYSTEM_SCALE:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_system_scale);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_FRAG_THRESH:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_READ_APS:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- return ieee80211_ioctl(ic, cmd, data);
- if (sc->sc_scan_timer > 0) {
- error = EINPROGRESS;
- break;
- }
- n = sc->sc_naps;
- if (len < sizeof(n)) {
- error = ENOSPC;
- break;
- }
- if (len < sizeof(n) + sizeof(struct wi_apinfo) * n)
- n = (len - sizeof(n)) / sizeof(struct wi_apinfo);
- len = sizeof(n) + sizeof(struct wi_apinfo) * n;
- memcpy(wreq.wi_val, &n, sizeof(n));
- memcpy((caddr_t)wreq.wi_val + sizeof(n), sc->sc_aps,
- sizeof(struct wi_apinfo) * n);
- break;
-
- case WI_RID_PRISM2:
- wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT;
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_MIF:
- mif = wreq.wi_val[0];
- error = wi_cmd(sc, WI_CMD_READMIF, mif, 0, 0);
- val = CSR_READ_2(sc, WI_RESP0);
- wreq.wi_val[0] = val;
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_ZERO_CACHE:
- case WI_RID_PROCFRAME: /* ignore for compatibility */
- /* XXX ??? */
- break;
-
- case WI_RID_READ_CACHE:
- return ieee80211_ioctl(ic, cmd, data);
-
- case WI_RID_SCAN_RES: /* compatibility interface */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- return ieee80211_ioctl(ic, cmd, data);
- if (sc->sc_scan_timer > 0) {
- error = EINPROGRESS;
- break;
- }
- n = sc->sc_naps;
- if (sc->sc_firmware_type == WI_LUCENT) {
- off = 0;
- reslen = WI_WAVELAN_RES_SIZE;
- } else {
- off = sizeof(struct wi_scan_p2_hdr);
- reslen = WI_PRISM2_RES_SIZE;
- }
- if (len < off + reslen * n)
- n = (len - off) / reslen;
- len = off + reslen * n;
- if (off != 0) {
- struct wi_scan_p2_hdr *p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- /*
- * Prepend Prism-specific header.
- */
- if (len < sizeof(struct wi_scan_p2_hdr)) {
- error = ENOSPC;
- break;
- }
- p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- p2->wi_rsvd = 0;
- p2->wi_reason = n; /* XXX */
- }
- for (i = 0; i < n; i++, off += reslen) {
- const struct wi_apinfo *ap = &sc->sc_aps[i];
-
- res = (struct wi_scan_res *)((char *)wreq.wi_val + off);
- res->wi_chan = ap->channel;
- res->wi_noise = ap->noise;
- res->wi_signal = ap->signal;
- IEEE80211_ADDR_COPY(res->wi_bssid, ap->bssid);
- res->wi_interval = ap->interval;
- res->wi_capinfo = ap->capinfo;
- res->wi_ssid_len = ap->namelen;
- memcpy(res->wi_ssid, ap->name,
- IEEE80211_NWID_LEN);
- if (sc->sc_firmware_type != WI_LUCENT) {
- /* XXX not saved from Prism cards */
- memset(res->wi_srates, 0,
- sizeof(res->wi_srates));
- res->wi_rate = ap->rate;
- res->wi_rsvd = 0;
- }
- }
- break;
-
- default:
- if (sc->sc_enabled) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- switch (wreq.wi_type) {
- case WI_RID_MAX_DATALEN:
- wreq.wi_val[0] = htole16(sc->sc_max_datalen);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_RTS_THRESH:
- wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_CNFAUTHMODE:
- wreq.wi_val[0] = htole16(sc->sc_cnfauthmode);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_NODENAME:
- if (len < sc->sc_nodelen + sizeof(u_int16_t)) {
- error = ENOSPC;
- break;
- }
- len = sc->sc_nodelen + sizeof(u_int16_t);
- wreq.wi_val[0] = htole16((sc->sc_nodelen + 1) / 2);
- memcpy(&wreq.wi_val[1], sc->sc_nodename,
- sc->sc_nodelen);
- break;
- default:
- return ieee80211_ioctl(ic, cmd, data);
- }
- break;
- }
- if (error)
- return error;
- wreq.wi_len = (len + 1) / 2 + 1;
- return copyout(&wreq, ifr->ifr_data, (wreq.wi_len + 1) * 2);
-}
-
-static int
-wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
- struct mbuf *m;
- int i, len, error, mif, val;
- struct ieee80211_rateset *rs;
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
- switch (wreq.wi_type) {
- case WI_RID_DBM_ADJUST:
- return ENODEV;
-
- case WI_RID_NODENAME:
- if (le16toh(wreq.wi_val[0]) * 2 > len ||
- le16toh(wreq.wi_val[0]) > sizeof(sc->sc_nodename)) {
- error = ENOSPC;
- break;
- }
- WI_LOCK(sc);
- if (sc->sc_enabled)
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- len);
- if (error == 0) {
- sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
- memcpy(sc->sc_nodename, &wreq.wi_val[1],
- sc->sc_nodelen);
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MICROWAVE_OVEN:
- case WI_RID_ROAMING_MODE:
- case WI_RID_SYSTEM_SCALE:
- case WI_RID_FRAG_THRESH:
- /* XXX unlocked reads */
- if (wreq.wi_type == WI_RID_MICROWAVE_OVEN &&
- (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0)
- break;
- if (wreq.wi_type == WI_RID_ROAMING_MODE &&
- (sc->sc_flags & WI_FLAGS_HAS_ROAMING) == 0)
- break;
- if (wreq.wi_type == WI_RID_SYSTEM_SCALE &&
- (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) == 0)
- break;
- if (wreq.wi_type == WI_RID_FRAG_THRESH &&
- (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) == 0)
- break;
- /* FALLTHROUGH */
- case WI_RID_RTS_THRESH:
- case WI_RID_CNFAUTHMODE:
- case WI_RID_MAX_DATALEN:
- WI_LOCK(sc);
- if (sc->sc_enabled) {
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- sizeof(u_int16_t));
- if (error != 0) {
- WI_UNLOCK(sc);
- break;
- }
- }
- switch (wreq.wi_type) {
- case WI_RID_FRAG_THRESH:
- ic->ic_fragthreshold = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_RTS_THRESH:
- ic->ic_rtsthreshold = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_MICROWAVE_OVEN:
- sc->sc_microwave_oven = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_ROAMING_MODE:
- sc->sc_roaming_mode = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_SYSTEM_SCALE:
- sc->sc_system_scale = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_CNFAUTHMODE:
- sc->sc_cnfauthmode = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_MAX_DATALEN:
- sc->sc_max_datalen = le16toh(wreq.wi_val[0]);
- break;
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_TX_RATE:
- WI_LOCK(sc);
- switch (le16toh(wreq.wi_val[0])) {
- case 3:
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- break;
- default:
- rs = &ic->ic_sup_rates[IEEE80211_MODE_11B];
- for (i = 0; i < rs->rs_nrates; i++) {
- if ((rs->rs_rates[i] & IEEE80211_RATE_VAL)
- / 2 == le16toh(wreq.wi_val[0]))
- break;
- }
- if (i == rs->rs_nrates) {
- WI_UNLOCK(sc);
- return EINVAL;
- }
- ic->ic_fixed_rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
- }
- if (sc->sc_enabled)
- error = wi_write_txrate(sc);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_SCAN_APS:
- WI_LOCK(sc);
- if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
- error = wi_scan_ap(sc, 0x3fff, 0x000f);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_SCAN_REQ: /* compatibility interface */
- WI_LOCK(sc);
- if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
- error = wi_scan_ap(sc, wreq.wi_val[0], wreq.wi_val[1]);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MGMT_XMIT:
- WI_LOCK(sc);
- if (!sc->sc_enabled)
- error = ENETDOWN;
- else if (ic->ic_mgtq.ifq_len > 5)
- error = EAGAIN;
- else {
- /* NB: m_devget uses M_DONTWAIT so can hold the lock */
- /* XXX wi_len looks in u_int8_t, not in u_int16_t */
- m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0,
- ifp, NULL);
- if (m != NULL)
- IF_ENQUEUE(&ic->ic_mgtq, m);
- else
- error = ENOMEM;
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MIF:
- mif = wreq.wi_val[0];
- val = wreq.wi_val[1];
- WI_LOCK(sc);
- error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_PROCFRAME: /* ignore for compatibility */
- break;
-
- case WI_RID_OWN_SSID:
- if (le16toh(wreq.wi_val[0]) * 2 > len ||
- le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
- error = ENOSPC;
- break;
- }
- WI_LOCK(sc);
- memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_ssid[0].len = le16toh(wreq.wi_val[0]) * 2;
- memcpy(ic->ic_des_ssid[0].ssid, &wreq.wi_val[1],
- ic->ic_des_ssid[0].len);
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- WI_UNLOCK(sc);
- break;
-
- default:
- WI_LOCK(sc);
- if (sc->sc_enabled)
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- len);
- if (error == 0) {
- /* XXX ieee80211_ioctl does a copyin */
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc);
- error = 0;
- }
- }
- WI_UNLOCK(sc);
- break;
- }
- return error;
-}
-#endif
-
static int
-wi_write_txrate(struct wi_softc *sc)
+wi_write_txrate(struct wi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- int i;
- u_int16_t rate;
-
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- rate = 0; /* auto */
- else
- rate = ic->ic_fixed_rate / 2;
-
- /* rate: 0, 1, 2, 5, 11 */
-
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- switch (rate) {
- case 0: /* auto == 11mbps auto */
- rate = 3;
- break;
- /* case 1, 2 map to 1, 2*/
- case 5: /* 5.5Mbps -> 4 */
- rate = 4;
- break;
- case 11: /* 11mbps -> 5 */
- rate = 5;
- break;
- default:
- break;
- }
- break;
- default:
- /* Choose a bit according to this table.
- *
- * bit | data rate
- * ----+-------------------
- * 0 | 1Mbps
- * 1 | 2Mbps
- * 2 | 5.5Mbps
- * 3 | 11Mbps
- */
- for (i = 8; i > 0; i >>= 1) {
- if (rate >= i)
- break;
- }
- if (i == 0)
- rate = 0xf; /* auto */
- else
- rate = i;
- break;
- }
- return wi_write_val(sc, WI_RID_TX_RATE, rate);
-}
-
-static int
-wi_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
- ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
-{
- struct wi_softc *sc = ic->ic_ifp->if_softc;
-
- /*
- * When doing host encryption of outbound frames fail requests
- * for keys that are not marked w/ the SWCRYPT flag so the
- * net80211 layer falls back to s/w crypto. Note that we also
- * fixup existing keys below to handle mode changes.
- */
- if ((sc->sc_encryption & HOST_ENCRYPT) &&
- (k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0)
- return 0;
- return sc->sc_key_alloc(ic, k, keyix, rxkeyix);
+ static const uint16_t lucent_rates[12] = {
+ [ 0] = 3, /* auto */
+ [ 1] = 1, /* 1Mb/s */
+ [ 2] = 2, /* 2Mb/s */
+ [ 5] = 4, /* 5.5Mb/s */
+ [11] = 5 /* 11Mb/s */
+ };
+ static const uint16_t intersil_rates[12] = {
+ [ 0] = 0xf, /* auto */
+ [ 1] = 0, /* 1Mb/s */
+ [ 2] = 1, /* 2Mb/s */
+ [ 5] = 2, /* 5.5Mb/s */
+ [11] = 3, /* 11Mb/s */
+ };
+ const uint16_t *rates = sc->sc_firmware_type == WI_LUCENT ?
+ lucent_rates : intersil_rates;
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ return wi_write_val(sc, WI_RID_TX_RATE,
+ (tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
+ rates[0] : rates[tp->ucastrate / 2]));
}
static int
-wi_write_wep(struct wi_softc *sc)
+wi_write_wep(struct wi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
int error = 0;
int i, keylen;
u_int16_t val;
@@ -2566,20 +1749,20 @@ wi_write_wep(struct wi_softc *sc)
switch (sc->sc_firmware_type) {
case WI_LUCENT:
- val = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
+ val = (vap->iv_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
if (error)
break;
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
break;
- error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_def_txkey);
+ error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, vap->iv_def_txkey);
if (error)
break;
memset(wkey, 0, sizeof(wkey));
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- keylen = ic->ic_nw_keys[i].wk_keylen;
+ keylen = vap->iv_nw_keys[i].wk_keylen;
wkey[i].wi_keylen = htole16(keylen);
- memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key,
+ memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key,
keylen);
}
error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS,
@@ -2588,8 +1771,8 @@ wi_write_wep(struct wi_softc *sc)
break;
case WI_INTERSIL:
- case WI_SYMBOL:
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ val = HOST_ENCRYPT | HOST_DECRYPT;
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
/*
* ONLY HWB3163 EVAL-CARD Firmware version
* less than 0.8 variant2
@@ -2599,25 +1782,15 @@ wi_write_wep(struct wi_softc *sc)
* It is under investigation for details.
* (ichiro@netbsd.org)
*/
- if (sc->sc_firmware_type == WI_INTERSIL &&
- sc->sc_sta_firmware_ver < 802 ) {
+ if (sc->sc_sta_firmware_ver < 802 ) {
/* firm ver < 0.8 variant 2 */
wi_write_val(sc, WI_RID_PROMISC, 1);
}
wi_write_val(sc, WI_RID_CNFAUTHMODE,
- sc->sc_cnfauthmode);
- /* XXX should honor IEEE80211_F_DROPUNENC */
- val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED;
- /*
- * Encryption firmware has a bug for HostAP mode.
- */
- if (sc->sc_firmware_type == WI_INTERSIL &&
- ic->ic_opmode == IEEE80211_M_HOSTAP)
- val |= HOST_ENCRYPT;
+ vap->iv_bss->ni_authmode);
+ val |= PRIVACY_INVOKED;
} else {
- wi_write_val(sc, WI_RID_CNFAUTHMODE,
- IEEE80211_AUTH_OPEN);
- val = HOST_ENCRYPT | HOST_DECRYPT;
+ wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN);
}
error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
if (error)
@@ -2625,55 +1798,17 @@ wi_write_wep(struct wi_softc *sc)
sc->sc_encryption = val;
if ((val & PRIVACY_INVOKED) == 0)
break;
- error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY,
- ic->ic_def_txkey);
- if (error)
- break;
- if (val & HOST_DECRYPT)
- break;
- /*
- * It seems that the firmware accept 104bit key only if
- * all the keys have 104bit length. We get the length of
- * the transmit key and use it for all other keys.
- * Perhaps we should use software WEP for such situation.
- */
- if (ic->ic_def_txkey != IEEE80211_KEYIX_NONE)
- keylen = ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen;
- else /* XXX should not hapen */
- keylen = IEEE80211_WEP_KEYLEN;
- if (keylen > IEEE80211_WEP_KEYLEN)
- keylen = 13; /* 104bit keys */
- else
- keylen = IEEE80211_WEP_KEYLEN;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- error = wi_write_rid(sc, WI_RID_P2_CRYPT_KEY0 + i,
- ic->ic_nw_keys[i].wk_key, keylen);
- if (error)
- break;
- }
+ error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, vap->iv_def_txkey);
break;
}
- /*
- * XXX horrible hack; insure pre-existing keys are
- * setup properly to do s/w crypto.
- */
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- struct ieee80211_key *k = &ic->ic_nw_keys[i];
- if (k->wk_flags & IEEE80211_KEY_XMIT) {
- if (sc->sc_encryption & HOST_ENCRYPT)
- k->wk_flags |= IEEE80211_KEY_SWCRYPT;
- else
- k->wk_flags &= ~IEEE80211_KEY_SWCRYPT;
- }
- }
return error;
}
static int
wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
{
- int i, s = 0;
-
+ int i, s = 0;
+
if (sc->wi_gone)
return (ENODEV);
@@ -2684,7 +1819,8 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
DELAY(1*1000); /* 1ms */
}
if (i == 0) {
- device_printf(sc->sc_dev, "wi_cmd: busy bit won't clear.\n" );
+ device_printf(sc->sc_dev, "%s: busy bit won't clear, cmd 0x%x\n",
+ __func__, cmd);
sc->wi_gone = 1;
return(ETIMEDOUT);
}
@@ -2717,8 +1853,8 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
}
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev,
- "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
+ device_printf(sc->sc_dev, "%s: timeout on cmd 0x%04x; "
+ "event status 0x%04x\n", __func__, cmd, s);
if (s == 0xffff)
sc->wi_gone = 1;
return(ETIMEDOUT);
@@ -2739,8 +1875,8 @@ wi_seek_bap(struct wi_softc *sc, int id, int off)
if ((status & WI_OFF_BUSY) == 0)
break;
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev, "timeout in wi_seek to %x/%x\n",
- id, off);
+ device_printf(sc->sc_dev, "%s: timeout, id %x off %x\n",
+ __func__, id, off);
sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
if (status == 0xffff)
sc->wi_gone = 1;
@@ -2749,7 +1885,8 @@ wi_seek_bap(struct wi_softc *sc, int id, int off)
DELAY(1);
}
if (status & WI_OFF_ERR) {
- device_printf(sc->sc_dev, "failed in wi_seek to %x/%x\n", id, off);
+ device_printf(sc->sc_dev, "%s: error, id %x off %x\n",
+ __func__, id, off);
sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
return EIO;
}
@@ -2787,9 +1924,6 @@ wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
if (buflen == 0)
return 0;
-#ifdef WI_HERMES_AUTOINC_WAR
- again:
-#endif
if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
if ((error = wi_seek_bap(sc, id, off)) != 0)
return error;
@@ -2800,33 +1934,6 @@ wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
sc->sc_bap_off += cnt * 2;
-#ifdef WI_HERMES_AUTOINC_WAR
- /*
- * According to the comments in the HCF Light code, there is a bug
- * in the Hermes (or possibly in certain Hermes firmware revisions)
- * where the chip's internal autoincrement counter gets thrown off
- * during data writes: the autoincrement is missed, causing one
- * data word to be overwritten and subsequent words to be written to
- * the wrong memory locations. The end result is that we could end
- * up transmitting bogus frames without realizing it. The workaround
- * for this is to write a couple of extra guard words after the end
- * of the transfer, then attempt to read then back. If we fail to
- * locate the guard words where we expect them, we preform the
- * transfer over again.
- */
- if ((sc->sc_flags & WI_FLAGS_BUG_AUTOINC) && (id & 0xf000) == 0) {
- CSR_WRITE_2(sc, WI_DATA0, 0x1234);
- CSR_WRITE_2(sc, WI_DATA0, 0x5678);
- wi_seek_bap(sc, id, sc->sc_bap_off);
- sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
- if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
- CSR_READ_2(sc, WI_DATA0) != 0x5678) {
- device_printf(sc->sc_dev,
- "detect auto increment bug, try again\n");
- goto again;
- }
- }
-#endif
return 0;
}
@@ -2863,8 +1970,8 @@ wi_alloc_fid(struct wi_softc *sc, int len, int *idp)
int i;
if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
- device_printf(sc->sc_dev, "failed to allocate %d bytes on NIC\n",
- len);
+ device_printf(sc->sc_dev, "%s: failed to allocate %d bytes on NIC\n",
+ __func__, len);
return ENOMEM;
}
@@ -2874,7 +1981,7 @@ wi_alloc_fid(struct wi_softc *sc, int len, int *idp)
DELAY(1);
}
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev, "timeout in alloc\n");
+ device_printf(sc->sc_dev, "%s: timeout in alloc\n", __func__);
return ETIMEDOUT;
}
*idp = CSR_READ_2(sc, WI_ALLOC_FID);
@@ -2923,251 +2030,33 @@ wi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen)
ltbuf[1] = htole16(rid);
error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
- if (error)
+ if (error) {
+ device_printf(sc->sc_dev, "%s: bap0 write failure, rid 0x%x\n",
+ __func__, rid);
return error;
+ }
error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen);
- if (error)
+ if (error) {
+ device_printf(sc->sc_dev, "%s: bap1 write failure, rid 0x%x\n",
+ __func__, rid);
return error;
+ }
return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0);
}
static int
-wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211_node *ni;
- int buflen;
- u_int16_t val;
- struct wi_ssid ssid;
- u_int8_t old_bssid[IEEE80211_ADDR_LEN];
-
- DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]));
-
- /*
- * Internal to the driver the INIT and RUN states are used
- * so bypass the net80211 state machine for other states.
- * Beware however that this requires use to net80211 state
- * management that otherwise would be handled for us.
- */
- switch (nstate) {
- case IEEE80211_S_INIT:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- return (*sc->sc_newstate)(ic, nstate, arg);
-
- case IEEE80211_S_SCAN:
- return (*sc->sc_newstate)(ic, nstate, arg);
-
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- ic->ic_state = nstate; /* NB: skip normal ieee80211 handling */
- break;
-
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- buflen = IEEE80211_ADDR_LEN;
- IEEE80211_ADDR_COPY(old_bssid, ni->ni_bssid);
- wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen);
- IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
- buflen = sizeof(val);
- wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen);
- ni->ni_chan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(val, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (ni->ni_chan == NULL)
- ni->ni_chan = &ic->ic_channels[0];
- ic->ic_curchan = ic->ic_bsschan = ni->ni_chan;
-#if NBPFILTER > 0
- sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
- htole16(ni->ni_chan->ic_freq);
- sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
- htole16(ni->ni_chan->ic_flags);
-#endif
- /*
- * XXX hack; unceremoniously clear
- * IEEE80211_F_DROPUNENC when operating with
- * wep enabled so we don't drop unencoded frames
- * at the 802.11 layer. This is necessary because
- * we must strip the WEP bit from the 802.11 header
- * before passing frames to ieee80211_input because
- * the card has already stripped the WEP crypto
- * header from the packet.
- */
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- /* XXX check return value */
- buflen = sizeof(ssid);
- wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen);
- ni->ni_esslen = le16toh(ssid.wi_len);
- if (ni->ni_esslen > IEEE80211_NWID_LEN)
- ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/
- memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
- }
- return (*sc->sc_newstate)(ic, nstate, arg);
- default:
- break;
- }
- return 0;
-}
-
-static int
-wi_scan_ap(struct wi_softc *sc, u_int16_t chanmask, u_int16_t txrate)
-{
- int error = 0;
- u_int16_t val[2];
-
- if (!sc->sc_enabled)
- return ENXIO;
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- (void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
- break;
- case WI_INTERSIL:
- val[0] = htole16(chanmask); /* channel */
- val[1] = htole16(txrate); /* tx rate */
- error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val));
- break;
- case WI_SYMBOL:
- /*
- * XXX only supported on 3.x ?
- */
- val[0] = BSCAN_BCAST | BSCAN_ONETIME;
- error = wi_write_rid(sc, WI_RID_BCAST_SCAN_REQ,
- val, sizeof(val[0]));
- break;
- }
- if (error == 0) {
- sc->sc_scan_timer = WI_SCAN_WAIT;
- DPRINTF(("wi_scan_ap: start scanning, "
- "chamask 0x%x txrate 0x%x\n", chanmask, txrate));
- }
- return error;
-}
-
-static void
-wi_scan_result(struct wi_softc *sc, int fid, int cnt)
-{
-#define N(a) (sizeof (a) / sizeof (a[0]))
- int i, naps, off, szbuf;
- struct wi_scan_header ws_hdr; /* Prism2 header */
- struct wi_scan_data_p2 ws_dat; /* Prism2 scantable*/
- struct wi_apinfo *ap;
- struct ieee80211_scanparams sp;
- struct ieee80211_frame wh;
- static long rstamp;
- struct ieee80211com *ic;
- uint8_t ssid[2+IEEE80211_NWID_LEN];
-
- ic = &sc->sc_ic;
- rstamp++;
- memset(&sp, 0, sizeof(sp));
-
- off = sizeof(u_int16_t) * 2;
- memset(&ws_hdr, 0, sizeof(ws_hdr));
- switch (sc->sc_firmware_type) {
- case WI_INTERSIL:
- wi_read_bap(sc, fid, off, &ws_hdr, sizeof(ws_hdr));
- off += sizeof(ws_hdr);
- szbuf = sizeof(struct wi_scan_data_p2);
- break;
- case WI_SYMBOL:
- szbuf = sizeof(struct wi_scan_data_p2) + 6;
- break;
- case WI_LUCENT:
- szbuf = sizeof(struct wi_scan_data);
- break;
- default:
- device_printf(sc->sc_dev,
- "wi_scan_result: unknown firmware type %u\n",
- sc->sc_firmware_type);
- naps = 0;
- goto done;
- }
- naps = (cnt * 2 + 2 - off) / szbuf;
- if (naps > N(sc->sc_aps))
- naps = N(sc->sc_aps);
- sc->sc_naps = naps;
- /* Read Data */
- ap = sc->sc_aps;
- memset(&ws_dat, 0, sizeof(ws_dat));
-
- for (i = 0; i < naps; i++, ap++) {
- uint8_t rates[2 + IEEE80211_RATE_MAXSIZE];
- uint16_t *bssid;
- wi_read_bap(sc, fid, off, &ws_dat,
- (sizeof(ws_dat) < szbuf ? sizeof(ws_dat) : szbuf));
- DPRINTF2(("wi_scan_result: #%d: off %d bssid %s\n", i, off,
- ether_sprintf(ws_dat.wi_bssid)));
-
- off += szbuf;
- ap->scanreason = le16toh(ws_hdr.wi_reason);
- memcpy(ap->bssid, ws_dat.wi_bssid, sizeof(ap->bssid));
-
- bssid = (uint16_t *)&ap->bssid;
- if (bssid[0] == 0 && bssid[1] == 0 && bssid[2] == 0)
- break;
-
- memcpy(wh.i_addr2, ws_dat.wi_bssid, sizeof(ap->bssid));
- memcpy(wh.i_addr3, ws_dat.wi_bssid, sizeof(ap->bssid));
- ap->channel = le16toh(ws_dat.wi_chid);
- ap->signal = le16toh(ws_dat.wi_signal);
- ap->noise = le16toh(ws_dat.wi_noise);
- ap->quality = ap->signal - ap->noise;
- sp.capinfo = ap->capinfo = le16toh(ws_dat.wi_capinfo);
- sp.bintval = ap->interval = le16toh(ws_dat.wi_interval);
- ap->rate = le16toh(ws_dat.wi_rate);
- rates[1] = 1;
- rates[2] = (uint8_t)ap->rate / 5;
- ap->namelen = le16toh(ws_dat.wi_namelen);
- if (ap->namelen > sizeof(ap->name))
- ap->namelen = sizeof(ap->name);
- memcpy(ap->name, ws_dat.wi_name, ap->namelen);
- sp.ssid = (uint8_t *)&ssid[0];
- memcpy(sp.ssid + 2, ap->name, ap->namelen);
- sp.ssid[1] = ap->namelen;
- sp.bchan = ap->channel;
- sp.curchan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(ap->channel, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (sp.curchan == NULL)
- sp.curchan = &ic->ic_channels[0];
- sp.rates = &rates[0];
- sp.tstamp = (uint8_t *)&rstamp;
- DPRINTF(("calling add_scan, bssid %s chan %d signal %d\n",
- ether_sprintf(ws_dat.wi_bssid), ap->channel, ap->signal));
- ieee80211_add_scan(ic, &sp, &wh, 0, ap->signal, ap->noise, rstamp);
- }
-done:
- /* Done scanning */
- sc->sc_scan_timer = 0;
- DPRINTF(("wi_scan_result: scan complete: ap %d\n", naps));
-#undef N
-}
-
-static void
-wi_dump_pkt(struct wi_frame *wh, struct ieee80211_node *ni, int rssi)
+wi_write_appie(struct wi_softc *sc, int rid, const struct ieee80211_appie *ie)
{
- if (ni != NULL)
- ieee80211_dump_pkt(ni->ni_ic,
- (u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr),
- ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL,
- rssi);
- printf(" status 0x%x rx_tstamp1 %u rx_tstamp0 0x%u rx_silence %u\n",
- le16toh(wh->wi_status), le16toh(wh->wi_rx_tstamp1),
- le16toh(wh->wi_rx_tstamp0), wh->wi_rx_silence);
- printf(" rx_signal %u rx_rate %u rx_flow %u\n",
- wh->wi_rx_signal, wh->wi_rx_rate, wh->wi_rx_flow);
- printf(" tx_rtry %u tx_rate %u tx_ctl 0x%x dat_len %u\n",
- wh->wi_tx_rtry, wh->wi_tx_rate,
- le16toh(wh->wi_tx_ctl), le16toh(wh->wi_dat_len));
- printf(" ehdr dst %6D src %6D type 0x%x\n",
- wh->wi_ehdr.ether_dhost, ":", wh->wi_ehdr.ether_shost, ":",
- wh->wi_ehdr.ether_type);
+ /* NB: 42 bytes is probably ok to have on the stack */
+ char buf[sizeof(uint16_t) + 40];
+
+ if (ie->ie_len > 40)
+ return EINVAL;
+ /* NB: firmware requires 16-bit ie length before ie data */
+ *(uint16_t *) buf = htole16(ie->ie_len);
+ memcpy(buf + sizeof(uint16_t), ie->ie_data, ie->ie_len);
+ return wi_write_rid(sc, rid, buf, ie->ie_len + sizeof(uint16_t));
}
int
@@ -3180,9 +2069,9 @@ wi_alloc(device_t dev, int rid)
sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sc->iobase_rid, 0, ~0, (1 << 6),
rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
- if (!sc->iobase) {
+ if (sc->iobase == NULL) {
device_printf(dev, "No I/O space?!\n");
- return (ENXIO);
+ return ENXIO;
}
sc->wi_io_addr = rman_get_start(sc->iobase);
@@ -3192,32 +2081,28 @@ wi_alloc(device_t dev, int rid)
sc->mem_rid = rid;
sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->mem_rid, RF_ACTIVE);
-
- if (!sc->mem) {
+ if (sc->mem == NULL) {
device_printf(dev, "No Mem space on prism2.5?\n");
- return (ENXIO);
+ return ENXIO;
}
sc->wi_btag = rman_get_bustag(sc->mem);
sc->wi_bhandle = rman_get_bushandle(sc->mem);
}
-
sc->irq_rid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
RF_ACTIVE |
((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
-
- if (!sc->irq) {
+ if (sc->irq == NULL) {
wi_free(dev);
device_printf(dev, "No irq?!\n");
- return (ENXIO);
+ return ENXIO;
}
sc->sc_dev = dev;
sc->sc_unit = device_get_unit(dev);
-
- return (0);
+ return 0;
}
void
@@ -3237,356 +2122,4 @@ wi_free(device_t dev)
bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
sc->mem = NULL;
}
-
- return;
-}
-
-#if 0
-static int
-wi_get_debug(struct wi_softc *sc, struct wi_req *wreq)
-{
- int error = 0;
-
- wreq->wi_len = 1;
-
- switch (wreq->wi_type) {
- case WI_DEBUG_SLEEP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_sleep;
- break;
- case WI_DEBUG_DELAYSUPP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
- break;
- case WI_DEBUG_TXSUPP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
- break;
- case WI_DEBUG_MONITOR:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_monitor;
- break;
- case WI_DEBUG_LEDTEST:
- wreq->wi_len += 3;
- wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
- wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
- wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
- break;
- case WI_DEBUG_CONTTX:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_conttx;
- wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
- break;
- case WI_DEBUG_CONTRX:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_contrx;
- break;
- case WI_DEBUG_SIGSTATE:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
- wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
- break;
- case WI_DEBUG_CONFBITS:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_confbits;
- wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
- break;
- default:
- error = EIO;
- break;
- }
-
- return (error);
-}
-
-static int
-wi_set_debug(struct wi_softc *sc, struct wi_req *wreq)
-{
- int error = 0;
- u_int16_t cmd, param0 = 0, param1 = 0;
-
- switch (wreq->wi_type) {
- case WI_DEBUG_RESET:
- case WI_DEBUG_INIT:
- case WI_DEBUG_CALENABLE:
- break;
- case WI_DEBUG_SLEEP:
- sc->wi_debug.wi_sleep = 1;
- break;
- case WI_DEBUG_WAKE:
- sc->wi_debug.wi_sleep = 0;
- break;
- case WI_DEBUG_CHAN:
- param0 = wreq->wi_val[0];
- break;
- case WI_DEBUG_DELAYSUPP:
- sc->wi_debug.wi_delaysupp = 1;
- break;
- case WI_DEBUG_TXSUPP:
- sc->wi_debug.wi_txsupp = 1;
- break;
- case WI_DEBUG_MONITOR:
- sc->wi_debug.wi_monitor = 1;
- break;
- case WI_DEBUG_LEDTEST:
- param0 = wreq->wi_val[0];
- param1 = wreq->wi_val[1];
- sc->wi_debug.wi_ledtest = 1;
- sc->wi_debug.wi_ledtest_param0 = param0;
- sc->wi_debug.wi_ledtest_param1 = param1;
- break;
- case WI_DEBUG_CONTTX:
- param0 = wreq->wi_val[0];
- sc->wi_debug.wi_conttx = 1;
- sc->wi_debug.wi_conttx_param0 = param0;
- break;
- case WI_DEBUG_STOPTEST:
- sc->wi_debug.wi_delaysupp = 0;
- sc->wi_debug.wi_txsupp = 0;
- sc->wi_debug.wi_monitor = 0;
- sc->wi_debug.wi_ledtest = 0;
- sc->wi_debug.wi_ledtest_param0 = 0;
- sc->wi_debug.wi_ledtest_param1 = 0;
- sc->wi_debug.wi_conttx = 0;
- sc->wi_debug.wi_conttx_param0 = 0;
- sc->wi_debug.wi_contrx = 0;
- sc->wi_debug.wi_sigstate = 0;
- sc->wi_debug.wi_sigstate_param0 = 0;
- break;
- case WI_DEBUG_CONTRX:
- sc->wi_debug.wi_contrx = 1;
- break;
- case WI_DEBUG_SIGSTATE:
- param0 = wreq->wi_val[0];
- sc->wi_debug.wi_sigstate = 1;
- sc->wi_debug.wi_sigstate_param0 = param0;
- break;
- case WI_DEBUG_CONFBITS:
- param0 = wreq->wi_val[0];
- param1 = wreq->wi_val[1];
- sc->wi_debug.wi_confbits = param0;
- sc->wi_debug.wi_confbits_param0 = param1;
- break;
- default:
- error = EIO;
- break;
- }
-
- if (error)
- return (error);
-
- cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
- error = wi_cmd(sc, cmd, param0, param1, 0);
-
- return (error);
-}
-#endif
-
-/*
- * Special routines to download firmware for Symbol CF card.
- * XXX: This should be modified generic into any PRISM-2 based card.
- */
-
-#define WI_SBCF_PDIADDR 0x3100
-
-/* unaligned load little endian */
-#define GETLE32(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
-#define GETLE16(p) ((p)[0] | ((p)[1]<<8))
-
-int
-wi_symbol_load_firm(struct wi_softc *sc, const void *primsym, int primlen,
- const void *secsym, int seclen)
-{
- uint8_t ebuf[256];
- int i;
-
- /* load primary code and run it */
- wi_symbol_set_hcr(sc, WI_HCR_EEHOLD);
- if (wi_symbol_write_firm(sc, primsym, primlen, NULL, 0))
- return EIO;
- wi_symbol_set_hcr(sc, WI_HCR_RUN);
- for (i = 0; ; i++) {
- if (i == 10)
- return ETIMEDOUT;
- tsleep(sc, PWAIT, "wiinit", 1);
- if (CSR_READ_2(sc, WI_CNTL) == WI_CNTL_AUX_ENA_STAT)
- break;
- /* write the magic key value to unlock aux port */
- CSR_WRITE_2(sc, WI_PARAM0, WI_AUX_KEY0);
- CSR_WRITE_2(sc, WI_PARAM1, WI_AUX_KEY1);
- CSR_WRITE_2(sc, WI_PARAM2, WI_AUX_KEY2);
- CSR_WRITE_2(sc, WI_CNTL, WI_CNTL_AUX_ENA_CNTL);
- }
-
- /* issue read EEPROM command: XXX copied from wi_cmd() */
- CSR_WRITE_2(sc, WI_PARAM0, 0);
- CSR_WRITE_2(sc, WI_PARAM1, 0);
- CSR_WRITE_2(sc, WI_PARAM2, 0);
- CSR_WRITE_2(sc, WI_COMMAND, WI_CMD_READEE);
- for (i = 0; i < WI_TIMEOUT; i++) {
- if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD)
- break;
- DELAY(1);
- }
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
-
- CSR_WRITE_2(sc, WI_AUX_PAGE, WI_SBCF_PDIADDR / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, WI_SBCF_PDIADDR % WI_AUX_PGSZ);
- CSR_READ_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (uint16_t *)ebuf, sizeof(ebuf) / 2);
- if (GETLE16(ebuf) > sizeof(ebuf))
- return EIO;
- if (wi_symbol_write_firm(sc, secsym, seclen, ebuf + 4, GETLE16(ebuf)))
- return EIO;
- return 0;
-}
-
-static int
-wi_symbol_write_firm(struct wi_softc *sc, const void *buf, int buflen,
- const void *ebuf, int ebuflen)
-{
- const uint8_t *p, *ep, *q, *eq;
- char *tp;
- uint32_t addr, id, eid;
- int i, len, elen, nblk, pdrlen;
-
- /*
- * Parse the header of the firmware image.
- */
- p = buf;
- ep = p + buflen;
- while (p < ep && *p++ != ' '); /* FILE: */
- while (p < ep && *p++ != ' '); /* filename */
- while (p < ep && *p++ != ' '); /* type of the firmware */
- nblk = strtoul(p, &tp, 10);
- p = tp;
- pdrlen = strtoul(p + 1, &tp, 10);
- p = tp;
- while (p < ep && *p++ != 0x1a); /* skip rest of header */
-
- /*
- * Block records: address[4], length[2], data[length];
- */
- for (i = 0; i < nblk; i++) {
- addr = GETLE32(p); p += 4;
- len = GETLE16(p); p += 2;
- CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
- CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (const uint16_t *)p, len / 2);
- p += len;
- }
-
- /*
- * PDR: id[4], address[4], length[4];
- */
- for (i = 0; i < pdrlen; ) {
- id = GETLE32(p); p += 4; i += 4;
- addr = GETLE32(p); p += 4; i += 4;
- len = GETLE32(p); p += 4; i += 4;
- /* replace PDR entry with the values from EEPROM, if any */
- for (q = ebuf, eq = q + ebuflen; q < eq; q += elen * 2) {
- elen = GETLE16(q); q += 2;
- eid = GETLE16(q); q += 2;
- elen--; /* elen includes eid */
- if (eid == 0)
- break;
- if (eid != id)
- continue;
- CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
- CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (const uint16_t *)q, len / 2);
- break;
- }
- }
- return 0;
-}
-
-static int
-wi_symbol_set_hcr(struct wi_softc *sc, int mode)
-{
- uint16_t hcr;
-
- CSR_WRITE_2(sc, WI_COR, WI_COR_RESET);
- tsleep(sc, PWAIT, "wiinit", 1);
- hcr = CSR_READ_2(sc, WI_HCR);
- hcr = (hcr & WI_HCR_4WIRE) | (mode & ~WI_HCR_4WIRE);
- CSR_WRITE_2(sc, WI_HCR, hcr);
- tsleep(sc, PWAIT, "wiinit", 1);
- CSR_WRITE_2(sc, WI_COR, WI_COR_IOMODE);
- tsleep(sc, PWAIT, "wiinit", 1);
- return 0;
-}
-
-/*
- * This function can be called by ieee80211_set_shortslottime(). Refer to
- * IEEE Std 802.11-1999 pp. 85 to know how these values are computed.
- */
-static void
-wi_update_slot(struct ifnet *ifp)
-{
- DPRINTF(("wi update slot unimplemented\n"));
-}
-
-static void
-wi_set_channel(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- if (sc->sc_enabled && !(sc->sc_flags & WI_FLAGS_SCANNING)) {
- DPRINTF(("wi_set_channel: %d (%dMHz)\n",
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- ic->ic_curchan->ic_freq));
-
- sc->wi_channel = ic->ic_curchan;
- wi_write_val(sc, WI_RID_OWN_CHNL,
- ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- /* XXX: some cards need to be re-enabled */
- wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
- wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
- }
- }
- WI_UNLOCK(sc);
-}
-
-static void
-wi_scan_start(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- sc->sc_flags |= WI_FLAGS_SCANNING;
- wi_scan_ap(sc, 0x3fff, 0x000f);
- WI_UNLOCK(sc);
-
-}
-
-static void
-wi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
-{
- /* The firmware is not capable of scanning a single channel */
-}
-
-static void
-wi_scan_mindwell(struct ieee80211com *ic)
-{
- /* NB: don't try to abort scan; wait for firmware to finish */
-}
-
-static void
-wi_scan_end(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- sc->sc_flags &= ~WI_FLAGS_SCANNING;
- WI_UNLOCK(sc);
}
diff --git a/sys/dev/wi/if_wi_pccard.c b/sys/dev/wi/if_wi_pccard.c
index 53f067b..ad1b508 100644
--- a/sys/dev/wi/if_wi_pccard.c
+++ b/sys/dev/wi/if_wi_pccard.c
@@ -71,9 +71,6 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wavelan_ieee.h>
#include <dev/wi/if_wireg.h>
#include <dev/wi/if_wivar.h>
-#ifdef WI_SYMBOL_FIRMWARE
-#include <dev/wi/spectrum24t_cf.h>
-#endif
#include "card_if.h"
#include "pccarddevs.h"
@@ -152,7 +149,6 @@ static const struct pccard_product wi_pccard_products[] = {
PCMCIA_CARD(SIEMENS, SS1021),
PCMCIA_CARD(SIMPLETECH, SPECTRUM24_ALT),
PCMCIA_CARD(SOCKET, LP_WLAN_CF),
- PCMCIA_CARD(SYMBOL, LA4100),
PCMCIA_CARD(TDK, LAK_CD011WL),
{ NULL }
};
@@ -167,64 +163,39 @@ wi_pccard_probe(device_t dev)
/* Make sure we're a network driver */
error = pccard_get_function(dev, &fcn);
if (error != 0)
- return (error);
+ return error;
if (fcn != PCCARD_FUNCTION_NETWORK)
- return (ENXIO);
+ return ENXIO;
- if ((pp = pccard_product_lookup(dev, wi_pccard_products,
- sizeof(wi_pccard_products[0]), NULL)) != NULL) {
+ pp = pccard_product_lookup(dev, wi_pccard_products,
+ sizeof(wi_pccard_products[0]), NULL);
+ if (pp != NULL) {
if (pp->pp_name != NULL)
device_set_desc(dev, pp->pp_name);
- return (0);
+ return 0;
}
- return (ENXIO);
+ return ENXIO;
}
-
static int
wi_pccard_attach(device_t dev)
{
struct wi_softc *sc;
- int error;
- uint32_t vendor, product;
+ int error;
sc = device_get_softc(dev);
sc->wi_gone = 0;
sc->wi_bus_type = WI_BUS_PCCARD;
error = wi_alloc(dev, 0);
- if (error)
- return (error);
-
- /* Make sure interrupts are disabled. */
- CSR_WRITE_2(sc, WI_INT_EN, 0);
- CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
-
- /*
- * The cute little Symbol LA4100-series CF cards need to have
- * code downloaded to them.
- */
- pccard_get_vendor(dev, &vendor);
- pccard_get_product(dev, &product);
- if (vendor == PCMCIA_VENDOR_SYMBOL &&
- product == PCMCIA_PRODUCT_SYMBOL_LA4100) {
-#ifdef WI_SYMBOL_FIRMWARE
- if (wi_symbol_load_firm(device_get_softc(dev),
- spectrum24t_primsym, sizeof(spectrum24t_primsym),
- spectrum24t_secsym, sizeof(spectrum24t_secsym))) {
- device_printf(dev, "couldn't load firmware\n");
- return (ENXIO);
- }
-#else
- device_printf(dev,
- "Symbol LA4100 needs 'option WI_SYMBOL_FIRMWARE'\n");
- wi_free(dev);
- return (ENXIO);
-#endif
+ if (error == 0) {
+ /* Make sure interrupts are disabled. */
+ CSR_WRITE_2(sc, WI_INT_EN, 0);
+ CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
+
+ error = wi_attach(dev);
+ if (error != 0)
+ wi_free(dev);
}
- pccard_get_ether(dev, sc->sc_hintmacaddr);
- error = wi_attach(dev);
- if (error != 0)
- wi_free(dev);
- return (error);
+ return error;
}
diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c
index fa67a6b..cfbe4dc 100644
--- a/sys/dev/wi/if_wi_pci.c
+++ b/sys/dev/wi/if_wi_pci.c
@@ -246,10 +246,8 @@ static int
wi_pci_suspend(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- wi_stop(ifp, 1);
+ wi_stop(sc, 1);
return (0);
}
@@ -258,8 +256,7 @@ static int
wi_pci_resume(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (sc->wi_bus_type != WI_BUS_PCI_NATIVE)
return (0);
diff --git a/sys/dev/wi/if_wivar.h b/sys/dev/wi/if_wivar.h
index 88238a3..a3e9e66 100644
--- a/sys/dev/wi/if_wivar.h
+++ b/sys/dev/wi/if_wivar.h
@@ -34,11 +34,6 @@
* $FreeBSD$
*/
-#if 0
-#define WICACHE /* turn on signal strength cache code */
-#define MAXWICACHE 10
-#endif
-
/*
* Encryption controls. We can enable or disable encryption as
* well as specify up to 4 encryption keys. We can also specify
@@ -61,17 +56,26 @@
#define WI_MAX_AID 256 /* max stations for ap operation */
+struct wi_vap {
+ struct ieee80211vap wv_vap;
+ struct ieee80211_beacon_offsets wv_bo;
+ struct task wv_connected_task;
+ struct task wv_disconnected_task;
+ struct task wv_assoc_failed_task;
+
+ void (*wv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, u_int32_t);
+ int (*wv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define WI_VAP(vap) ((struct wi_vap *)(vap))
+
struct wi_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- int (*sc_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
device_t sc_dev;
struct mtx sc_mtx;
struct callout sc_watchdog;
+ struct task sc_oor_task;
int sc_unit;
int wi_gone;
int sc_enabled;
@@ -104,33 +108,21 @@ struct wi_softc {
int wi_io_addr;
int wi_cmd_count;
- struct bpf_if *sc_drvbpf;
int sc_flags;
int sc_if_flags;
int sc_bap_id;
int sc_bap_off;
-
- u_int16_t sc_procframe;
+
+ int sc_porttype;
u_int16_t sc_portnum;
+ u_int16_t sc_encryption;
+ u_int16_t sc_monitor_port;
/* RSSI interpretation */
u_int16_t sc_min_rssi; /* clamp sc_min_rssi < RSSI */
u_int16_t sc_max_rssi; /* clamp RSSI < sc_max_rssi */
u_int16_t sc_dbm_offset; /* dBm ~ RSSI - sc_dbm_offset */
- u_int16_t sc_max_datalen;
- u_int16_t sc_system_scale;
- u_int16_t sc_cnfauthmode;
- u_int16_t sc_roaming_mode;
- u_int16_t sc_microwave_oven;
- u_int16_t sc_authtype;
- u_int16_t sc_encryption;
-
- int sc_nodelen;
- char sc_nodename[IEEE80211_NWID_LEN];
- char sc_net_name[IEEE80211_NWID_LEN];
- uint8_t sc_hintmacaddr[IEEE80211_ADDR_LEN];
-
int sc_buflen; /* TX buffer size */
int sc_ntxbuf;
#define WI_NTXBUF 3
@@ -141,74 +133,29 @@ struct wi_softc {
int sc_txnext; /* index of next TX */
int sc_txcur; /* index of current TX*/
int sc_tx_timer;
- int sc_scan_timer;
struct wi_counters sc_stats;
u_int16_t sc_ibss_port;
-#define WI_MAXAPINFO 30
- struct wi_apinfo sc_aps[WI_MAXAPINFO];
- int sc_naps;
-
- struct {
- u_int16_t wi_sleep;
- u_int16_t wi_delaysupp;
- u_int16_t wi_txsupp;
- u_int16_t wi_monitor;
- u_int16_t wi_ledtest;
- u_int16_t wi_ledtest_param0;
- u_int16_t wi_ledtest_param1;
- u_int16_t wi_conttx;
- u_int16_t wi_conttx_param0;
- u_int16_t wi_contrx;
- u_int16_t wi_sigstate;
- u_int16_t wi_sigstate_param0;
- u_int16_t wi_confbits;
- u_int16_t wi_confbits_param0;
- } wi_debug;
-
struct timeval sc_last_syn;
int sc_false_syns;
u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2];
- union {
- struct wi_tx_radiotap_header th;
- u_int8_t pad[64];
- } u_tx_rt;
+ struct wi_tx_radiotap_header sc_tx_th;
int sc_tx_th_len;
- union {
- struct wi_rx_radiotap_header th;
- u_int8_t pad[64];
- } u_rx_rt;
+ struct wi_rx_radiotap_header sc_rx_th;
int sc_rx_th_len;
};
-#define sc_tx_th u_tx_rt.th
-#define sc_rx_th u_rx_rt.th
/* maximum consecutive false change-of-BSSID indications */
#define WI_MAX_FALSE_SYNS 10
-#define WI_SCAN_INQWAIT 3 /* wait sec before inquire */
-#define WI_SCAN_WAIT 5 /* maximum scan wait */
-
-#define WI_FLAGS_ATTACHED 0x0001
-#define WI_FLAGS_INITIALIZED 0x0002
-#define WI_FLAGS_OUTRANGE 0x0004
-#define WI_FLAGS_HAS_MOR 0x0010
+#define WI_FLAGS_HAS_ENHSECURITY 0x0001
+#define WI_FLAGS_HAS_WPASUPPORT 0x0002
#define WI_FLAGS_HAS_ROAMING 0x0020
-#define WI_FLAGS_HAS_DIVERSITY 0x0040
-#define WI_FLAGS_HAS_SYSSCALE 0x0080
-#define WI_FLAGS_BUG_AUTOINC 0x0100
#define WI_FLAGS_HAS_FRAGTHR 0x0200
#define WI_FLAGS_HAS_DBMADJUST 0x0400
-#define WI_FLAGS_SCANNING 0x0800
-
-
-/* driver-specific node state */
-struct wi_node {
- struct ieee80211_node ni; /* base class */
-};
struct wi_card_ident {
u_int16_t card_id;
@@ -240,5 +187,4 @@ extern devclass_t wi_devclass;
void wi_init(void *);
void wi_intr(void *);
int wi_mgmt_xmit(struct wi_softc *, caddr_t, int);
-void wi_stop(struct ifnet *, int);
-int wi_symbol_load_firm(struct wi_softc *, const void *, int, const void *, int);
+void wi_stop(struct wi_softc *, int);
diff --git a/sys/dev/wi/spectrum24t_cf.h b/sys/dev/wi/spectrum24t_cf.h
deleted file mode 100644
index ce053fa..0000000
--- a/sys/dev/wi/spectrum24t_cf.h
+++ /dev/null
@@ -1,4327 +0,0 @@
-/* $NetBSD$ */
-/* $FreeBSD$ */
-
-/*-
- * Copyright (c) 2001 Symbol Technologies Inc. -- http://www.symbol.com
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted provided
- * that the following conditions are met:
- * 1. Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistribution 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 author may not be used to endorse or promote products
- * derived from this software without specific prior written permission
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR 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.
- */
-
-/*
- * Firmware for Symbol Wireless Networker Spectrum24t CF card.
- * Automatically generated from EPRIMSYM and ESECSYM:
- * T3.10-04,F3.10-04,01/17/2002
- */
-
-#ifndef _FIRMWARE_WI_SPECTRUM24T_CF_H_
-#define _FIRMWARE_WI_SPECTRUM24T_CF_H_
-
-const u_int8_t spectrum24t_primsym[] = {
-0x46,0x49,0x4c,0x45,0x3a,0x20,0x45,0x50,0x52,0x49,0x4d,0x53,0x59,0x4d,0x2c,0x54,
-0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,
-0x2c,0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x20,0x50,0x52,0x49,0x4d,
-0x41,0x52,0x59,0x20,0x33,0x20,0x37,0x32,0x20,0x33,0x36,0x20,0x34,0x30,0x0a,0x1a,
-0xfe,0x17,0x7e,0x00,0x02,0x00,0xff,0xff,0x00,0x00,0x7e,0x00,0xfe,0x0f,0x00,0x64,
-0x90,0xff,0x00,0xf7,0x20,0xfe,0x00,0x64,0x91,0xff,0x01,0xf7,0x20,0xfe,0x00,0x64,
-0x92,0xff,0x02,0xf7,0x20,0xfe,0x00,0x64,0x93,0xff,0x03,0xf7,0x20,0xfe,0x00,0x64,
-0x94,0xff,0x04,0xf7,0x20,0xfe,0x00,0x64,0x95,0xff,0x05,0xf7,0x20,0xfe,0x00,0x64,
-0x96,0xff,0x06,0xf7,0x20,0xfe,0x00,0x64,0x97,0xff,0x07,0x04,0x30,0x44,0x04,0xa8,
-0xff,0xff,0x11,0x03,0x00,0x60,0xf6,0x78,0xff,0xff,0x87,0xff,0x8a,0xff,0x98,0xff,
-0x20,0xfe,0x44,0x41,0x00,0x64,0x64,0x40,0x10,0x2b,0x04,0x64,0x40,0x51,0x00,0x64,
-0x40,0x50,0x00,0x64,0x40,0x40,0x99,0xff,0x08,0x60,0x2a,0x62,0x04,0x60,0xff,0x64,
-0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x98,0xff,0x08,0x60,0x28,0x62,
-0x28,0x60,0x10,0x64,0xa2,0xdb,0x28,0x60,0x31,0x40,0x04,0x26,0xa8,0x60,0x99,0xff,
-0x10,0xe4,0xff,0xff,0xff,0xff,0x98,0xff,0x55,0x60,0xfc,0xe0,0xa0,0xfe,0xa1,0xfe,
-0xa2,0xfe,0xa3,0xfe,0xa4,0xfe,0xa5,0xfe,0xa6,0xfe,0xa7,0xfe,0xa8,0xfe,0xa9,0xfe,
-0xaa,0xfe,0xab,0xfe,0xac,0xfe,0xad,0xfe,0xae,0xfe,0xaf,0xfe,0xb0,0xfe,0xb1,0xfe,
-0xb2,0xfe,0xb3,0xfe,0xb4,0xfe,0xb5,0xfe,0xb6,0xfe,0xb7,0xfe,0xb8,0xfe,0xb9,0xfe,
-0xba,0xfe,0xbb,0xfe,0xbc,0xfe,0xbd,0xfe,0xbe,0xfe,0xbf,0xfe,0x99,0xff,0x18,0xec,
-0x0e,0xe3,0x12,0xe3,0x1d,0xe3,0x22,0xe3,0x2a,0xe3,0x32,0xe3,0x3a,0xe3,0x10,0xed,
-0x42,0xe3,0x4a,0xe3,0x52,0xe3,0x5a,0xe3,0x62,0xe3,0x68,0xe3,0x70,0xe3,0x7a,0xe3,
-0x04,0xee,0x82,0xe3,0x8a,0xe3,0x92,0xe3,0x9a,0xe3,0xa2,0xe3,0xaa,0xe3,0xb2,0xe3,
-0xba,0xe3,0x21,0x60,0x7d,0xe7,0x68,0x60,0x7d,0xe7,0x10,0x60,0x7d,0xe7,0x7d,0xe7,
-0xf0,0x60,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x6d,0x60,
-0x7d,0xe7,0x98,0x60,0x7d,0xe7,0x39,0x60,0x7d,0xe7,0x3f,0x60,0x7d,0xe7,0x42,0x60,
-0x7d,0xe7,0x0c,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,
-0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x01,0x60,0x7d,0xe7,0xad,0x60,0x7d,0xe7,0x7d,0xe7,
-0x37,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0e,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,
-0x07,0x60,0x80,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x80,0xe7,0xff,0xff,0xff,0xff,0x06,0xe3,0xff,0xff,0x98,0xff,0x30,0x44,
-0x00,0xa8,0xff,0xff,0x09,0x02,0x08,0x60,0x00,0x64,0xc8,0x81,0x3e,0x63,0x00,0x64,
-0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,0x00,0x60,0x3e,0x63,0xff,0x60,0xfe,0x61,
-0xfc,0x60,0x00,0x66,0x09,0x60,0xc2,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x05,0x60,
-0x08,0x63,0x08,0x60,0xfe,0x61,0xfc,0x60,0x00,0x66,0x0a,0x60,0x02,0x64,0x58,0xd0,
-0x59,0xd9,0xfd,0x1f,0x58,0x4f,0x2e,0x00,0x58,0x4f,0x14,0x00,0x30,0x44,0x00,0xa8,
-0x00,0x64,0x03,0x03,0x0f,0xfb,0xd4,0xfe,0x00,0x00,0x63,0xff,0xff,0xff,0x65,0xff,
-0xff,0xff,0x58,0x4f,0x41,0x00,0x01,0x60,0x9c,0x78,0xff,0xff,0x98,0xff,0x00,0xe1,
-0xa1,0xff,0xfc,0x00,0x00,0x60,0x7c,0x63,0x0e,0x60,0x80,0x64,0xe0,0x87,0x15,0xfb,
-0x04,0x61,0x60,0x46,0xdc,0x84,0x00,0xfa,0xcd,0x81,0x01,0xfc,0xfa,0x02,0x00,0x64,
-0x00,0xfa,0x80,0x60,0x00,0x65,0xb7,0x83,0x01,0xfc,0x15,0xf5,0x3c,0x63,0x01,0xfc,
-0x2f,0x58,0xff,0xff,0x00,0x60,0x7e,0x63,0xfe,0x60,0x00,0x65,0x45,0x4b,0xdc,0x60,
-0xfe,0x61,0xfc,0x60,0x00,0x65,0x0f,0x60,0x0c,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x7e,0x63,0x40,0xa1,0x40,0xa1,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x7e,0x63,0x40,0xa1,0x40,0xa1,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x2f,0x58,0xff,0xff,0x04,0xee,0x0d,0x60,0x26,0x63,0x0e,0x60,
-0x0a,0x65,0xbd,0xd1,0xff,0xff,0x64,0x48,0x64,0x47,0x00,0x7f,0x60,0x41,0x80,0xbc,
-0x60,0x4a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0xff,0xff,0xff,0xff,0xd7,0x80,
-0xff,0xff,0xef,0x02,0x68,0x40,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x01,0xbc,0x00,0x7f,
-0x40,0x5e,0x98,0xff,0x0d,0x63,0x01,0x60,0x58,0x4e,0x70,0x78,0xff,0xff,0x99,0xff,
-0x3e,0x44,0xfc,0xb4,0x00,0x7f,0x40,0x5e,0x98,0xff,0x0d,0x63,0x01,0x60,0x58,0x4e,
-0x70,0x78,0xff,0xff,0x00,0xee,0x88,0xec,0x00,0xed,0x2f,0x58,0xff,0xff,0xff,0xff,
-0xfe,0x1f,0x2e,0x58,0xff,0xff,0x98,0xff,0x00,0xe1,0x30,0x44,0x01,0xa8,0x02,0xa8,
-0x05,0x03,0x05,0x03,0x83,0xff,0x8d,0xff,0x00,0x64,0x40,0x40,0x43,0xe1,0x00,0x60,
-0x91,0x65,0x78,0x44,0xc4,0x98,0xff,0xff,0x98,0xff,0x88,0xe2,0x00,0xe1,0x30,0x44,
-0x02,0xa8,0x01,0xa8,0x0b,0x03,0x06,0x03,0x85,0xff,0x88,0xff,0xeb,0x60,0x19,0xe2,
-0x00,0x60,0x90,0xe2,0x40,0xe1,0x04,0x60,0x00,0x71,0x8d,0xe2,0x01,0x60,0x3e,0x65,
-0x78,0x44,0xc4,0x98,0xff,0xff,0x0a,0xe1,0xa3,0xff,0xae,0xff,0xff,0xff,0xff,0xff,
-0xae,0xff,0x01,0x60,0x63,0x65,0x78,0x44,0xc4,0x98,0xff,0xff,0xaa,0xff,0x08,0x60,
-0x14,0x64,0xa0,0xd1,0x04,0x60,0x41,0x76,0x3f,0x60,0xff,0x64,0xa0,0x83,0x08,0x60,
-0x14,0x64,0xa0,0xdd,0x64,0x44,0x80,0x2b,0x1d,0x00,0x50,0xfe,0x08,0x60,0x02,0x64,
-0xa0,0xd1,0xfe,0x60,0x01,0x64,0xd0,0x80,0x08,0x60,0x04,0x64,0xa0,0xd1,0xdc,0x60,
-0x23,0x64,0xd0,0x80,0x08,0x60,0x06,0x64,0xa0,0xd1,0xba,0x60,0x45,0x64,0xd0,0x80,
-0x63,0x47,0x01,0x01,0x07,0x00,0xc0,0xbf,0x60,0x43,0x08,0x60,0x14,0x64,0xa0,0xdd,
-0x04,0x60,0x01,0x76,0x41,0x00,0xab,0xff,0xff,0xff,0xff,0xff,0xab,0xff,0x3c,0x00,
-0xa9,0xff,0x77,0x44,0x60,0x57,0x40,0x4a,0x2a,0x44,0x10,0xb0,0x20,0x44,0x04,0x03,
-0xfd,0xb4,0x01,0xb0,0x40,0x40,0x01,0x02,0x2f,0x00,0xfe,0xb4,0x40,0x40,0xa8,0xff,
-0x20,0x44,0x02,0xb0,0x60,0x41,0x08,0x60,0x00,0x64,0xa0,0xd1,0x61,0x44,0x03,0x03,
-0x01,0xbc,0x40,0x40,0x1e,0x00,0x02,0xbc,0x40,0x40,0x64,0x44,0x3f,0xb4,0x0b,0xa8,
-0xff,0xff,0x06,0x02,0x64,0x44,0x80,0xb0,0xff,0xff,0x02,0x03,0x01,0x64,0x13,0xfb,
-0x0f,0xf9,0x08,0x60,0x02,0x64,0xa0,0xd1,0x10,0xf9,0x08,0x60,0x04,0x64,0xa0,0xd1,
-0xd4,0xfe,0x11,0xf9,0x08,0x60,0x06,0x64,0xa0,0xd1,0x12,0xf9,0xae,0xff,0xff,0xff,
-0xae,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0xba,0x3f,0x75,0x44,
-0x01,0x26,0x08,0xf7,0xff,0xff,0xff,0xff,0x02,0x26,0x09,0xf7,0xff,0xff,0xff,0xff,
-0x04,0x26,0x0a,0xf7,0xff,0xff,0xff,0xff,0x08,0x26,0x0b,0xf7,0xff,0xff,0xff,0xff,
-0x40,0x26,0xa7,0xff,0xe9,0x00,0x0f,0x60,0xfe,0x65,0x08,0x60,0x1c,0x64,0xa0,0xd3,
-0xff,0xff,0x24,0x86,0x80,0x67,0x60,0x5c,0x08,0x60,0x1c,0x64,0xa0,0xd9,0x08,0x60,
-0x18,0x64,0xa0,0xd3,0x15,0xf3,0x40,0x45,0xfc,0x2b,0x02,0x00,0x40,0x45,0x05,0x00,
-0x02,0x60,0x58,0x4f,0xb2,0x78,0xff,0xff,0x07,0x02,0x58,0x4f,0x4a,0x00,0x04,0x05,
-0x66,0x50,0x65,0x52,0x61,0x51,0x0a,0x00,0x40,0x67,0x26,0x45,0xb4,0x84,0x40,0x46,
-0x15,0xf5,0x06,0xf0,0x80,0x60,0x64,0x50,0x20,0x52,0x7e,0x71,0x99,0xff,0xac,0xff,
-0x98,0xff,0x26,0x5c,0x08,0x60,0x1c,0x64,0xa0,0xd9,0xb6,0x00,0x0f,0x60,0xfe,0x65,
-0x08,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0x24,0x86,0x80,0x67,0x60,0x5c,0x08,0x60,
-0x1e,0x64,0xa0,0xd9,0x08,0x60,0x1a,0x64,0xa0,0xd3,0x15,0xf3,0x40,0x45,0xfc,0x2b,
-0x02,0x00,0x40,0x45,0x03,0x00,0x58,0x4f,0x3c,0x00,0x08,0x02,0x58,0x4f,0x19,0x00,
-0x8e,0xff,0x04,0x05,0x66,0x50,0x65,0x52,0x61,0x51,0x0a,0x00,0x40,0x67,0x26,0x45,
-0xb4,0x84,0x40,0x46,0x15,0xf5,0x06,0xf0,0x80,0x60,0x64,0x50,0x20,0x52,0x7e,0x71,
-0x8d,0xff,0x99,0xff,0xad,0xff,0x98,0xff,0x26,0x5c,0x08,0x60,0x1e,0x64,0xa0,0xd9,
-0xcc,0x00,0x25,0x46,0x26,0x41,0x44,0x63,0x01,0xf2,0xff,0xff,0xff,0xb5,0xd5,0x81,
-0xff,0xff,0x07,0x04,0x00,0xf2,0x04,0x63,0x00,0xa8,0x60,0x46,0xf5,0x02,0x42,0xfe,
-0x0e,0x00,0x61,0x44,0xc5,0x81,0x63,0x45,0xc5,0x81,0x60,0x45,0x00,0x64,0xd4,0x84,
-0x01,0xf2,0xf0,0x85,0xf0,0x80,0x65,0x44,0xf8,0x85,0xff,0xff,0x02,0xfe,0x2f,0x58,
-0xff,0xff,0x25,0x44,0x1a,0xf1,0x1b,0xf1,0xd0,0x80,0xd0,0x80,0x0e,0x04,0x08,0x06,
-0x1c,0xf1,0x1d,0xf1,0xd0,0x80,0xd0,0x80,0x08,0x04,0x02,0x06,0x48,0xfe,0x05,0x00,
-0x25,0x46,0x01,0xf0,0x03,0x67,0xa0,0x85,0x94,0x80,0x2f,0x58,0xff,0xff,0x84,0xe2,
-0x04,0x60,0x00,0x71,0x8d,0xe2,0x1e,0xf3,0x14,0xf3,0x00,0xbd,0xcc,0x83,0x08,0x03,
-0x14,0xfd,0x06,0x02,0x65,0x44,0x14,0xfb,0x89,0xff,0x80,0x60,0x00,0x75,0x88,0xff,
-0xa1,0xff,0xff,0xff,0xbc,0x3f,0x7f,0x67,0x01,0x61,0x23,0x58,0xff,0xff,0x0f,0xf3,
-0x10,0xf1,0x40,0x44,0x44,0x45,0x11,0xf1,0x12,0xf1,0x44,0x46,0x44,0x47,0x3f,0xb4,
-0x0b,0xa8,0xff,0xff,0x06,0x02,0x24,0x44,0x80,0xb0,0xff,0xff,0x02,0x03,0x00,0x67,
-0x08,0x00,0x24,0x44,0x3f,0xb4,0xe0,0x85,0x09,0x60,0x00,0x64,0x44,0xd7,0x58,0x43,
-0xff,0xff,0x61,0x43,0x60,0x45,0x24,0x44,0x3f,0xb4,0xb4,0x9c,0xff,0x27,0x01,0x00,
-0x03,0x00,0x08,0x60,0x0a,0x64,0xa0,0xdd,0x08,0x60,0x08,0x64,0xa0,0xd9,0x10,0x75,
-0xa1,0xff,0xff,0xff,0xbe,0x3f,0xb4,0xfe,0xff,0xff,0xd1,0x05,0xb5,0xfe,0xb6,0xfe,
-0xb7,0xfe,0xf6,0x00,0x08,0x60,0x28,0x62,0x12,0x60,0x34,0x64,0xa2,0xdb,0x24,0x45,
-0x01,0x27,0x3d,0x00,0x7f,0x60,0xc0,0x64,0xa4,0x80,0x7f,0x67,0x02,0x61,0x40,0x02,
-0x20,0x44,0x20,0xb0,0xdf,0xb4,0x1b,0x03,0x40,0x40,0xfc,0x60,0x00,0x66,0x00,0x60,
-0x16,0x64,0x60,0x41,0x08,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x26,0x64,
-0x60,0x41,0x08,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x02,0x64,0x40,0x50,0x63,0xff,
-0xff,0xff,0x65,0xff,0xff,0xff,0x04,0x64,0x40,0x50,0x67,0xff,0xff,0xff,0xfc,0x60,
-0x00,0x66,0x17,0x60,0xfe,0x63,0xbd,0xd0,0xc0,0x60,0xde,0x64,0xd0,0x80,0xdb,0x83,
-0x0e,0x02,0x20,0x44,0x10,0xbc,0x40,0x40,0x02,0x64,0x40,0x50,0x63,0xff,0xff,0xff,
-0x65,0xff,0xff,0xff,0x00,0x64,0x40,0x50,0x0c,0x60,0x01,0x78,0xff,0xff,0x08,0x60,
-0x00,0x64,0xc8,0x81,0x3e,0x63,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,
-0x23,0x58,0xff,0xff,0x7e,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,0x7f,0x67,0x02,0x61,
-0x23,0x02,0x25,0x45,0xfc,0x2b,0x1e,0x00,0x0c,0x60,0x70,0x63,0x0e,0x61,0x24,0x44,
-0x01,0x27,0x10,0x00,0xbd,0xd3,0xa3,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,
-0x08,0xa3,0xf8,0x02,0x04,0x61,0x15,0xf5,0x00,0x64,0x22,0xfa,0x25,0x44,0x5a,0xda,
-0x00,0x67,0x0a,0x00,0xbd,0xd3,0xbe,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,
-0x08,0xa3,0xf8,0x02,0x7f,0x67,0x04,0x61,0x23,0x58,0xff,0xff,0xbf,0xd3,0xff,0xff,
-0x62,0x43,0xbf,0xd1,0xf8,0xa3,0xa3,0xd1,0x64,0x43,0x60,0x41,0x15,0xf5,0xe8,0x84,
-0xdc,0x84,0x22,0xfa,0x5a,0xd8,0x62,0x44,0xbd,0xd1,0xc9,0x81,0x58,0xd8,0xfc,0x02,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0xbf,0xd1,0xff,0xff,
-0x62,0x43,0xcc,0x84,0xe0,0x85,0x0b,0x06,0xbf,0xd1,0x64,0x41,0xd5,0x80,0x64,0x43,
-0x01,0x06,0x65,0x41,0x46,0x64,0x58,0xd0,0xc9,0x81,0xbd,0xd9,0xfc,0x02,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,0x40,0x4b,0x4b,0xd3,0x15,0xf5,
-0x60,0x41,0xd8,0x84,0xe8,0x84,0x22,0xfa,0x25,0x44,0x23,0xfa,0xbf,0xd3,0x66,0x45,
-0x48,0x63,0xc8,0x84,0x2b,0x46,0x58,0xd0,0x65,0x46,0xc9,0x81,0xbd,0xd8,0xfa,0x02,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x00,0x64,0x40,0x45,0x02,0x60,0x00,0x64,
-0x40,0x46,0x77,0x60,0xfc,0xe0,0x99,0xff,0x00,0xec,0x02,0xe3,0xd8,0xe3,0x0a,0xe1,
-0x2f,0x60,0xfe,0x62,0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0x25,0x44,0x60,0x47,
-0x01,0xb4,0xe0,0x85,0xa0,0x7e,0xb4,0x84,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,
-0x25,0x44,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x08,0x61,0x61,0x5c,0x5a,0xd1,
-0xff,0xff,0x64,0x47,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x2c,0x02,0x64,0x44,
-0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x26,0x02,0xcd,0x81,0xff,0xff,0xef,0x02,
-0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x03,0x60,0xe8,0x7a,0x2d,0xe2,0xa1,0xff,
-0xff,0xff,0x24,0xe2,0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0xa1,0x7e,0x04,0x60,
-0x58,0x4e,0xa4,0x78,0xff,0xff,0xf0,0x02,0x04,0x60,0x58,0x4e,0x97,0x78,0xff,0xff,
-0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x26,0x45,0x25,0x44,0x10,0xa4,0xd4,0x80,
-0x40,0x45,0xb8,0x02,0x28,0xe2,0xff,0x60,0xff,0x64,0xa2,0xdb,0x55,0x60,0xfc,0xe0,
-0x98,0xff,0x8d,0xe2,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x77,0x60,0xfc,0xe0,
-0x99,0xff,0x00,0xec,0x02,0xe3,0xd8,0xe3,0x04,0x60,0x58,0x4e,0x97,0x78,0xff,0xff,
-0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0xa0,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,
-0xff,0xff,0x00,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x04,0x60,0x58,0x4e,
-0x7b,0x78,0xff,0xff,0xa1,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x2f,0x60,
-0xfe,0x62,0x01,0x60,0x00,0x61,0x61,0x5c,0x04,0x60,0x58,0x4e,0xc7,0x78,0xff,0xff,
-0x60,0x47,0x60,0x45,0x04,0x60,0x58,0x4e,0xc7,0x78,0xff,0xff,0xb4,0x84,0xcd,0x81,
-0x5a,0xdb,0xf2,0x02,0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0xff,0xff,0x04,0x60,
-0x58,0x4e,0x97,0x78,0xff,0xff,0x55,0x60,0xfc,0xe0,0x98,0xff,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x01,0x60,0x02,0xe3,0xff,0xff,0xff,0xff,0x01,0xec,0xff,0xff,
-0xff,0xff,0x01,0x60,0x06,0xe3,0xff,0xff,0xff,0xff,0x00,0xec,0x2e,0x58,0xff,0xff,
-0x01,0x60,0x06,0xe3,0xff,0xff,0xff,0xff,0x01,0xec,0xff,0xff,0xff,0xff,0x01,0x60,
-0x02,0xe3,0xff,0xff,0xff,0xff,0x00,0xec,0x2e,0x58,0xff,0xff,0x09,0x63,0x01,0xec,
-0xff,0xff,0xff,0xff,0x3f,0x40,0x08,0x26,0x04,0x00,0x00,0xec,0xcf,0x83,0xff,0xff,
-0xf6,0x02,0x2e,0x58,0xff,0xff,0x0e,0x63,0x60,0x40,0x80,0x2a,0x03,0x00,0x01,0x60,
-0x02,0xe3,0x02,0x00,0x01,0x60,0x06,0xe3,0x01,0xec,0xe0,0x84,0xff,0xff,0xff,0xff,
-0xff,0xff,0x00,0xec,0xf1,0x1f,0x01,0x60,0x02,0xe3,0xff,0xff,0xff,0xff,0x3f,0x40,
-0x08,0x26,0x08,0x00,0x01,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xec,
-0x00,0x64,0x01,0x00,0x01,0x64,0x00,0xbc,0x2e,0x58,0xff,0xff,0x0e,0x63,0xe0,0x84,
-0x3f,0x40,0x08,0x26,0xdc,0x84,0x01,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x00,0xec,0xf5,0x1f,0xff,0xff,0x01,0x60,0x06,0xe3,0xff,0xff,0x01,0xec,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xec,0x01,0x60,0x02,0xe3,0x00,0x7f,0x2e,0x58,
-0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0x74,0x01,0xff,0x00,0x85,0x01,0xff,0x00,
-0x18,0x02,0xe8,0x01,0xd9,0x01,0xa7,0x01,0xd4,0x01,0xdc,0x01,0xc8,0x02,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,
-0x00,0x00,0x13,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0x63,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0x37,0x04,0xd5,0x03,0xdc,0x02,0xda,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x7e,0x00,0x08,0x11,0x00,0xf8,0x7f,0x00,0xfe,0x07,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0x00,0x00,0xc1,0xc0,0x81,0xc1,0x40,0x01,0x01,0xc3,0xc0,0x03,
-0x80,0x02,0x41,0xc2,0x01,0xc6,0xc0,0x06,0x80,0x07,0x41,0xc7,0x00,0x05,0xc1,0xc5,
-0x81,0xc4,0x40,0x04,0x01,0xcc,0xc0,0x0c,0x80,0x0d,0x41,0xcd,0x00,0x0f,0xc1,0xcf,
-0x81,0xce,0x40,0x0e,0x00,0x0a,0xc1,0xca,0x81,0xcb,0x40,0x0b,0x01,0xc9,0xc0,0x09,
-0x80,0x08,0x41,0xc8,0x01,0xd8,0xc0,0x18,0x80,0x19,0x41,0xd9,0x00,0x1b,0xc1,0xdb,
-0x81,0xda,0x40,0x1a,0x00,0x1e,0xc1,0xde,0x81,0xdf,0x40,0x1f,0x01,0xdd,0xc0,0x1d,
-0x80,0x1c,0x41,0xdc,0x00,0x14,0xc1,0xd4,0x81,0xd5,0x40,0x15,0x01,0xd7,0xc0,0x17,
-0x80,0x16,0x41,0xd6,0x01,0xd2,0xc0,0x12,0x80,0x13,0x41,0xd3,0x00,0x11,0xc1,0xd1,
-0x81,0xd0,0x40,0x10,0x01,0xf0,0xc0,0x30,0x80,0x31,0x41,0xf1,0x00,0x33,0xc1,0xf3,
-0x81,0xf2,0x40,0x32,0x00,0x36,0xc1,0xf6,0x81,0xf7,0x40,0x37,0x01,0xf5,0xc0,0x35,
-0x80,0x34,0x41,0xf4,0x00,0x3c,0xc1,0xfc,0x81,0xfd,0x40,0x3d,0x01,0xff,0xc0,0x3f,
-0x80,0x3e,0x41,0xfe,0x01,0xfa,0xc0,0x3a,0x80,0x3b,0x41,0xfb,0x00,0x39,0xc1,0xf9,
-0x81,0xf8,0x40,0x38,0x00,0x28,0xc1,0xe8,0x81,0xe9,0x40,0x29,0x01,0xeb,0xc0,0x2b,
-0x80,0x2a,0x41,0xea,0x01,0xee,0xc0,0x2e,0x80,0x2f,0x41,0xef,0x00,0x2d,0xc1,0xed,
-0x81,0xec,0x40,0x2c,0x01,0xe4,0xc0,0x24,0x80,0x25,0x41,0xe5,0x00,0x27,0xc1,0xe7,
-0x81,0xe6,0x40,0x26,0x00,0x22,0xc1,0xe2,0x81,0xe3,0x40,0x23,0x01,0xe1,0xc0,0x21,
-0x80,0x20,0x41,0xe0,0x01,0xa0,0xc0,0x60,0x80,0x61,0x41,0xa1,0x00,0x63,0xc1,0xa3,
-0x81,0xa2,0x40,0x62,0x00,0x66,0xc1,0xa6,0x81,0xa7,0x40,0x67,0x01,0xa5,0xc0,0x65,
-0x80,0x64,0x41,0xa4,0x00,0x6c,0xc1,0xac,0x81,0xad,0x40,0x6d,0x01,0xaf,0xc0,0x6f,
-0x80,0x6e,0x41,0xae,0x01,0xaa,0xc0,0x6a,0x80,0x6b,0x41,0xab,0x00,0x69,0xc1,0xa9,
-0x81,0xa8,0x40,0x68,0x00,0x78,0xc1,0xb8,0x81,0xb9,0x40,0x79,0x01,0xbb,0xc0,0x7b,
-0x80,0x7a,0x41,0xba,0x01,0xbe,0xc0,0x7e,0x80,0x7f,0x41,0xbf,0x00,0x7d,0xc1,0xbd,
-0x81,0xbc,0x40,0x7c,0x01,0xb4,0xc0,0x74,0x80,0x75,0x41,0xb5,0x00,0x77,0xc1,0xb7,
-0x81,0xb6,0x40,0x76,0x00,0x72,0xc1,0xb2,0x81,0xb3,0x40,0x73,0x01,0xb1,0xc0,0x71,
-0x80,0x70,0x41,0xb0,0x00,0x50,0xc1,0x90,0x81,0x91,0x40,0x51,0x01,0x93,0xc0,0x53,
-0x80,0x52,0x41,0x92,0x01,0x96,0xc0,0x56,0x80,0x57,0x41,0x97,0x00,0x55,0xc1,0x95,
-0x81,0x94,0x40,0x54,0x01,0x9c,0xc0,0x5c,0x80,0x5d,0x41,0x9d,0x00,0x5f,0xc1,0x9f,
-0x81,0x9e,0x40,0x5e,0x00,0x5a,0xc1,0x9a,0x81,0x9b,0x40,0x5b,0x01,0x99,0xc0,0x59,
-0x80,0x58,0x41,0x98,0x01,0x88,0xc0,0x48,0x80,0x49,0x41,0x89,0x00,0x4b,0xc1,0x8b,
-0x81,0x8a,0x40,0x4a,0x00,0x4e,0xc1,0x8e,0x81,0x8f,0x40,0x4f,0x01,0x8d,0xc0,0x4d,
-0x80,0x4c,0x41,0x8c,0x00,0x44,0xc1,0x84,0x81,0x85,0x40,0x45,0x01,0x87,0xc0,0x47,
-0x80,0x46,0x41,0x86,0x01,0x82,0xc0,0x42,0x80,0x43,0x41,0x83,0x00,0x41,0xc1,0x81,
-0x81,0x80,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x63,0x03,0xff,0xff,0xbc,0x03,0x8b,0x03,0xdc,0x10,0x0e,0x00,0xfe,0xff,0xbc,0x03,
-0x8b,0x03,0xda,0x10,0x02,0x00,0x00,0xfd,0xbc,0x03,0x8b,0x03,0x8e,0x10,0x02,0x00,
-0x01,0xfd,0xbc,0x03,0x8b,0x03,0x90,0x10,0x06,0x00,0x02,0xfd,0xbc,0x03,0x8b,0x03,
-0xaa,0x10,0x08,0x00,0x03,0xfd,0xbc,0x03,0x8b,0x03,0xb2,0x10,0x0a,0x00,0x04,0xfd,
-0xbc,0x03,0x8b,0x03,0xc6,0x10,0x0a,0x00,0x05,0xfd,0xbc,0x03,0x8b,0x03,0xea,0x10,
-0x0c,0x00,0x06,0xfd,0xbc,0x03,0x8b,0x03,0xf8,0x10,0x0c,0x00,0x0a,0xfd,0xbc,0x03,
-0x8b,0x03,0x96,0x10,0x0c,0x00,0x0b,0xfd,0xbc,0x03,0x8b,0x03,0xa2,0x10,0x08,0x00,
-0x0c,0xfd,0xbc,0x03,0x8b,0x03,0xbc,0x10,0x0a,0x00,0x0d,0xfd,0xbc,0x03,0x8b,0x03,
-0xd0,0x10,0x0a,0x00,0xe0,0xfc,0x8f,0x03,0xa5,0x03,0x3c,0x00,0x02,0x00,0x09,0x00,
-0x06,0x00,0x74,0x01,0x0e,0x00,0x18,0x02,0x10,0x00,0xe8,0x01,0x12,0x00,0xd9,0x01,
-0x14,0x00,0xa7,0x01,0x16,0x00,0xd4,0x01,0x18,0x00,0xdc,0x01,0x1a,0x00,0xc8,0x02,
-0x0a,0x00,0x85,0x01,0x00,0x01,0x80,0x00,0x00,0x02,0x01,0x04,0x38,0x06,0x80,0x08,
-0x03,0x0a,0x04,0x0c,0x04,0x0e,0x00,0x10,0x00,0x12,0xc8,0x14,0x13,0x16,0x00,0x18,
-0x00,0x1a,0x00,0x1c,0x5c,0x1e,0xc1,0x20,0x1e,0x22,0x54,0x24,0x07,0x26,0x6a,0x28,
-0x12,0x2a,0x00,0x2c,0x00,0x2e,0x1c,0x30,0x20,0x32,0x82,0x34,0x08,0x36,0x7a,0x38,
-0xca,0x3a,0x24,0x3c,0xd6,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x7f,0x46,0x8b,0x48,
-0x0f,0x4a,0x06,0x4c,0x0a,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,
-0x20,0x5a,0xee,0x5c,0x1a,0x5e,0x26,0x60,0x5b,0x62,0x00,0x04,0x00,0x2c,0x0c,0x2e,
-0x01,0x2c,0x10,0x2e,0x02,0x2c,0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,
-0x05,0x2c,0x20,0x2e,0x06,0x2c,0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,
-0x09,0x2c,0x34,0x2e,0x0a,0x2c,0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,
-0x0d,0x2c,0x43,0x2e,0x0e,0x2c,0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,
-0x11,0x2c,0x50,0x2e,0x12,0x2c,0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,
-0x15,0x2c,0x6d,0x2e,0x16,0x2c,0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,
-0x19,0x2c,0x7f,0x2e,0x1a,0x2c,0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,
-0x1d,0x2c,0x7f,0x2e,0x1e,0x2c,0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0xff,0xff,0x0e,0xf1,
-0x02,0x60,0x5f,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0e,0xf1,
-0x02,0x60,0x2c,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,
-0x0e,0xf1,0x02,0x60,0x15,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0xf7,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x47,0xff,
-0x0e,0xf1,0x02,0x60,0x15,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0x40,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x42,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x43,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0d,0xf7,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x0f,0x7e,0x00,
-0x0a,0x01,0xff,0xff,0x47,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xb0,0xfe,0xb1,0xfe,0xb2,0xfe,0xb3,0xfe,0xff,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0x0e,0xf1,0x03,0x60,0x0c,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xb8,0xfe,0xb9,0xfe,0xba,0xfe,0xbb,0xfe,0xff,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x20,0x4e,0x60,0x00,0x00,0x00,0x00,0x40,0x39,0x39,0x53,0x41,0x30,0x31,
-0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x17,0x00,
-0x02,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
-0x50,0x72,0x69,0x6d,0x61,0x72,0x79,0x20,0x46,0x27,0x73,0x20,0x20,0x00,0x46,0x33,
-0x2e,0x31,0x30,0x2d,0x30,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x31,0x2f,0x31,
-0x37,0x2f,0x32,0x30,0x30,0x32,0x00,0x00,0x00,0x00,0x01,0x80,0xff,0xff,0xff,0xff,
-0xdc,0x10,0x7e,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x96,0x10,0x7e,0x00,
-0x0c,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x26,0x0e,0x7e,0x00,0x02,0x00,0x00,0x00,
-0x06,0x00,0x00,0x00,0xbc,0x10,0x7e,0x00,0x0a,0x00,0x00,0x00,0x07,0x00,0x00,0x00,
-0xd0,0x10,0x7e,0x00,0x0a,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xa2,0x10,0x7e,0x00,
-0x08,0x00,0x00,0x00,0xfe,0x17,0x7e,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0xf8,0x7f,0x00,0xfe,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,
-0x08,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x01,0x00,0x17,0x00,0x02,0x00,
-0x02,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x7f,0xff
-};
-
-const u_int8_t spectrum24t_secsym[] = {
-0x46,0x49,0x4c,0x45,0x3a,0x20,0x45,0x53,0x45,0x43,0x53,0x59,0x4d,0x2c,0x54,0x33,
-0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,
-0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x20,0x53,0x45,0x43,0x4f,0x4e,
-0x44,0x41,0x52,0x59,0x20,0x32,0x32,0x20,0x32,0x34,0x30,0x20,0x31,0x32,0x20,0x35,
-0x34,0x20,0x0a,0x1a,0xfe,0x17,0x7e,0x00,0xf2,0x0f,0xde,0xc0,0xce,0xd0,0x0a,0x60,
-0x04,0x63,0xa3,0xd3,0x06,0xa3,0xdc,0x80,0x00,0xa8,0x0b,0x03,0xfa,0x02,0xf6,0xa3,
-0x18,0x60,0x00,0x64,0xbd,0xdb,0x00,0x60,0x7e,0x64,0xbd,0xdb,0xd0,0x60,0xce,0x64,
-0xa3,0xdb,0x0c,0x60,0x23,0x78,0xff,0xff,0x7f,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,
-0x7f,0x67,0x02,0x61,0x02,0x03,0x23,0x58,0xff,0xff,0x02,0x64,0x40,0x50,0x61,0xff,
-0xff,0xff,0x99,0xff,0x88,0xec,0x0e,0xe3,0x12,0xe3,0x1d,0xe3,0x22,0xe3,0x2a,0xe3,
-0x32,0xe3,0x3a,0xe3,0x80,0xed,0x42,0xe3,0x4a,0xe3,0x52,0xe3,0x5a,0xe3,0x62,0xe3,
-0x68,0xe3,0x70,0xe3,0x7a,0xe3,0x00,0xee,0x82,0xe3,0x8a,0xe3,0x92,0xe3,0x9a,0xe3,
-0xa2,0xe3,0xaa,0xe3,0xb2,0xe3,0xba,0xe3,0x01,0x60,0x14,0xe3,0x01,0x60,0x19,0xe3,
-0x01,0x60,0x20,0xe3,0x01,0x60,0x28,0xe3,0x21,0x60,0x7d,0xe7,0x68,0x60,0x7d,0xe7,
-0x10,0x60,0x7d,0xe7,0x7d,0xe7,0xf0,0x60,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,
-0x36,0x60,0x7d,0xe7,0x6d,0x60,0x7d,0xe7,0x98,0x60,0x7d,0xe7,0x39,0x60,0x7d,0xe7,
-0x3f,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0c,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,
-0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x01,0x60,0x7d,0xe7,
-0xad,0x60,0x7d,0xe7,0x7d,0xe7,0x37,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0e,0x60,
-0x7d,0xe7,0xc2,0x60,0x7d,0xe7,0x07,0x60,0x80,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe7,0xff,0xff,0xff,0xff,0x06,0xe3,
-0xff,0xff,0x98,0xff,0x73,0x60,0xeb,0x78,0xff,0xff,0x04,0xee,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x00,0x69,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x5e,0x00,0x7f,0x1f,0xfb,0x88,0xec,0x00,0xee,0xff,0xff,0xff,0xff,
-0x04,0xee,0xff,0xff,0xff,0xff,0x00,0xee,0xb0,0xfe,0xb1,0xfe,0xb2,0xfe,0xb3,0xfe,
-0xb8,0xfe,0xb9,0xfe,0xba,0xfe,0x10,0x64,0x40,0x40,0x00,0x64,0x40,0x41,0x40,0x42,
-0x40,0x54,0x40,0x55,0x40,0x5e,0x40,0x5a,0x40,0x5b,0x1d,0x60,0x8c,0x62,0xa2,0xd1,
-0x12,0x60,0x34,0x64,0xd0,0x80,0xff,0xff,0x20,0x02,0x1d,0x60,0x8e,0x62,0xa2,0xd1,
-0x55,0x60,0xaa,0x64,0xd0,0x80,0xff,0xff,0x18,0x02,0x1e,0x60,0x50,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x08,0x60,0x2e,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa4,
-0xe1,0xa0,0x17,0x03,0xe0,0x85,0x15,0x07,0x1e,0x60,0x52,0x62,0xff,0xff,0xc6,0x82,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x0c,0x00,0x1e,0x60,0x50,0x62,0x00,0x64,
-0xa2,0xdb,0x1e,0x60,0x52,0x63,0x00,0x64,0x20,0x61,0xbd,0xdb,0xff,0xa1,0xff,0xff,
-0xfc,0x02,0x01,0x60,0x88,0x63,0x00,0x60,0x3e,0x61,0xfe,0x60,0x00,0x66,0x7f,0x60,
-0xfe,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x0f,0x60,0xce,0x63,0x0e,0x60,0x7e,0x61,
-0xfe,0x60,0x00,0x66,0x85,0x60,0x08,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x4a,0x60,
-0x8a,0x63,0x1e,0x60,0x90,0x61,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x01,0x63,0x7f,0xfd,
-0x00,0x60,0x2a,0x63,0x0c,0x60,0x40,0x61,0x0e,0x60,0x7e,0x64,0x58,0xd1,0x59,0xd9,
-0xfd,0x1f,0x15,0x60,0xf8,0x61,0xa1,0xd3,0xff,0xff,0xe0,0x83,0xcb,0x83,0x59,0xd1,
-0x59,0xd3,0xa4,0xdb,0xfc,0x1f,0x00,0x60,0xfe,0x63,0xfe,0x60,0x00,0x65,0x45,0x4b,
-0xdb,0x60,0xfe,0x61,0xfe,0x60,0x00,0x65,0x81,0x60,0x88,0x64,0x65,0x46,0x58,0xd0,
-0x2b,0x46,0x59,0xd8,0xfb,0x1f,0x01,0x60,0xbe,0x63,0xdd,0x60,0x4e,0x61,0x82,0x60,
-0xd8,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,0x59,0xd8,0xfb,0x1f,0x00,0x60,0x0e,0x63,
-0xdf,0x60,0x1e,0x61,0x84,0x60,0xa8,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,0x59,0xd8,
-0xfb,0x1f,0x69,0x60,0x1e,0x64,0x7f,0xa4,0xe0,0x87,0x00,0x7f,0x1a,0xfb,0x0d,0x60,
-0x22,0x62,0x08,0x60,0x00,0x65,0xa2,0xd3,0xff,0xff,0xd4,0x80,0xff,0xff,0x0b,0x06,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xcc,0x84,0x1d,0xfb,0x65,0x44,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x1c,0xfb,0x65,0x44,0x80,0xa4,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xcc,0x84,
-0x1b,0xfb,0x1b,0xf1,0x1a,0xf3,0xff,0xff,0x9e,0xfb,0x7c,0x63,0x60,0x46,0x01,0xfc,
-0xdc,0x84,0xd0,0x80,0x00,0xfa,0xfa,0x04,0x9f,0xfb,0x1c,0xf3,0x60,0x46,0x00,0xa8,
-0x1d,0xf1,0x09,0x03,0x00,0xfa,0x01,0xfc,0x60,0x46,0x01,0xfc,0xdc,0x84,0xd0,0x80,
-0x00,0xfa,0xfa,0x04,0x9f,0xfb,0x00,0x64,0x00,0xfa,0x63,0x44,0x80,0x7f,0x01,0xfa,
-0x1b,0xf3,0x1a,0xf1,0xdc,0x84,0x50,0x93,0x33,0x44,0xfd,0xfb,0x00,0x60,0x7c,0x61,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0x46,0x45,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x1e,0x60,0x9e,0x62,0x66,0x44,0xa2,0xdb,0x1f,0xf1,0x01,0x65,0x64,0x40,
-0x10,0x2a,0x07,0x00,0xeb,0x60,0x19,0xe2,0x01,0x60,0x76,0x63,0x20,0x64,0xc5,0xfb,
-0x07,0x00,0xeb,0x60,0x19,0xe2,0x00,0x65,0x01,0x60,0x18,0x63,0x14,0x64,0xc2,0xfb,
-0x17,0x60,0x34,0x62,0xa2,0xdd,0x17,0x60,0x7e,0x62,0x65,0x44,0xa2,0xdb,0x00,0x60,
-0x30,0xe2,0x00,0x60,0x50,0xe2,0x00,0x60,0x79,0xe2,0x01,0x60,0x90,0xe2,0x01,0x60,
-0xd0,0xe2,0x01,0x60,0xf9,0xe2,0xb4,0xf3,0xb5,0xf1,0x60,0x45,0xc4,0x84,0xc0,0x84,
-0x24,0xfb,0x01,0x60,0x30,0x64,0xc0,0x84,0x25,0xfb,0x60,0x45,0xa0,0xa4,0x29,0xfb,
-0x01,0x60,0x60,0x64,0xc0,0x84,0x2d,0xfb,0xa0,0xa4,0x31,0xfb,0x00,0x60,0xf8,0x64,
-0xc0,0x84,0x26,0xfb,0xa0,0xa4,0x2a,0xfb,0x01,0x60,0x10,0x64,0xc0,0x84,0x2e,0xfb,
-0xa0,0xa4,0x32,0xfb,0x00,0x60,0xd5,0x64,0xc0,0x84,0x27,0xfb,0xa0,0xa4,0x2b,0xfb,
-0x00,0x60,0xde,0x64,0xc0,0x84,0x2f,0xfb,0xa0,0xa4,0x33,0xfb,0x00,0x60,0xcb,0x64,
-0xc0,0x84,0x28,0xfb,0xa0,0xa4,0x2c,0xfb,0x00,0x60,0xcf,0x64,0xc0,0x84,0x30,0xfb,
-0xa0,0xa4,0x34,0xfb,0xb5,0xf3,0x24,0xf1,0xc4,0x84,0x35,0xfb,0xc0,0x84,0x37,0xfb,
-0x00,0x64,0x40,0x50,0x63,0xff,0x00,0x64,0x40,0x54,0x40,0x55,0x40,0x41,0x40,0x42,
-0x40,0x5e,0xd1,0xfe,0x82,0xff,0x92,0xff,0x98,0xff,0x00,0x64,0x40,0x52,0x17,0x60,
-0x3a,0x65,0x1f,0xf3,0xa5,0xd1,0x60,0x40,0x10,0x2a,0xff,0xff,0x20,0x26,0x03,0x00,
-0x13,0x60,0x98,0x62,0x02,0x00,0x14,0x60,0x7a,0x62,0x64,0x44,0x3e,0x7f,0xa2,0xdb,
-0x1f,0xf3,0xff,0xff,0x17,0x60,0x5c,0x65,0xa5,0xd1,0x60,0x40,0x20,0x26,0x05,0x00,
-0x13,0x60,0xa6,0x62,0x64,0x44,0x4c,0x7f,0x04,0x00,0x14,0x60,0x82,0x62,0x64,0x44,
-0x46,0x7f,0xa2,0xdb,0x00,0x64,0x40,0x41,0x40,0x46,0x40,0x47,0x00,0xe1,0x11,0x60,
-0x8e,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0x00,0x60,0x13,0x66,0x3c,0x64,0x01,0xfa,
-0x0a,0x64,0x20,0xfa,0x87,0xff,0x97,0xff,0x08,0x60,0x28,0x62,0x23,0x60,0x45,0x64,
-0xa2,0xdb,0x66,0xff,0xff,0xff,0x65,0xff,0xff,0xff,0x64,0xff,0xff,0xff,0x62,0xff,
-0xff,0xff,0x61,0xff,0xff,0xff,0x3c,0x60,0xa6,0x65,0x0c,0x64,0xa5,0xdb,0x0e,0x60,
-0x5a,0x64,0x97,0xfb,0xff,0xff,0x2d,0xff,0x08,0x60,0x00,0x64,0xc8,0x81,0x3e,0x63,
-0x00,0x64,0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,0x10,0x60,0x5f,0x78,0xff,0xff,
-0x10,0x75,0x01,0x60,0x03,0xe8,0x99,0xff,0x08,0x60,0x2a,0x62,0x04,0x60,0xff,0x64,
-0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x10,0x60,0xdc,0xe0,0xff,0xff,
-0xff,0xff,0x98,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0xa1,0xff,0xff,0xff,0xfd,0x00,
-0x98,0xff,0x30,0x44,0x02,0xa8,0x00,0xe1,0x07,0x02,0x62,0xff,0x63,0xff,0x64,0xff,
-0x65,0xff,0x66,0xff,0xa1,0xff,0xff,0xff,0x82,0xff,0x91,0xff,0x99,0xff,0x88,0xff,
-0x6c,0x40,0x41,0xff,0xc4,0xe2,0x43,0xff,0x40,0x49,0x08,0xe1,0x10,0x60,0x81,0x78,
-0xff,0xff,0x98,0xff,0x30,0x44,0x02,0xa8,0x00,0xe1,0x02,0x02,0xa1,0xff,0xff,0xff,
-0x00,0x64,0xdc,0xfb,0x82,0xff,0x92,0xff,0x98,0xff,0x88,0xff,0x72,0x44,0x60,0x52,
-0x03,0x04,0x01,0x64,0x40,0x40,0x05,0x00,0xdc,0x80,0xff,0xff,0x02,0x02,0x01,0x64,
-0x40,0x40,0x48,0xe2,0xe2,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x09,0x00,0x67,0x60,
-0x84,0x65,0xa5,0xd1,0xff,0xff,0x64,0x40,0x00,0x3a,0x02,0x00,0x64,0xe2,0x01,0x70,
-0x6d,0xe2,0xbc,0xff,0xb5,0xff,0xff,0x64,0x40,0x4b,0x00,0x64,0x40,0x4d,0x40,0x47,
-0xd7,0xfb,0x22,0xfb,0x00,0xe1,0x08,0x64,0x40,0x4c,0x26,0x44,0x02,0xb4,0x40,0x46,
-0x65,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,0x02,0x00,0x00,0x3a,0x03,0x00,0x68,0xe2,
-0xc8,0xe2,0x68,0x00,0xb4,0xf1,0x02,0x64,0x64,0x56,0x60,0x54,0xcd,0xe2,0xc4,0xe2,
-0x6c,0x40,0x07,0x60,0x80,0xe8,0x44,0xe2,0x64,0xe2,0x46,0xff,0x47,0xff,0x67,0x60,
-0x5c,0x62,0x01,0x64,0xa2,0xdb,0x9c,0xfe,0xff,0xff,0x0b,0x04,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x06,0x02,0x01,0x64,0x40,0xfb,0x26,0x44,0xfd,0xb4,0x40,0x46,
-0x05,0xff,0x27,0x44,0x06,0x22,0x06,0x00,0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,
-0xc0,0xfe,0x03,0x00,0x20,0x64,0x21,0xfb,0xc0,0xfe,0x99,0xff,0x3d,0x44,0xf7,0xb4,
-0x40,0x5d,0x98,0xff,0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x3e,0x44,
-0x7c,0xb4,0x08,0xbc,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x99,0xff,0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0xbc,0xff,
-0xff,0xff,0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,0xff,0xff,
-0xff,0xff,0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0x99,0xff,0x3e,0x44,
-0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x46,0xff,0x47,0xff,0x0e,0x60,0xac,0x64,0xa0,0xd7,0xff,0xff,0xff,0xff,0x98,0xff,
-0x30,0x44,0x02,0xa8,0x00,0xe1,0x0f,0x03,0x83,0xff,0x8d,0xff,0x00,0x64,0x40,0x40,
-0x40,0x44,0x40,0x43,0x40,0x42,0x40,0x41,0x1a,0x60,0xcc,0x64,0x40,0x4e,0x3c,0x60,
-0x6a,0x64,0x40,0x4d,0xe3,0xe1,0x19,0x60,0x56,0x78,0xff,0xff,0x98,0xff,0x30,0x44,
-0x02,0xa8,0x00,0xe1,0x02,0x02,0xa1,0xff,0xff,0xff,0x84,0xff,0x88,0xff,0x98,0xff,
-0x99,0xff,0xf2,0xe6,0xda,0xe6,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0xa8,0xf3,
-0x80,0xfb,0x0a,0x64,0x40,0x4b,0x1e,0x60,0x98,0x65,0xb8,0xf3,0xff,0xff,0xa5,0xdb,
-0x01,0x64,0x8c,0xfb,0x00,0x64,0x8e,0xfb,0x8d,0xfb,0x40,0x5c,0x1b,0x60,0x20,0x78,
-0xff,0xff,0x98,0xff,0x88,0xe2,0x30,0x44,0x00,0xe1,0x02,0xa8,0x85,0xff,0x02,0x02,
-0xa1,0xff,0xff,0xff,0x88,0xff,0x99,0xff,0x00,0x60,0x00,0xeb,0xff,0xff,0xff,0xff,
-0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x4f,0x60,0xf3,0xea,0x4f,0x60,0x36,0xeb,
-0x43,0x60,0x40,0xea,0x43,0x60,0xe4,0xeb,0x44,0x60,0x52,0xea,0x44,0x60,0x34,0xeb,
-0x45,0x60,0x7d,0xea,0x45,0x60,0x58,0xeb,0x47,0x60,0x8b,0xea,0x47,0x60,0xd0,0xeb,
-0x48,0x60,0x47,0xea,0x48,0x60,0xc3,0xeb,0x49,0x60,0xa0,0xea,0x49,0x60,0xfd,0xeb,
-0x4a,0x60,0xb2,0xea,0x4a,0x60,0x34,0xeb,0x4b,0x60,0xc1,0xea,0x4b,0x60,0x58,0xeb,
-0x4c,0x60,0xd7,0xea,0x4c,0x60,0xc0,0xeb,0x4d,0x60,0xeb,0xea,0x4d,0x60,0xd0,0xeb,
-0x4e,0x60,0xa0,0xea,0x4e,0x60,0x91,0xeb,0x40,0x60,0xf0,0xea,0x40,0x60,0xfc,0xeb,
-0x41,0x60,0x24,0xea,0x41,0x60,0xa2,0xeb,0x42,0x60,0x20,0xea,0x42,0x60,0x20,0xeb,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x06,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x67,0x60,
-0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0xff,0xff,0x30,0x60,0x00,0xea,0xff,0xff,0xff,0xff,
-0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0x3a,0x5c,0x40,0x27,0xfc,0x00,0xa0,0xd9,
-0x00,0x60,0x00,0xeb,0xa0,0x60,0x00,0xeb,0xc0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,
-0x67,0x60,0xce,0x64,0x3b,0x5c,0xa0,0xd9,0x3b,0x5c,0x40,0x27,0xfc,0x00,0xa0,0xd9,
-0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0x98,0xff,0xc0,0x60,0x00,0xeb,0x00,0x64,
-0x3e,0xfb,0x40,0xfb,0xff,0xff,0x67,0x60,0x4c,0x62,0x00,0x64,0xa2,0xdb,0x0a,0x64,
-0x40,0x48,0x03,0x60,0xe8,0x64,0x40,0x4b,0x67,0x60,0x4a,0x62,0x05,0x60,0xdc,0x64,
-0xa2,0xdb,0x1e,0x64,0x40,0x4c,0x69,0xe1,0x04,0x60,0x00,0x71,0x8d,0xe2,0x00,0x64,
-0x40,0x40,0xf8,0x60,0x89,0x78,0xff,0xff,0xa2,0xff,0x98,0xff,0x30,0x44,0x02,0xa8,
-0x00,0xe1,0x28,0x03,0x86,0xff,0x88,0xff,0x18,0x60,0xc6,0x65,0x64,0x64,0xa5,0xdb,
-0xff,0xff,0x00,0x64,0x40,0x46,0x58,0xfb,0x28,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0x28,0x60,0x58,0x4f,0x87,0x78,0xff,0xff,0x29,0x60,0x58,0x4f,0x0f,0x78,0xff,0xff,
-0x28,0x60,0x58,0x4f,0x3b,0x78,0xff,0xff,0x27,0x60,0x58,0x4f,0xe8,0x78,0xff,0xff,
-0x27,0x60,0x58,0x4f,0xd1,0x78,0xff,0xff,0x27,0x60,0x58,0x4f,0xff,0x78,0xff,0xff,
-0x13,0xe1,0xa3,0xff,0x3d,0x60,0x2f,0x78,0xff,0xff,0x0f,0x4e,0x01,0x60,0xe4,0x61,
-0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,
-0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,
-0x66,0x44,0x15,0xfb,0x07,0xe1,0xa3,0xff,0x04,0x60,0x41,0x76,0x00,0x60,0x00,0x7c,
-0x08,0x60,0x14,0x64,0xa0,0xd9,0xae,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0xa1,0xff,
-0xff,0xff,0x2c,0x45,0xb8,0x3f,0x41,0xff,0x30,0x44,0x20,0xb4,0x34,0x91,0x9f,0xfe,
-0xff,0xff,0x43,0x05,0x29,0x44,0x05,0x22,0xf2,0x00,0x04,0x26,0x3a,0x00,0x01,0x2a,
-0xee,0x00,0x44,0xff,0xc8,0x74,0xcd,0xe2,0x0c,0xe1,0x29,0x44,0xfe,0xb4,0x40,0x49,
-0x24,0x41,0xe1,0x81,0x00,0x60,0xc8,0x65,0xc5,0x94,0x0c,0xe1,0xe0,0x00,0x1a,0xff,
-0xde,0x00,0xdd,0x00,0x41,0xff,0x40,0x64,0xa0,0xfb,0x3e,0x44,0x01,0x26,0xd7,0x00,
-0x08,0x00,0xc4,0xe2,0x41,0x64,0xa0,0xfb,0x3e,0x44,0x01,0x2a,0x02,0x00,0x62,0xff,
-0x09,0x00,0x01,0x64,0xdc,0xfb,0x67,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x1a,0xff,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x08,0xe1,0x00,0x64,
-0x40,0x49,0x72,0x52,0x32,0x7b,0x4d,0xe2,0x44,0xff,0xb9,0x00,0xb8,0x00,0xb7,0x00,
-0xb6,0x00,0x29,0x44,0xfb,0xb4,0x40,0x49,0xb6,0x00,0x65,0xf3,0xff,0xff,0xff,0xff,
-0x04,0x2a,0x04,0x00,0xbf,0xfe,0x10,0x60,0xb3,0x78,0xff,0xff,0x99,0xf1,0x99,0xff,
-0x64,0x40,0x02,0x3b,0x03,0x00,0x11,0x60,0x5f,0x78,0xff,0xff,0x03,0x60,0xe8,0x74,
-0xcd,0xe2,0x04,0xe1,0xa1,0xff,0xff,0xff,0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x00,0x6b,
-0x3e,0x44,0x74,0xb4,0x04,0xbc,0x40,0x5e,0xff,0xff,0xff,0xff,0x02,0xbd,0x45,0x5e,
-0xff,0xff,0xff,0xff,0x40,0x5e,0x00,0xe1,0x00,0x7c,0x15,0x60,0x22,0x62,0x58,0x4f,
-0x18,0x00,0x58,0x4f,0x16,0x00,0x58,0x4f,0x14,0x00,0x58,0x4f,0x12,0x00,0x01,0x7c,
-0x58,0x4f,0x0f,0x00,0x58,0x4f,0x0d,0x00,0x3d,0x44,0x7f,0xb4,0x40,0x5d,0xff,0xff,
-0xff,0xff,0x80,0xbc,0x40,0x5d,0xbf,0xfe,0x2d,0xff,0x08,0xe1,0x10,0x60,0x81,0x78,
-0xff,0xff,0x02,0x65,0xa2,0xd3,0x02,0xa2,0x60,0x47,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x81,0x06,0x63,0xe1,0x81,0x3d,0x44,0x80,0xb4,0x10,0xbc,0x02,0x24,0x04,0xbc,
-0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,0xff,0xff,0xff,0xff,0x40,0x5d,0xa2,0xd3,
-0x02,0xa2,0x60,0x47,0x60,0x41,0x0e,0x63,0xe1,0x81,0x3d,0x44,0x80,0xb4,0x10,0xbc,
-0x02,0x24,0x04,0xbc,0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,0xff,0xff,0xff,0xff,
-0x40,0x5d,0xa2,0xd3,0x02,0xa2,0x60,0x47,0x60,0x41,0x0e,0x63,0xe1,0x81,0x3d,0x44,
-0x80,0xb4,0x10,0xbc,0x02,0x24,0x04,0xbc,0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,
-0xff,0xff,0xff,0xff,0x40,0x5d,0x64,0x40,0x01,0x26,0x08,0x00,0x3c,0x44,0x5f,0xb4,
-0x20,0x65,0x34,0x9c,0xff,0xff,0xff,0xff,0x40,0x5c,0x05,0x00,0x01,0x65,0x34,0x9d,
-0xff,0xff,0xff,0xff,0x40,0x5d,0x2f,0x58,0xff,0xff,0x99,0xf1,0x00,0xe1,0x64,0x44,
-0x00,0x7f,0xe0,0x85,0xc4,0x84,0xe0,0x85,0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x00,0x6b,
-0x3e,0x44,0x74,0xb4,0x40,0x5e,0x01,0x7c,0x15,0x60,0x46,0x62,0xc6,0x82,0x58,0x4f,
-0xa0,0x00,0x01,0x60,0xf4,0x64,0x60,0x54,0xcd,0xe2,0x32,0x44,0x08,0x2b,0xfd,0x00,
-0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x62,0xff,0x01,0x7c,0x08,0x60,0x2a,0x64,
-0xa0,0xd9,0x9a,0xf3,0xbf,0xfe,0x60,0x40,0x05,0x36,0x2d,0xff,0x07,0x36,0xd8,0xfe,
-0x08,0xe1,0x10,0x60,0x81,0x78,0xff,0xff,0xdc,0xf3,0xff,0xff,0x60,0x40,0x00,0x36,
-0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0x64,0xa0,0xfb,0x0a,0x64,0x40,0x4c,
-0x19,0xff,0x20,0x44,0x01,0x2a,0x04,0x00,0x00,0x64,0x40,0x40,0xa1,0xf3,0x09,0x00,
-0x1a,0xe1,0x00,0x64,0xd0,0xfb,0x31,0x44,0x01,0x26,0x1b,0xe1,0xa1,0xff,0xff,0xff,
-0xb9,0x3f,0x72,0x45,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0x11,0x64,0xa0,0xfb,
-0xa2,0xf3,0x06,0x04,0xdc,0x84,0xa2,0xfb,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,
-0x4b,0xf3,0xff,0xff,0xfe,0xa0,0x65,0xf3,0xe3,0x04,0x60,0x40,0x02,0x2a,0xe0,0x00,
-0x99,0xff,0x3d,0x44,0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbc,0xff,0xff,0xff,0xff,
-0x40,0x5d,0x98,0xff,0x00,0x64,0x4b,0xfb,0xd3,0x00,0x22,0xf1,0x43,0xff,0x64,0x40,
-0x07,0x26,0x03,0x00,0x12,0x60,0x2e,0x78,0xff,0xff,0x6c,0x40,0x03,0xe1,0x00,0x6b,
-0x99,0xff,0x3e,0x44,0x01,0xbc,0x00,0x7f,0x40,0x5e,0xdd,0xf1,0x3d,0x44,0xe7,0xb4,
-0x40,0x5d,0x3e,0x44,0xed,0xb4,0xb0,0x84,0x40,0x5e,0x3d,0x44,0x08,0xbc,0x40,0x5d,
-0x98,0xff,0x05,0x64,0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,0x77,0xb4,0x80,0xbc,
-0x40,0x5e,0x98,0xff,0x3c,0x46,0x1c,0xf0,0x53,0xf3,0x64,0x41,0x08,0xb1,0x60,0x45,
-0x03,0x22,0x00,0x61,0xb5,0x85,0x2b,0x5c,0xd1,0x80,0x1f,0xf1,0x0b,0x03,0x41,0x4b,
-0x38,0x64,0x65,0x40,0x08,0x2a,0x80,0x64,0x60,0x48,0x88,0x6a,0xff,0xff,0xff,0xff,
-0x01,0x16,0xfe,0x00,0x7f,0xf1,0x10,0x64,0x64,0x40,0x0e,0x36,0xb4,0x85,0x65,0x48,
-0x8a,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x4b,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x0a,0x03,0x99,0xff,0x3d,0x44,0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbd,
-0x00,0x64,0x4b,0xfb,0x45,0x5d,0x98,0xff,0x22,0xf1,0x23,0xf3,0x64,0x45,0x00,0xbc,
-0x00,0x64,0x4d,0x03,0x00,0x61,0x23,0xfb,0x5e,0xf1,0xa4,0xf3,0x64,0x40,0x04,0x2a,
-0x06,0x00,0x60,0x45,0x73,0x44,0xd4,0x84,0xe7,0xa0,0x0d,0x0e,0x0c,0x04,0x28,0x60,
-0xda,0x63,0x72,0x45,0x65,0x44,0xd4,0xfb,0x04,0x05,0x65,0x44,0xdc,0x80,0xff,0xff,
-0x05,0x02,0x01,0x64,0x40,0x40,0x11,0x60,0x8e,0x78,0xff,0xff,0xff,0x60,0xf0,0x64,
-0xd4,0x80,0x20,0xa4,0xf8,0x04,0xd4,0x80,0xff,0xff,0xf5,0x07,0x02,0xfe,0xbd,0xd3,
-0xff,0xff,0x44,0x8a,0x02,0x24,0xdd,0x81,0x02,0x24,0xdd,0x81,0xbd,0xd3,0xa1,0xf1,
-0x61,0x45,0xc0,0x84,0x00,0x61,0x02,0x24,0x01,0xb9,0xc4,0x84,0x60,0x55,0x2a,0x52,
-0xa1,0xfb,0x02,0x24,0x01,0xb9,0xbd,0xd3,0xa2,0xf1,0x61,0x45,0xc0,0x84,0x00,0x61,
-0x02,0x24,0x01,0xb9,0xc4,0x84,0xa2,0xfb,0x02,0x24,0x01,0xb9,0xbd,0xd3,0xa3,0xf1,
-0x61,0x45,0xc0,0x84,0xc4,0x84,0xa3,0xfb,0x22,0xf3,0xff,0xff,0x60,0x45,0x65,0x40,
-0x01,0x2a,0x07,0x00,0x3c,0x44,0x40,0x42,0x22,0xf3,0xff,0xff,0xfe,0xb4,0x22,0xfb,
-0x06,0x00,0x11,0x60,0x8e,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x07,0x64,
-0xa0,0xfb,0x22,0x46,0x0f,0xf0,0xff,0xff,0x64,0x40,0x01,0x2a,0x03,0x00,0x16,0x60,
-0x40,0x78,0xff,0xff,0x15,0x60,0x3b,0x78,0xff,0xff,0x27,0x44,0x04,0x2a,0x09,0x00,
-0xfb,0xb4,0x40,0x47,0x3c,0x46,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x27,0x44,0x02,0x2a,0x08,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,0x21,0xfb,
-0xc0,0xfe,0x11,0x60,0x8e,0x78,0xff,0xff,0x02,0x0a,0x00,0x64,0x60,0x50,0x11,0x60,
-0x8e,0x78,0xff,0xff,0x01,0x60,0x2c,0x74,0xcd,0xe2,0x46,0xff,0x47,0xff,0x01,0x64,
-0x57,0xfb,0x83,0xe1,0x00,0x65,0x26,0x44,0x02,0x26,0x09,0x00,0x3e,0x44,0x34,0x81,
-0xff,0xff,0x05,0x03,0x45,0x5e,0x26,0x44,0x02,0xbc,0x40,0x46,0xd1,0xfe,0x0c,0x64,
-0x40,0x4c,0x19,0xff,0xa1,0xff,0x4c,0x4e,0x01,0x25,0x00,0x00,0x01,0x64,0xd0,0xfb,
-0x4b,0x74,0xcd,0xe2,0xf0,0x60,0x00,0x78,0x00,0x61,0x46,0xff,0x47,0xff,0x11,0x60,
-0x8e,0x78,0xff,0xff,0x99,0xff,0x3e,0x44,0xfd,0xb4,0x40,0x5e,0x98,0xff,0xb5,0xff,
-0xbc,0xff,0x46,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,
-0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,0xff,0xff,0xff,0xff,
-0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,
-0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x47,0xff,
-0x26,0x43,0x04,0x2a,0x50,0x00,0xfb,0xb3,0x43,0x46,0x04,0xbb,0x2a,0x44,0x23,0xfa,
-0x20,0x44,0xe8,0x80,0x00,0x64,0x40,0x40,0xa1,0xf3,0x05,0x04,0x72,0x45,0xdc,0x84,
-0xa1,0xfb,0x60,0x55,0x65,0x52,0x24,0xfa,0xa2,0xf3,0x02,0x04,0xdc,0x84,0xa2,0xfb,
-0x27,0xfa,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,0x28,0xfa,0x2a,0x44,0xdc,0x80,
-0xff,0xff,0x01,0x02,0x58,0x80,0xf4,0xb3,0x32,0x40,0x01,0x2a,0x08,0x00,0x04,0xbb,
-0x0f,0xfc,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x2d,0x44,
-0x0c,0x26,0x0d,0x00,0xcf,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x04,0x03,0x26,0x44,
-0x02,0xbc,0x40,0x46,0x36,0x00,0x0f,0xfc,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x27,0x44,
-0x04,0x2a,0x09,0x00,0xfb,0xb4,0x40,0x47,0x2d,0x44,0x58,0x36,0x37,0x00,0x02,0x64,
-0x21,0xfb,0xc0,0xfe,0x26,0x00,0x02,0x2a,0x24,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,
-0x21,0xfb,0xc0,0xfe,0x1e,0x00,0x2a,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,0x58,0x80,
-0x27,0x44,0x80,0x2a,0x13,0x00,0x7f,0xb4,0x40,0x47,0x27,0x44,0x04,0x2a,0x06,0x00,
-0xfb,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x08,0x00,0x27,0x44,0x02,0x2a,
-0x05,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x26,0x44,0x80,0x2a,0x07,0x00,0x20,0xf1,0x70,0x44,0xd0,0x80,0xff,0xff,
-0x02,0x05,0x64,0xe2,0x64,0x50,0x11,0x60,0x8e,0x78,0xff,0xff,0x06,0x64,0xa0,0xfb,
-0x22,0x46,0x29,0xf0,0xf7,0x60,0xff,0x64,0xa0,0x84,0xa2,0xda,0x04,0x64,0x03,0xfa,
-0x00,0xf2,0xff,0xff,0x04,0xfa,0x01,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x04,0x64,0xa0,0xfb,0x46,0xff,0x47,0xff,0x0a,0x64,0x40,0x4c,0x19,0xff,
-0x03,0xe1,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x08,0x3a,0x0f,0x00,0x17,0x60,
-0xea,0x64,0xa0,0xd3,0xff,0xff,0xe8,0x84,0xe0,0x84,0x60,0x45,0x17,0x60,0xee,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xff,0xff,0xa1,0xff,0xff,0xff,
-0xb9,0x3f,0x00,0x6b,0x99,0xff,0x3e,0x44,0x01,0xbc,0x00,0x7f,0x40,0x5e,0xdd,0xf1,
-0x3d,0x44,0xe7,0xb4,0x40,0x5d,0x3e,0x44,0xed,0xb4,0xb0,0x84,0x40,0x5e,0x3d,0x44,
-0x08,0xbc,0x40,0x5d,0x98,0xff,0x05,0x64,0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,
-0x77,0xb4,0x80,0xbc,0x40,0x5e,0x98,0xff,0x22,0xf2,0x08,0x63,0xff,0xff,0x08,0x2a,
-0x00,0x63,0x28,0x44,0x04,0x26,0x0a,0x00,0x25,0x44,0x06,0xfa,0x60,0x46,0x01,0xf2,
-0xff,0xff,0x61,0x5e,0x03,0x2b,0x01,0xfa,0x21,0x46,0x0d,0x00,0x28,0x44,0xb4,0x36,
-0x0a,0x00,0x08,0x63,0x3c,0x46,0x1c,0xf2,0x53,0xf3,0x60,0x40,0xf0,0x27,0x7e,0x00,
-0x5c,0x07,0x08,0x2a,0x00,0x63,0x21,0x46,0x60,0x45,0x1b,0x00,0x20,0xf2,0xff,0xff,
-0x00,0x7f,0xf6,0xa0,0x00,0x65,0x07,0x03,0xec,0xa0,0x01,0x65,0x04,0x03,0xc9,0xa0,
-0x02,0x65,0x01,0x03,0x03,0x65,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x00,0x36,
-0x08,0x00,0x59,0x60,0x68,0x62,0xa2,0xd3,0xff,0xff,0xd4,0x80,0xff,0xff,0x01,0x05,
-0x60,0x45,0x45,0x45,0x65,0x40,0x03,0x22,0x00,0x63,0xb7,0x85,0x2b,0x5c,0xd3,0x80,
-0xff,0xff,0x0b,0x03,0x43,0x4b,0x38,0x64,0x65,0x40,0x08,0x2a,0x80,0x64,0x60,0x48,
-0x88,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x7f,0xf1,0x00,0x64,0x64,0x40,
-0x0e,0x36,0x10,0x64,0xb4,0x85,0x65,0x48,0x8a,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x4b,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x0a,0x03,0x99,0xff,0x3d,0x44,
-0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbd,0x00,0x64,0x4b,0xfb,0x45,0x5d,0x98,0xff,
-0x2a,0x44,0x23,0xfa,0x20,0x44,0xe8,0x80,0x00,0x64,0x40,0x40,0xa1,0xf3,0x05,0x04,
-0x72,0x45,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0x24,0xfa,0xa2,0xf3,0x02,0x04,
-0xdc,0x84,0xa2,0xfb,0x27,0xfa,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,0x28,0xfa,
-0x2a,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,0x58,0x80,0x08,0x29,0x09,0x00,0x22,0xf1,
-0xff,0xff,0x64,0x40,0x07,0x2e,0x04,0x00,0x43,0xff,0x10,0x64,0x21,0xfb,0xc0,0xfe,
-0x2d,0x44,0x08,0x22,0x03,0x00,0x0d,0xb0,0x0c,0x3a,0x0a,0x00,0x26,0x43,0x84,0xbb,
-0xf4,0xb3,0x21,0x46,0x0f,0xfc,0x00,0x64,0x40,0x46,0x01,0x5d,0xdc,0xfe,0x05,0xff,
-0x09,0x64,0xa0,0xfb,0x28,0x44,0xb4,0x3a,0x0b,0x00,0x27,0x44,0x06,0x22,0x05,0x00,
-0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x17,0x60,0x32,0x78,0xff,0xff,
-0xa4,0x36,0x0a,0x00,0x04,0x26,0x0b,0x00,0x27,0x44,0x06,0x22,0x05,0x00,0xf9,0xb4,
-0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x16,0x60,0xeb,0x78,0xff,0xff,0x28,0x44,
-0xd4,0x3a,0x5b,0x00,0x48,0xe2,0x1c,0x42,0x22,0x46,0x1c,0xf2,0xff,0xff,0x07,0xb4,
-0xfc,0xa0,0x03,0x64,0x01,0x02,0x1c,0xfa,0x27,0xf0,0x01,0x60,0x00,0x64,0xc0,0x84,
-0x27,0xfa,0x26,0xf0,0xff,0x60,0x00,0x64,0xa0,0x84,0x26,0xfa,0x59,0x60,0xec,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x53,0xf3,0x00,0x7c,0x60,0x43,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x59,0x60,0xf0,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x18,0x60,0xbc,0x65,0xe3,0x83,
-0xc7,0x82,0xa2,0xd9,0x27,0x44,0xf8,0xb4,0x40,0x44,0x29,0xf0,0xf7,0x60,0xff,0x64,
-0xa0,0x84,0xa2,0xda,0x0b,0xf2,0x03,0xfa,0xff,0xff,0x0c,0xf2,0x04,0xfa,0x34,0xf2,
-0xff,0xff,0xdc,0x84,0x34,0xfa,0x14,0xf2,0x0f,0xb5,0x0f,0xb4,0xcc,0x84,0x94,0x80,
-0x29,0xf0,0x04,0x02,0xfb,0x60,0xff,0x64,0xa0,0x84,0x03,0x00,0x04,0x64,0x60,0x47,
-0xb0,0x84,0x29,0xfa,0x00,0x64,0x15,0xfa,0x3f,0x00,0xc4,0x3a,0x1d,0x00,0x27,0x44,
-0xfd,0xb4,0x40,0x47,0x48,0xe2,0x5a,0x60,0x2e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5a,0x60,
-0xbe,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x20,0x00,0x28,0x44,0x04,0x2a,0x0b,0x00,0x32,0x44,
-0x04,0x2a,0x08,0x00,0x22,0xf3,0xff,0xff,0xff,0xff,0x02,0x2a,0x03,0x00,0x17,0x60,
-0x98,0x78,0xff,0xff,0x04,0x26,0x08,0x00,0x68,0x3a,0x06,0x00,0x32,0x44,0x00,0x2b,
-0x03,0x00,0x15,0x60,0x36,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x0a,0x64,
-0xa0,0xfb,0x11,0x60,0x8e,0x78,0xff,0xff,0x1c,0x42,0x22,0x46,0x53,0xf3,0xff,0xff,
-0x40,0x45,0x29,0xf2,0xff,0xff,0xff,0xff,0x04,0x2b,0x61,0x00,0x16,0xf2,0xff,0xff,
-0x40,0x43,0x21,0xf2,0x25,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,
-0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x0d,0x03,0xdc,0x84,
-0x03,0x65,0xd5,0x80,0x25,0x40,0x03,0x3a,0x07,0x00,0x06,0x07,0x23,0x5c,0x60,0x41,
-0x00,0x64,0x80,0x7f,0x30,0x83,0x61,0x44,0x40,0x44,0x0f,0x64,0x14,0xf0,0x34,0xf2,
-0xa0,0x81,0x0f,0xb4,0xc9,0x85,0xd4,0x80,0x24,0x44,0x0f,0x02,0x1f,0xf2,0x25,0x40,
-0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,
-0xff,0xff,0xae,0x81,0xff,0xff,0x01,0x03,0xdc,0x84,0xc0,0x65,0xc4,0x85,0x59,0x60,
-0x68,0x61,0xa1,0xd1,0x25,0x44,0xd0,0x80,0xff,0xff,0x01,0x04,0x64,0x44,0x2b,0x5c,
-0x08,0x26,0x0a,0x00,0x00,0x36,0x25,0xf1,0x01,0x36,0x26,0xf1,0x02,0x36,0x27,0xf1,
-0x03,0x36,0x28,0xf1,0x65,0x44,0x0a,0x00,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,
-0x02,0x36,0x2b,0xf1,0x03,0x36,0x2c,0xf1,0x65,0x44,0xa0,0xa4,0xc0,0x84,0xb5,0xf1,
-0xc0,0x84,0xc0,0x84,0x2a,0xfa,0x27,0x44,0x40,0xbc,0x40,0x47,0x44,0x00,0x17,0xf2,
-0x1f,0xf2,0x40,0x43,0x25,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,
-0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x0d,0x03,0xdc,0x84,
-0x03,0x65,0xd5,0x80,0x25,0x40,0x03,0x3a,0x07,0x00,0x06,0x07,0x23,0x5c,0x60,0x41,
-0x00,0x64,0x80,0x7f,0x30,0x83,0x61,0x44,0x40,0x44,0x56,0x64,0xa0,0xd2,0x00,0x7c,
-0x60,0x40,0x01,0x26,0x1c,0x00,0x59,0x60,0x68,0x61,0xa1,0xd1,0x25,0x44,0xd0,0x80,
-0xff,0xff,0x01,0x04,0x64,0x44,0x2b,0x5c,0x08,0x26,0x09,0x00,0x00,0x36,0x25,0xf1,
-0x01,0x36,0x26,0xf1,0x02,0x36,0x27,0xf1,0x03,0x36,0x28,0xf1,0x08,0x00,0x00,0x36,
-0x29,0xf1,0x01,0x36,0x2a,0xf1,0x02,0x36,0x2b,0xf1,0x03,0x36,0x2c,0xf1,0x2a,0xf8,
-0x27,0x44,0xbf,0xb4,0x40,0x47,0x22,0x46,0x29,0xf0,0x6b,0x44,0x64,0x40,0x40,0x27,
-0x80,0xbc,0x60,0x4b,0xf3,0x60,0x58,0x4f,0xba,0x78,0xff,0xff,0xbc,0xff,0x22,0x46,
-0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x26,0x0e,0x00,0x27,0x44,0x04,0xbc,0x40,0x47,
-0x35,0xf3,0xb4,0xff,0x60,0x5b,0x4d,0xe2,0x84,0x60,0x1d,0x7d,0x8e,0x60,0x00,0x6b,
-0x13,0x60,0x9b,0x78,0xff,0xff,0xb5,0xff,0xbc,0xff,0x46,0xff,0x47,0xff,0xb7,0xff,
-0xb4,0xff,0x00,0x6b,0x99,0xff,0x3e,0x44,0x7c,0xb4,0x08,0xbc,0x40,0x5e,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,
-0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xb7,0xff,
-0xb4,0xff,0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,0x99,0xff,0x3e,0x44,0x02,0xbc,
-0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0x46,0xff,0x47,0xff,0x13,0x60,0x88,0x78,
-0xff,0xff,0x0e,0x64,0xa0,0xfb,0x00,0x60,0x13,0x66,0x46,0x42,0x10,0x60,0x00,0x7c,
-0x3c,0x46,0x29,0xf2,0x22,0x46,0xa0,0x84,0xb4,0xbc,0x29,0xfa,0x59,0x60,0x68,0x61,
-0xa1,0xd1,0x53,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x64,0x44,0x40,0x45,
-0x3c,0x46,0x2b,0xf2,0x2c,0xf0,0x60,0x43,0x2d,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,
-0x2d,0xfa,0x3c,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,0x30,0xf2,0x22,0x46,0x2e,0xfc,
-0x2f,0xf8,0x30,0xfa,0x3c,0x46,0x29,0xf2,0xff,0xff,0xff,0xff,0x04,0x2b,0x13,0x00,
-0x21,0xf2,0x53,0xf1,0xff,0xff,0x64,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,
-0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x01,0x03,
-0xdc,0x84,0x40,0x44,0x12,0x00,0x1f,0xf2,0x53,0xf1,0xff,0xff,0x64,0x40,0x02,0x36,
-0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,
-0xae,0x81,0xff,0xff,0x01,0x03,0xdc,0x84,0x40,0x44,0x00,0x64,0x40,0x43,0x25,0x44,
-0x00,0x3a,0x0b,0x00,0x2b,0x5c,0x08,0x26,0x04,0x00,0x2d,0xf1,0x25,0xf1,0x64,0x43,
-0x2b,0x00,0x31,0xf1,0x29,0xf1,0x64,0x43,0x24,0x00,0x01,0x3a,0x0b,0x00,0x2b,0x5c,
-0x08,0x26,0x04,0x00,0x2e,0xf1,0x26,0xf1,0x64,0x43,0x1e,0x00,0x32,0xf1,0x2a,0xf1,
-0x64,0x43,0x17,0x00,0x02,0x3a,0x0b,0x00,0x2b,0x5c,0x08,0x26,0x04,0x00,0x2f,0xf1,
-0x27,0xf1,0x64,0x43,0x11,0x00,0x33,0xf1,0x2b,0xf1,0x64,0x43,0x0a,0x00,0x2b,0x5c,
-0x08,0x26,0x04,0x00,0x30,0xf1,0x28,0xf1,0x64,0x43,0x06,0x00,0x34,0xf1,0x2c,0xf1,
-0x64,0x43,0xa0,0xa3,0x60,0x65,0x02,0x00,0xc0,0x65,0xd7,0x83,0xb5,0xf3,0xff,0xff,
-0xc0,0x84,0xc0,0x84,0xc4,0x84,0x24,0x45,0xc4,0x84,0x22,0x46,0x2a,0xfa,0x63,0x44,
-0xb5,0xf1,0xff,0xff,0xd0,0x84,0xff,0xff,0x40,0x44,0xf3,0x60,0x58,0x4f,0xba,0x78,
-0xff,0xff,0xbc,0xff,0x27,0x44,0x02,0xbc,0x40,0x47,0x35,0xf3,0xb4,0xff,0x60,0x5b,
-0x4d,0xe2,0x13,0x60,0x9b,0x78,0xff,0xff,0x0d,0x64,0xa0,0xfb,0x00,0x64,0x40,0x43,
-0x2b,0x44,0x08,0x26,0x0f,0x00,0x25,0x44,0x00,0x36,0x25,0xf1,0x01,0x36,0x26,0xf1,
-0x02,0x36,0x27,0xf1,0xfd,0xa0,0xff,0xff,0x15,0x02,0x28,0xf1,0x80,0x60,0x00,0x64,
-0x40,0x43,0x10,0x00,0x25,0x44,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,0x02,0x36,
-0x2b,0xf1,0xfd,0xa0,0xff,0xff,0x04,0x02,0x2c,0xf1,0x80,0x60,0x00,0x64,0x40,0x43,
-0x60,0x65,0x01,0x00,0xc0,0x65,0x00,0x60,0x13,0x66,0x20,0xf3,0x46,0x42,0xd0,0x83,
-0xff,0xff,0x02,0x28,0x00,0x63,0x2a,0xfc,0x64,0x44,0xb5,0xf1,0xd4,0x84,0xd0,0x84,
-0xff,0xff,0x40,0x44,0xd4,0x64,0x29,0xfa,0x21,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,
-0x30,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,0x2d,0xfa,0xf3,0x60,0x58,0x4f,0xba,0x78,
-0xff,0xff,0xbc,0xff,0x5c,0x00,0x0f,0x64,0xa0,0xfb,0x5a,0x60,0xba,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x00,0x64,0x40,0x43,0x00,0x60,0x13,0x66,0x46,0x42,0xc4,0x64,0x29,0xfa,
-0x21,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,0x30,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,
-0x2d,0xfa,0x2b,0x44,0x08,0x26,0x0f,0x00,0x25,0x44,0x00,0x36,0x25,0xf1,0x01,0x36,
-0x26,0xf1,0x02,0x36,0x27,0xf1,0xfd,0xa0,0xff,0xff,0x15,0x02,0x28,0xf1,0x80,0x60,
-0x00,0x64,0x40,0x43,0x10,0x00,0x25,0x44,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,
-0x02,0x36,0x2b,0xf1,0xfd,0xa0,0xff,0xff,0x04,0x02,0x2c,0xf1,0x80,0x60,0x00,0x64,
-0x40,0x43,0x60,0x65,0x01,0x00,0xc0,0x65,0x20,0xf3,0x22,0x46,0xd0,0x84,0x2a,0xfa,
-0x64,0x44,0xb5,0xf1,0xd4,0x84,0xd0,0x84,0xff,0xff,0x40,0x44,0xf3,0x60,0x58,0x4f,
-0xba,0x78,0xff,0xff,0xbc,0xff,0x5a,0x60,0x32,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xb4,0xff,
-0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,0x8e,0x60,0x00,0x6b,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x10,0x64,0xa0,0xfb,0xbc,0xff,0x00,0x60,0x13,0x66,0x46,0x42,0xf3,0x60,
-0x58,0x4f,0xba,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0xff,0x00,0x00,0xe0,
-0x7f,0x00,0x1a,0x0e,0xd8,0xf1,0x01,0x64,0xd0,0x80,0xcf,0xfb,0x1a,0x03,0xd7,0xfb,
-0x17,0x60,0xe4,0x65,0xa5,0xd3,0x41,0x4d,0xdc,0x84,0xa5,0xdb,0x26,0x44,0x02,0x26,
-0x17,0x00,0x3e,0x45,0x35,0x81,0xff,0xff,0x0f,0x02,0x5b,0x60,0x06,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0xf3,0x60,0x8e,0x78,0xff,0xff,0x41,0x5e,0x02,0x64,0x40,0x46,0xd1,0xfe,
-0x21,0x46,0x46,0x45,0x4c,0xe2,0x0e,0x64,0x40,0x4c,0x19,0xff,0x03,0xe1,0x26,0x44,
-0x02,0xb4,0x12,0xbc,0x40,0x46,0x2e,0x44,0x20,0xfa,0x17,0x60,0x7a,0x62,0xa2,0xd1,
-0xff,0xff,0x64,0x40,0x01,0x2a,0x12,0x00,0x66,0x69,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x44,0x00,0x7f,0x60,0x45,0x7c,0x69,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x44,0x00,0x7f,0x60,0x47,0xb4,0x84,0x0f,0x00,0x00,0x65,0x7c,0x69,
-0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x68,0x44,0xdf,0xf1,0x00,0x7f,0xd0,0x80,
-0xff,0xff,0x01,0x04,0x64,0x44,0x60,0x47,0xb4,0x84,0x25,0xfa,0x20,0xf2,0xff,0xff,
-0xff,0xb4,0x0a,0x36,0x00,0x7f,0x14,0x36,0x01,0x7f,0x37,0x36,0x02,0x7f,0x6e,0x36,
-0x03,0x7f,0x26,0xfa,0x00,0x7c,0x22,0xf8,0x35,0xf1,0x01,0x64,0x57,0xfb,0x2e,0x44,
-0xa1,0xff,0x6c,0x43,0x21,0xfc,0x7e,0x69,0xc3,0x94,0xcd,0xe2,0x9c,0xfe,0xff,0xff,
-0x0e,0x04,0x67,0x60,0x5e,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x01,0x64,
-0xcf,0xfb,0x27,0x44,0x80,0xbc,0x40,0x47,0xf3,0x60,0x94,0x78,0xff,0xff,0xdc,0xf1,
-0xff,0xff,0x64,0x40,0x00,0x36,0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0x7c,
-0xcf,0xf9,0x40,0x45,0x6e,0x36,0x35,0x00,0x37,0x36,0x26,0x00,0x14,0x36,0x1f,0x00,
-0x0a,0x36,0x17,0x00,0x5a,0x60,0xfa,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x21,0x63,0xa0,0xfd,
-0x27,0x44,0x80,0xbc,0x40,0x47,0x07,0x60,0xd0,0x74,0xcd,0xe2,0xf3,0x60,0x94,0x78,
-0xff,0xff,0xa1,0xff,0x4c,0x48,0xeb,0x83,0xeb,0x83,0xeb,0x83,0x20,0x00,0xa1,0xff,
-0x4c,0x48,0xeb,0x83,0xeb,0x83,0x1b,0x00,0xa1,0xff,0x4c,0x48,0xe3,0x85,0xc7,0x85,
-0xe3,0x83,0xe3,0x83,0xe3,0x83,0xc7,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,
-0x0e,0x00,0xe3,0x85,0xc7,0x85,0xe3,0x83,0xe3,0x83,0xe3,0x83,0xa1,0xff,0x4c,0x48,
-0xc7,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,0xff,0xff,0x80,0x27,0xcf,0x83,0x1f,0xfc,
-0xfc,0xa3,0x48,0xf3,0x43,0x43,0xa1,0xff,0x4c,0x4e,0x1c,0x7c,0xd0,0x9c,0xd3,0x80,
-0x20,0x44,0x0f,0x04,0x5a,0x60,0xfe,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x27,0x44,0x80,0xbc,0x40,0x47,0x07,0x60,0xd0,0x74,0xcd,0xe2,0xf3,0x60,0x94,0x78,
-0xff,0xff,0xe8,0x84,0x52,0x4a,0x01,0x05,0x70,0x80,0x52,0x63,0x28,0x44,0xbd,0xda,
-0xff,0xff,0xa1,0xff,0x6c,0x45,0x2e,0x44,0x80,0x2b,0x20,0xfb,0xbd,0xda,0x65,0x44,
-0xbd,0xf1,0xbd,0xda,0x50,0xfe,0xff,0xff,0x01,0x2a,0x04,0x00,0x26,0x44,0x20,0xbc,
-0x40,0x46,0x01,0x00,0xd0,0x80,0xa1,0xff,0x6c,0x44,0xbe,0xf1,0xbd,0xda,0xc4,0x85,
-0xd0,0x80,0x00,0x61,0x28,0x44,0x04,0x2a,0x03,0x00,0x40,0x27,0xd1,0x00,0x01,0x61,
-0xbf,0xf1,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xc4,0x85,0xd0,0x80,0x32,0x44,
-0x01,0x2a,0x27,0x00,0x28,0x44,0xd4,0x36,0x02,0x00,0xc4,0x3a,0x06,0x00,0x00,0x64,
-0x38,0xfa,0x08,0x64,0xf2,0x60,0xbb,0x78,0x40,0x4c,0xa1,0xff,0x6c,0x44,0xbd,0xda,
-0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x20,0x61,0x28,0x44,0x03,0x2b,0x00,0x61,
-0x60,0x40,0x40,0x27,0x02,0xb9,0x41,0x4e,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x28,0x44,
-0xb4,0x36,0xe5,0x00,0xa4,0x36,0xe3,0x00,0xe4,0x36,0xe1,0x00,0xf1,0x60,0xb9,0x78,
-0xff,0xff,0x61,0x40,0x01,0x22,0x31,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x28,0x44,
-0xd4,0x3a,0x02,0x00,0x48,0x61,0x08,0x00,0xc4,0x36,0x05,0x00,0x28,0x44,0xb4,0x3a,
-0x03,0x00,0x4d,0x61,0x01,0x00,0x49,0x61,0x41,0x4d,0x46,0x4e,0x08,0x64,0x40,0x4c,
-0x05,0x01,0x00,0x65,0x2d,0x44,0x04,0x2a,0x0a,0x00,0x0d,0x00,0x2d,0x44,0x49,0x36,
-0x05,0x00,0x48,0x3a,0x07,0x00,0x10,0x65,0x27,0x40,0x40,0x26,0x30,0x65,0xf2,0x60,
-0xbe,0x78,0x35,0x8d,0x30,0x65,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0xf2,0x60,0xbb,0x78,0x35,0x8d,0x45,0x4e,0x23,0x44,0xe8,0xa5,
-0xff,0xff,0x05,0x05,0x60,0x43,0xfa,0xa3,0xf3,0x60,0xa8,0x78,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0x26,0x41,0x20,0x26,0x1d,0x00,0x2d,0x44,0x22,0x01,0x32,0x40,
-0x02,0x2a,0x03,0x00,0x50,0xbc,0x40,0x4d,0x1e,0x00,0x01,0x64,0xcf,0xfb,0x18,0x60,
-0xc8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x27,0x44,0x06,0x22,0x06,0x00,
-0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x48,0xe2,0x27,0x44,0x80,0xbc,
-0x40,0x47,0x09,0x00,0x2e,0x44,0x03,0xa4,0xef,0xb1,0x08,0x24,0x40,0xb9,0x41,0x46,
-0x02,0x00,0x70,0xbc,0x40,0x4d,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x20,0x61,0x28,0x44,
-0x03,0x2b,0x00,0x61,0x60,0x40,0x40,0x2b,0x0d,0x00,0xbf,0x60,0xff,0x65,0x43,0xf3,
-0x02,0xb9,0x80,0xb0,0x28,0x44,0x06,0x03,0xa4,0x84,0x40,0x48,0x29,0xfa,0x04,0x64,
-0x22,0xfa,0x02,0xa9,0x23,0x44,0xe8,0xa4,0x41,0x4e,0x20,0x26,0xfa,0xa4,0x28,0x40,
-0x40,0x27,0xf8,0xa4,0x40,0x43,0x38,0xfa,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,
-0x23,0x44,0x80,0x2b,0x03,0x00,0x14,0x64,0x40,0x43,0x38,0xfa,0x23,0x47,0x3f,0xfa,
-0x08,0x65,0x45,0x4c,0x21,0xf2,0x70,0x45,0xd4,0x80,0xff,0xff,0x02,0x06,0x64,0xe2,
-0x60,0x50,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x2e,0x44,0x22,0x26,0x05,0x00,0x23,0x41,
-0xa1,0xff,0x6c,0x44,0x34,0xfa,0x54,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x2e,0x40,
-0x20,0x2a,0x12,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,0x6c,0x44,
-0xbd,0xda,0x2e,0x40,0x02,0x26,0x05,0x00,0x23,0x41,0xa1,0xff,0x6c,0x44,0x37,0xfa,
-0x3f,0x00,0xa1,0xff,0x6c,0x44,0x37,0xfa,0x32,0x44,0x01,0x2a,0x1a,0x00,0x00,0xf4,
-0x02,0x62,0x46,0x45,0xa1,0xff,0xda,0x82,0x6c,0x44,0xa2,0xda,0xff,0xff,0xa1,0xff,
-0xda,0x82,0x6c,0x44,0xa2,0xda,0x23,0x41,0x04,0xa1,0x78,0x7c,0x01,0x65,0x61,0x43,
-0xd1,0x80,0x46,0x45,0x02,0x07,0x04,0xa1,0x37,0x00,0x64,0x43,0xd1,0x81,0x7c,0x7c,
-0x3b,0x00,0xcf,0xf3,0x48,0xf1,0x00,0xa0,0x23,0x44,0x04,0x02,0x00,0xa0,0xd0,0x80,
-0x01,0x03,0x05,0x04,0x60,0x43,0x0c,0xa3,0xf3,0x60,0xa8,0x78,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xff,0xff,0x1a,0xfa,0xff,0xff,0xa1,0xff,0x6c,0x44,0xff,0xff,0x1b,0xfa,
-0xb0,0xff,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x3e,0xf3,0x23,0x41,0x04,0xbc,0x3e,0xfb,
-0x7c,0x7c,0x01,0x65,0x25,0x44,0xd5,0x80,0x61,0x43,0x45,0x04,0x35,0x03,0xdc,0xf3,
-0xff,0xff,0xff,0xff,0x00,0x36,0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0xf4,
-0x02,0x62,0xd1,0x80,0x46,0x45,0x06,0x07,0x61,0x40,0x01,0x26,0x01,0xa1,0x61,0x5c,
-0x00,0x61,0x02,0x00,0x64,0x43,0xd1,0x81,0xa1,0xff,0xec,0x44,0x5a,0xda,0xfc,0x1d,
-0xe2,0x1e,0x23,0x00,0xa1,0xff,0xd5,0x80,0x61,0x43,0x37,0x04,0x16,0x03,0x00,0xf4,
-0x02,0x62,0xd1,0x80,0x46,0x45,0x07,0x07,0xec,0x44,0x61,0x40,0x01,0x26,0x01,0xa1,
-0x61,0x5c,0x00,0x61,0x05,0x00,0x64,0x43,0xec,0x44,0xd1,0x81,0x01,0x00,0xec,0x44,
-0x7a,0xda,0xfd,0x1d,0xe8,0x1e,0x0a,0x00,0xa1,0xff,0xb6,0xff,0x6c,0x44,0x00,0xf4,
-0x02,0xfa,0x02,0x7c,0x46,0x45,0xb7,0xff,0x06,0x00,0xa1,0xff,0xb6,0xff,0x00,0x64,
-0x6c,0x44,0x5a,0xda,0xb7,0xff,0x64,0x41,0x28,0x44,0x40,0x2b,0x1d,0x00,0xb1,0xff,
-0x32,0x44,0x01,0x26,0x19,0x00,0xa1,0xff,0x6c,0x44,0xff,0xff,0x3c,0xfb,0xff,0xff,
-0xa1,0xff,0x6c,0x44,0xff,0xff,0x3d,0xfb,0x0f,0x00,0x6c,0x44,0x64,0x41,0x28,0x40,
-0x40,0x2b,0x0d,0x00,0xb1,0xff,0x32,0x40,0x01,0x26,0x09,0x00,0x3c,0xfb,0xff,0xff,
-0xa1,0xff,0x6c,0x44,0xff,0xff,0x3d,0xfb,0x21,0x46,0xa1,0xff,0x6c,0x40,0x21,0x46,
-0x6a,0x43,0xa1,0xff,0x6c,0x40,0x24,0xf1,0xff,0xff,0x64,0x54,0xcd,0xe2,0x19,0xff,
-0x01,0x16,0xfe,0x00,0x68,0x44,0x60,0x40,0x10,0x2a,0x04,0x00,0x22,0xf2,0xff,0xff,
-0x08,0xbc,0x22,0xfa,0x32,0x44,0x03,0x22,0x20,0x00,0x47,0xff,0x26,0x44,0xfd,0xb4,
-0x84,0xbc,0x63,0x40,0x40,0x27,0x08,0x00,0x0d,0x63,0x07,0x15,0x06,0x15,0x05,0x15,
-0x04,0x15,0xff,0xa3,0x02,0x15,0xf9,0x02,0x7f,0xb4,0x40,0x46,0x6c,0x40,0x28,0x44,
-0x40,0x2b,0x08,0x00,0x32,0x44,0x01,0x2a,0x05,0x00,0x23,0x44,0x08,0xa4,0x38,0xfa,
-0x60,0x47,0x3f,0xfa,0xf3,0x60,0x74,0x78,0xff,0xff,0x63,0x40,0x40,0x2b,0x05,0x00,
-0xd2,0xf3,0xff,0xff,0x01,0xa4,0xd2,0xfb,0x08,0x00,0x0d,0x64,0x4b,0x15,0x4a,0x15,
-0x49,0x15,0x48,0x15,0xff,0xa4,0x46,0x15,0xf9,0x02,0x48,0xe2,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x73,0x02,0x27,0x44,0x06,0x22,0x05,0x00,0xf9,0xb4,0x40,0x47,
-0x02,0x64,0x21,0xfb,0xc0,0xfe,0x20,0xf2,0xff,0xff,0xff,0xb4,0x0a,0x36,0x00,0x7f,
-0x14,0x36,0x01,0x7f,0x37,0x36,0x02,0x7f,0x6e,0x36,0x03,0x7f,0x26,0xfa,0x5a,0x60,
-0xd2,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,
-0xe0,0x84,0x60,0x45,0x5a,0x60,0xd6,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x28,0x40,
-0x04,0x26,0x3c,0x00,0x28,0x40,0x40,0x2b,0x39,0x00,0x26,0x44,0x04,0xbc,0xfd,0xb4,
-0x40,0x46,0x34,0x00,0x6c,0x40,0x2d,0x44,0x20,0x2a,0x18,0x00,0xb5,0xf3,0xff,0xff,
-0xf7,0xa4,0x60,0x54,0xcd,0xe2,0x5d,0x2a,0x05,0x00,0x70,0x44,0x00,0xbc,0xff,0xff,
-0x0d,0x02,0x05,0x00,0x59,0x2a,0x03,0x00,0x27,0x40,0x02,0x2a,0x07,0x00,0x47,0xff,
-0xb5,0xff,0x00,0x64,0xd7,0xfb,0x13,0x60,0xbb,0x78,0xff,0xff,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0x26,0x44,0x03,0x03,0x84,0xbc,0x40,0x46,0x10,0x00,0x84,0xbc,0x2d,0x40,
-0x0c,0x22,0x02,0x00,0x40,0x46,0x0a,0x00,0xfd,0xb4,0x40,0x46,0x25,0x44,0x06,0xfa,
-0x60,0x46,0x01,0xf2,0xff,0xff,0x61,0x5e,0x03,0x2b,0x01,0xfa,0x21,0x46,0x08,0x29,
-0x09,0x00,0x22,0xf1,0xff,0xff,0x64,0x40,0x07,0x2e,0x04,0x00,0x43,0xff,0x10,0x64,
-0x21,0xfb,0xc0,0xfe,0x00,0x64,0xd7,0xfb,0x47,0xff,0x12,0x60,0xe4,0x78,0xff,0xff,
-0xa1,0xff,0x6c,0x43,0x63,0x54,0xcd,0xe2,0x0e,0x64,0x01,0x00,0x0c,0x64,0x40,0x4c,
-0x19,0xff,0x00,0x64,0x40,0x4a,0xd7,0xfb,0x08,0x64,0x40,0x4c,0x27,0x44,0x06,0x22,
-0x06,0x00,0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x48,0xe2,0x82,0xe1,
-0xa1,0xff,0xd4,0x00,0x00,0x60,0x18,0x63,0xa1,0xff,0xec,0x44,0xff,0xff,0xfc,0x1d,
-0x04,0x1e,0xb6,0xff,0xa1,0xff,0x6c,0x44,0xb7,0xff,0x08,0x64,0x40,0x4c,0x19,0xff,
-0x27,0x44,0x80,0xbc,0x40,0x47,0xc2,0x00,0x01,0x64,0xd0,0xfb,0x24,0x44,0x60,0x48,
-0x90,0x6a,0x60,0x47,0x23,0x41,0xe1,0x81,0xff,0xff,0x01,0x16,0xfe,0x00,0x60,0x48,
-0x8e,0x6a,0x00,0x64,0x02,0x24,0x80,0x64,0x69,0x83,0xde,0xf1,0x25,0x45,0x02,0x2a,
-0x03,0x00,0x64,0x40,0x01,0x2a,0x04,0xbc,0x01,0x16,0xfe,0x00,0x60,0x48,0x8c,0x6a,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x02,0xe1,0x08,0x64,
-0x40,0x4c,0x29,0x44,0x01,0xbc,0x40,0x49,0x19,0xff,0x52,0x63,0x22,0x46,0xbd,0xd0,
-0x22,0xf3,0x43,0xff,0x60,0x40,0x07,0x22,0x03,0x00,0x10,0x64,0x21,0xfb,0xc0,0xfe,
-0x01,0xe1,0x28,0xf2,0x44,0x48,0x60,0x40,0x01,0x2a,0x03,0x00,0x40,0x67,0xb0,0x84,
-0x60,0x5c,0x99,0xff,0x07,0x60,0x00,0xe8,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x64,0x4d,
-0x64,0x47,0x60,0x4d,0xff,0xff,0xff,0xff,0xa1,0xff,0xb5,0xff,0xbb,0xff,0x32,0x64,
-0xa0,0xfb,0xff,0xff,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4d,0x60,0x47,0x60,0x4d,
-0x26,0x44,0x02,0xb4,0x40,0x46,0x20,0x61,0x28,0x44,0x80,0x36,0x04,0x00,0x50,0x3a,
-0x03,0x00,0x00,0x65,0xb5,0x81,0x04,0xb9,0x41,0x4e,0xa1,0xff,0xbd,0xd2,0xff,0xff,
-0x60,0x4d,0x60,0x47,0x60,0x4d,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4d,0x60,0x47,
-0x60,0x4d,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x28,0x44,0xc4,0x36,0x13,0x00,
-0xd4,0x36,0x11,0x00,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x28,0x44,0xb4,0x36,
-0x02,0x00,0xa4,0x3a,0x03,0x00,0xf5,0x60,0x03,0x78,0xff,0xff,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0xff,0x65,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x2e,0x41,
-0x28,0x44,0x03,0x2b,0xdf,0xb1,0x60,0x40,0x40,0x27,0x02,0xb9,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0x23,0x44,0xcc,0x84,0xdc,0x84,0x03,0x03,0x03,0x02,0x08,0xb9,
-0x01,0x00,0x10,0xb9,0xbd,0xd0,0x41,0x4e,0xa1,0xff,0xff,0xff,0x64,0x4c,0x2e,0x44,
-0x22,0x22,0x30,0x00,0x20,0x2a,0x0f,0x00,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,
-0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,
-0x2e,0x44,0x02,0x2a,0x1f,0x00,0xa1,0xff,0xff,0xff,0x38,0xf3,0x39,0xf3,0x60,0x4c,
-0x60,0x43,0x03,0xf0,0x04,0xf4,0x01,0xf2,0x64,0x42,0x04,0xa4,0xd0,0x81,0xa1,0xff,
-0x63,0x4c,0x23,0x43,0x2e,0x44,0x10,0x2a,0x06,0x00,0xa2,0xd2,0xff,0xff,0xa1,0xff,
-0xff,0xff,0x60,0x4f,0x63,0x00,0xe2,0xd2,0xff,0xff,0xa1,0xff,0xda,0x82,0xc9,0x81,
-0x60,0x4e,0x51,0x00,0x18,0x22,0x0a,0x00,0x10,0x26,0x03,0x00,0xf5,0x60,0x03,0x78,
-0xff,0xff,0x03,0xf0,0x04,0xf4,0x00,0x61,0x64,0x42,0x4b,0x00,0x04,0x2a,0x32,0x00,
-0x00,0xf4,0x01,0xf2,0xff,0x65,0xa4,0x81,0xa1,0xff,0x02,0xfe,0xff,0xff,0x10,0x25,
-0x42,0xfe,0x72,0x45,0x28,0x40,0x50,0x3a,0x00,0x00,0x65,0x4c,0x23,0x43,0xa1,0xf3,
-0x04,0x04,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0xa1,0xff,0xff,0xff,0x60,0x4c,
-0xa2,0xf3,0x03,0x04,0xdc,0x84,0xa2,0xfb,0xff,0xff,0xa1,0xff,0xff,0xff,0x60,0x4c,
-0xa3,0xf3,0x03,0x04,0xdc,0x84,0xa3,0xfb,0xff,0xff,0xa1,0xff,0x0c,0x62,0x60,0x4c,
-0xf8,0xa3,0x2e,0x44,0xfb,0xb4,0x40,0x4e,0x65,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,
-0x58,0x80,0x0c,0x00,0x23,0x43,0x03,0xf0,0x04,0xf4,0x01,0xf2,0x64,0x42,0x04,0xa4,
-0xd0,0x81,0x04,0x00,0x00,0xf4,0x01,0xf2,0x04,0x62,0xa4,0x81,0xa1,0xff,0xe2,0xd2,
-0xda,0x82,0xc9,0x81,0x60,0x4c,0xfa,0x1c,0xf5,0x1d,0x08,0x1e,0x02,0x02,0x00,0xf4,
-0x04,0x62,0xa2,0xd2,0xff,0xff,0xa1,0xff,0xff,0xff,0x60,0x4d,0x28,0x44,0x40,0x2b,
-0x04,0x00,0xa1,0xff,0x60,0x4e,0xa1,0xff,0x60,0x4c,0xa1,0xff,0xc3,0x60,0x33,0x64,
-0x60,0x4e,0xa1,0xff,0xff,0xff,0x62,0x5c,0x02,0xe1,0x08,0x64,0x40,0x4c,0x19,0xff,
-0x61,0x40,0x7f,0x26,0x02,0x00,0x00,0xf4,0x04,0x7c,0x66,0x44,0x22,0x46,0x0b,0xf8,
-0x0c,0xfa,0x34,0x64,0xa0,0xfb,0x6a,0x40,0x40,0x26,0x02,0x00,0xfc,0x0b,0x02,0x00,
-0x07,0x60,0x80,0xe8,0x24,0xf3,0xff,0xff,0x60,0x54,0xcd,0xe2,0x03,0x64,0xcc,0x84,
-0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x99,0xff,0x3e,0x44,0x7d,0xb4,0x08,0xbc,0x40,0x5e,0x98,0xff,0x05,0x64,
-0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x99,0xff,0x3d,0x44,0xf7,0xb4,0x40,0x5d,0x98,0xff,0x00,0x60,
-0x00,0x6b,0x99,0xff,0x3d,0x44,0x10,0xbc,0x40,0x5d,0x3e,0x44,0xfe,0xb4,0x40,0x5e,
-0x98,0xff,0xbc,0xff,0x28,0x44,0x40,0x2b,0x3e,0x00,0x99,0xff,0x3a,0x44,0x98,0xff,
-0x10,0x2b,0xfb,0x00,0x99,0xff,0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x06,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x00,0x64,
-0xdb,0xfb,0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0xff,0xff,0x30,0x60,0x00,0xea,
-0xff,0xff,0xff,0xff,0x98,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0xff,0xff,
-0x84,0x60,0x1d,0x7d,0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,
-0xff,0xff,0xff,0xff,0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,
-0x99,0xff,0x3e,0x44,0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x46,0xff,0x47,0xff,0x2f,0x58,0xff,0xff,0x01,0x64,0x3f,0xfb,
-0xff,0xff,0x00,0xea,0x99,0xff,0x3b,0x44,0x40,0x27,0xfd,0x00,0x98,0xff,0x00,0xeb,
-0x40,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,0x03,0x00,0x18,0x60,0x17,0x78,0xff,0xff,
-0x1b,0xf2,0x41,0xf1,0x60,0x40,0xc0,0x27,0x07,0x00,0x64,0x40,0x01,0x2a,0x16,0x00,
-0x67,0x60,0x8e,0x63,0x00,0x65,0x77,0x00,0xc0,0x2b,0x07,0x00,0x64,0x40,0x08,0x2a,
-0x0d,0x00,0x67,0x60,0xb8,0x63,0x30,0x65,0x6e,0x00,0x40,0x2b,0x0c,0x00,0x64,0x40,
-0x02,0x2a,0x04,0x00,0x67,0x60,0x9c,0x63,0x10,0x65,0x65,0x00,0x20,0x60,0x00,0xea,
-0xf6,0x60,0xeb,0x78,0xff,0xff,0x64,0x40,0x04,0x2a,0xf8,0x00,0x67,0x60,0xaa,0x63,
-0x95,0xf3,0xd6,0xf3,0x00,0xa0,0x00,0xa0,0x55,0x03,0x54,0x03,0x19,0x60,0x2a,0x65,
-0x30,0xf2,0x2f,0xf0,0x60,0x41,0x64,0x43,0xeb,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,
-0x61,0x47,0x93,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,0xeb,0x83,0x93,0x83,0x0f,0x60,
-0xf0,0x65,0xa7,0x85,0x44,0x60,0x4e,0x63,0xc7,0x83,0x02,0x65,0xbd,0xd3,0xff,0xff,
-0x60,0x40,0x80,0x2b,0x0e,0x00,0x5c,0x61,0xa1,0xd0,0xbd,0xd3,0x50,0xfe,0x59,0xd0,
-0xd0,0x80,0xbd,0xd3,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x1f,0x01,0x65,0x44,0xff,0xa5,0xff,0xff,0xe9,0x02,0x54,0x60,0x4e,0x63,0x7e,0x65,
-0xbd,0xd3,0xff,0xff,0x60,0x40,0x80,0x2b,0x0e,0x00,0x5c,0x61,0xa1,0xd0,0xbd,0xd3,
-0x50,0xfe,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x05,0x01,0x65,0x44,0xff,0xa5,0xff,0xff,0xe9,0x02,0x0a,0x00,
-0xf8,0xa3,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xe8,0x85,0x3d,0x60,0x4c,0x64,0xc4,0x83,
-0x67,0x44,0xd9,0xfb,0x20,0x65,0x22,0xf2,0xff,0xff,0xb4,0x84,0x22,0xfa,0x1a,0xf0,
-0x99,0xff,0x64,0x44,0xe0,0x7f,0x40,0x5b,0x64,0x47,0xe1,0x7f,0x40,0x5b,0x1b,0xf0,
-0xff,0xff,0x64,0x44,0xe2,0x7f,0x40,0x5b,0x60,0x47,0x60,0x41,0xd9,0xf3,0xd9,0xf9,
-0x45,0xf1,0x90,0x84,0x00,0x37,0x13,0x00,0x63,0x42,0x02,0x63,0x64,0x40,0x07,0x3a,
-0x0a,0x63,0x5a,0xd3,0xdd,0x81,0x60,0x45,0x61,0x5f,0x40,0x5b,0x65,0x47,0xdd,0x81,
-0x61,0x5f,0x40,0x5b,0xf6,0x1f,0x5a,0xd3,0xdd,0x81,0x61,0x5f,0x40,0x5b,0x98,0xff,
-0xa0,0x60,0x00,0xeb,0xc0,0x60,0x00,0xeb,0x64,0x40,0x07,0x3a,0x03,0x00,0x80,0x60,
-0x07,0xeb,0x02,0x00,0x80,0x60,0x0f,0xeb,0x33,0x60,0x00,0xeb,0x00,0x64,0x3f,0xfb,
-0x38,0xf2,0x00,0xf4,0xff,0xff,0x28,0x87,0x04,0x64,0x40,0x49,0x46,0x4a,0xb3,0xff,
-0x02,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0x02,0xbc,0x40,0x51,0x98,0xff,0xf8,0x60,
-0x89,0x78,0xff,0xff,0x40,0xf3,0x2a,0x46,0x60,0x40,0x01,0x26,0x65,0x00,0x29,0x43,
-0x27,0x44,0x00,0xa8,0x60,0x41,0xa3,0xd2,0x1b,0x03,0x99,0xff,0x3b,0x40,0x80,0x2b,
-0xfd,0x00,0x98,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0xc9,0x81,0x60,0x57,0xff,0xff,
-0x77,0x5f,0xbd,0xda,0x01,0x0f,0x25,0x00,0x0b,0x03,0xa3,0xd2,0x7f,0x26,0xf2,0x00,
-0x00,0xf2,0x04,0x63,0x00,0xa8,0x40,0x4a,0x60,0x46,0x3b,0x03,0xa3,0xd2,0xea,0x00,
-0x3d,0x46,0x38,0xf2,0x2a,0x46,0x60,0x40,0x01,0x2a,0x1f,0x00,0x63,0x40,0x7f,0x26,
-0x05,0x00,0x00,0xf2,0x04,0x63,0x00,0xa8,0x60,0x46,0x2b,0x03,0x46,0x4a,0x01,0x0f,
-0x11,0x00,0xa3,0xd2,0xff,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0x60,0x47,0xa3,0xda,
-0x0c,0x00,0xe6,0x03,0x63,0x40,0x7f,0x26,0x05,0x00,0x00,0xf2,0x04,0x63,0x00,0xa8,
-0x40,0x4a,0x17,0x03,0x43,0x49,0x41,0x47,0x11,0x00,0x00,0x64,0x9c,0xfb,0x99,0xff,
-0x30,0x44,0xfd,0xb4,0x40,0x51,0x98,0xff,0x3e,0xf3,0x00,0x7c,0x02,0xbc,0x3e,0xfb,
-0x3d,0x46,0x0f,0xf2,0x44,0x47,0x60,0x40,0x04,0x26,0x0b,0x00,0xf8,0x60,0x89,0x78,
-0xff,0xff,0x3d,0x46,0x0f,0xf0,0xff,0x60,0xf7,0x65,0x64,0x43,0x17,0x60,0xe5,0x78,
-0xff,0xff,0x17,0x60,0xa6,0x78,0xff,0xff,0x18,0x60,0x17,0x78,0xff,0xff,0x4c,0x2f,
-0x7e,0x00,0x18,0x09,0x3d,0x46,0x0f,0xf0,0x3c,0xf3,0x64,0x43,0x60,0x45,0x1b,0xf2,
-0x41,0xf1,0x60,0x40,0xc0,0x23,0x01,0x64,0xc0,0x2b,0x01,0x00,0x08,0x64,0x80,0x2b,
-0x01,0x00,0x04,0x64,0x40,0x2b,0x01,0x00,0x02,0x64,0xa0,0x84,0x0f,0x22,0x2d,0x00,
-0x65,0x44,0x63,0x5c,0x80,0x2a,0x29,0x00,0x99,0xff,0x3b,0x40,0x80,0x2b,0xfd,0x00,
-0x98,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0x60,0x57,0xff,0xff,0x77,0x5f,0x99,0xff,
-0x3b,0x40,0x80,0x2b,0xfd,0x00,0x98,0xff,0x3d,0xf3,0xff,0xff,0x60,0x57,0xff,0xff,
-0x77,0x5f,0x60,0x57,0xff,0xff,0x77,0x5f,0xff,0xff,0xff,0x60,0xf7,0x65,0x0b,0x14,
-0x0a,0x14,0x09,0x14,0x08,0x14,0x07,0x14,0x06,0x14,0x05,0x14,0x04,0x14,0x03,0x14,
-0x02,0x14,0xa7,0x83,0x01,0x00,0x08,0xbb,0x0f,0xfc,0x0f,0xf0,0x80,0x60,0x00,0x63,
-0xb3,0x9c,0x0f,0xf8,0x00,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0xfd,0xb4,0x40,0x51,
-0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x00,0xea,0x00,0xeb,
-0xa0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,
-0xf8,0x60,0xd7,0x78,0xff,0xff,0x00,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0xfd,0xb4,
-0x40,0x51,0x98,0xff,0x00,0xeb,0xa0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,0x20,0x60,
-0x00,0xea,0x00,0x64,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,0x40,0xfb,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3a,0x44,0x3b,0x44,0x98,0xff,0x3d,0x46,
-0x0f,0xf0,0x80,0x60,0x00,0x63,0xb3,0x83,0xff,0x60,0x7f,0x7c,0xa3,0x83,0x0f,0xfc,
-0xf8,0x60,0xd7,0x78,0xff,0xff,0xa9,0xff,0x77,0x44,0x60,0x57,0x40,0x4a,0x01,0x2a,
-0x31,0x00,0x24,0x44,0x00,0xa8,0x24,0x46,0x09,0xf2,0x2c,0x03,0x00,0xa8,0x40,0x44,
-0x13,0x03,0x60,0x46,0x60,0x5c,0x08,0x60,0x20,0x64,0xa0,0xd9,0x64,0x44,0x3a,0x44,
-0x01,0x26,0x02,0x00,0x01,0x75,0x03,0x00,0x3b,0x44,0x01,0xbc,0x40,0x5b,0x0e,0xf2,
-0xff,0xff,0x01,0xbc,0x0e,0xfa,0x0a,0xf4,0x08,0xf2,0x2d,0x45,0xd4,0x80,0x0e,0xf2,
-0x02,0x03,0xd2,0xfe,0x0f,0x00,0x02,0xbc,0x0e,0xfa,0xd0,0xfe,0x5a,0x60,0x70,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x2a,0x44,0x08,0x2a,0x1c,0x00,0x23,0x44,0x00,0xa8,0xff,0xff,
-0x18,0x03,0x3c,0x60,0x40,0x64,0x40,0x47,0x58,0x4f,0x53,0x00,0x46,0x43,0x11,0x02,
-0x0e,0xf2,0x66,0x43,0x60,0x5c,0x08,0x60,0x22,0x64,0xa0,0xdd,0x64,0x44,0x01,0xbc,
-0x0e,0xfa,0x3a,0x44,0x01,0x26,0x02,0x00,0x08,0x75,0x03,0x00,0x3b,0x44,0x08,0xbc,
-0x40,0x5b,0x2a,0x44,0x06,0x22,0x4b,0x00,0x22,0x44,0x00,0xa8,0x60,0x46,0x0e,0xf2,
-0x46,0x03,0x01,0xb0,0x02,0xbc,0x03,0x02,0x00,0x64,0x40,0x42,0x40,0x00,0x0e,0xfa,
-0xd0,0xfe,0x3c,0x60,0x46,0x64,0x40,0x47,0x58,0x4f,0x2b,0x00,0x46,0x42,0x37,0x02,
-0x22,0xf2,0x66,0x43,0x00,0xa8,0x0e,0xf2,0x12,0x02,0x01,0xbc,0x40,0x2a,0xe9,0x00,
-0xf7,0xb4,0x0e,0xfa,0xff,0xff,0x08,0x60,0x24,0x64,0xa0,0xdd,0x3a,0x44,0x01,0x26,
-0x02,0x00,0x02,0x75,0x24,0x00,0x3b,0x44,0x02,0xbc,0x40,0x5b,0x20,0x00,0x01,0xbc,
-0x80,0x2a,0xd7,0x00,0xf7,0xb4,0x0e,0xfa,0xff,0xff,0x08,0x60,0x24,0x64,0xa0,0xdd,
-0x3a,0x44,0x01,0x26,0x02,0x00,0x04,0x75,0x12,0x00,0x3b,0x44,0x04,0xbc,0x40,0x5b,
-0x0e,0x00,0x27,0x42,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0e,0xf2,0x04,0x03,
-0x05,0xb0,0x09,0xf2,0xf9,0x02,0x01,0x00,0x08,0xfe,0x2f,0x58,0xff,0xff,0x2a,0x44,
-0x80,0x2a,0x1f,0x00,0x21,0x44,0x00,0xa8,0x60,0x46,0x0e,0xf2,0x1a,0x03,0x02,0xbc,
-0x0e,0xfa,0xd0,0xfe,0x3c,0x60,0x70,0x64,0x40,0x47,0x58,0x4f,0xe2,0x00,0x46,0x41,
-0x10,0x02,0x0e,0xf2,0x66,0x43,0x01,0xbc,0x0e,0xfa,0xff,0xff,0x08,0x60,0x10,0x64,
-0xa0,0xdd,0x3a,0x44,0x01,0x26,0x02,0x00,0x80,0x75,0x03,0x00,0x3b,0x44,0x80,0xbc,
-0x40,0x5b,0x34,0x40,0xff,0x26,0x3b,0xff,0x2a,0x44,0x0c,0xf7,0xff,0xff,0xff,0xff,
-0x3a,0x44,0x02,0x2a,0x09,0x00,0x3b,0x44,0x00,0xa0,0xff,0xff,0x01,0x03,0x60,0x55,
-0x00,0x64,0x40,0x5a,0x40,0x5b,0x3d,0x00,0x00,0x64,0x40,0x4a,0x35,0x40,0x01,0x2a,
-0x19,0x00,0x24,0x41,0x00,0xb9,0x40,0x55,0x2c,0x02,0x0d,0x47,0x58,0x4f,0xb1,0x00,
-0x28,0x02,0x0e,0xf2,0x46,0x44,0x01,0xbc,0x0e,0xfa,0x66,0x5c,0x08,0x60,0x20,0x64,
-0xa0,0xd9,0x3a,0x44,0x01,0x26,0x02,0x00,0x01,0x75,0x03,0x00,0x3b,0x44,0x01,0xbc,
-0x40,0x5b,0x17,0x00,0x35,0x40,0x04,0x2a,0x07,0x00,0x23,0x41,0x00,0xb9,0x40,0x55,
-0x10,0x02,0x18,0x60,0x7d,0x78,0xff,0xff,0x35,0x40,0x02,0x2a,0x07,0x00,0x22,0x41,
-0x00,0xb9,0x40,0x55,0x06,0x02,0x18,0x60,0xa5,0x78,0xff,0xff,0x35,0x40,0x08,0x26,
-0x03,0x00,0x34,0x40,0x08,0x2a,0x05,0x00,0x21,0x41,0x00,0xb9,0x40,0x55,0x40,0x54,
-0x99,0x03,0x00,0x00,0xa1,0xff,0xff,0xff,0xba,0x3f,0x19,0x60,0x68,0x61,0x27,0x42,
-0xa2,0xd3,0x0e,0x4c,0x41,0x4e,0x40,0x45,0x60,0x46,0x00,0xa8,0x09,0xf2,0x06,0x03,
-0x40,0x4b,0x1a,0x60,0x5e,0x78,0xff,0xff,0x2b,0x44,0xf5,0x00,0x0c,0x4e,0x2e,0x58,
-0xff,0xff,0x25,0x46,0x08,0xf0,0x27,0x43,0xa4,0xd5,0xbd,0xd3,0x46,0x45,0x63,0x45,
-0x64,0x43,0x60,0x41,0x00,0xa8,0x00,0x64,0xa3,0xdb,0xbe,0xdb,0x04,0x02,0x65,0x43,
-0x25,0x44,0xbf,0xdb,0x05,0x00,0x61,0x44,0x0a,0xfa,0x61,0x46,0x25,0x44,0x09,0xfa,
-0x25,0x44,0x27,0x43,0x08,0xfe,0x05,0x03,0x60,0x46,0x09,0xf2,0x08,0xfc,0x00,0xa8,
-0xfa,0x00,0x66,0x44,0xa5,0xdb,0x2e,0x58,0xff,0xff,0x28,0x41,0x58,0x4f,0x0e,0x00,
-0x2e,0x58,0xff,0xff,0x28,0x41,0x40,0xa1,0x58,0x4f,0x08,0x00,0x05,0x03,0x28,0x41,
-0x58,0x4f,0x26,0x00,0x58,0x4f,0x56,0x00,0x2e,0x58,0xff,0xff,0x9e,0xf3,0x7c,0x63,
-0x00,0xbe,0x40,0x45,0x1b,0x03,0x00,0x65,0x65,0x44,0xdc,0x85,0x84,0xa1,0x00,0xf2,
-0x06,0x06,0x01,0xfc,0x00,0xa8,0x60,0x46,0xf7,0x02,0x40,0x45,0x0f,0x00,0x33,0x44,
-0x54,0x93,0x33,0x44,0xfd,0xfb,0x80,0x60,0x7c,0x64,0x01,0xfa,0x00,0x64,0x00,0xf0,
-0x00,0xfa,0xd0,0x80,0x9e,0xf9,0x02,0x02,0x9f,0xf9,0x08,0xfe,0x2f,0x58,0xff,0xff,
-0x66,0x43,0x25,0x46,0x05,0xfc,0x06,0xfc,0x61,0x44,0x02,0xfa,0x01,0xf0,0x03,0x67,
-0xb0,0x84,0x00,0xf0,0x3c,0x7e,0x01,0xfa,0x04,0x64,0x03,0xfa,0x04,0xf8,0x00,0x64,
-0x23,0xfa,0x0c,0x61,0x10,0x63,0x59,0xda,0xfe,0x1f,0x2f,0x58,0xff,0xff,0x05,0x4c,
-0x7c,0x61,0x58,0x4f,0xc3,0x00,0x80,0x63,0x13,0x03,0x2c,0x46,0xbf,0xd0,0x25,0x46,
-0xff,0xd8,0x2c,0x46,0xfb,0x1d,0x25,0x46,0x00,0x64,0xd0,0x80,0x09,0xfa,0x0a,0xfa,
-0x05,0x03,0x64,0x46,0x01,0xf0,0x08,0x67,0xc0,0x84,0x01,0xfa,0x58,0x4f,0x02,0x00,
-0x2e,0x58,0xff,0xff,0x27,0x43,0x00,0xbb,0x25,0x46,0x12,0x03,0xbe,0xd3,0x08,0xfc,
-0x00,0xa8,0xff,0xff,0x03,0x02,0x25,0x44,0xa3,0xdb,0x04,0x00,0x0a,0xfa,0x60,0x46,
-0x25,0x44,0x09,0xfa,0xbe,0xdb,0x04,0xa3,0xa3,0xd3,0xff,0xff,0xdc,0x84,0xa3,0xdb,
-0x2f,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x28,0x00,0x0c,0x47,0x58,0x4f,0xe2,0x00,
-0x2e,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x20,0x00,0x0c,0x47,0x58,0x4f,0x5e,0x00,
-0x2e,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x18,0x00,0x0c,0x47,0x27,0x44,0x00,0xbe,
-0x08,0xf0,0x11,0x03,0x09,0xf2,0x25,0x43,0x09,0xfc,0x63,0x46,0x27,0x43,0x0a,0xfc,
-0x09,0xfa,0x08,0xf8,0x00,0xa8,0x66,0x43,0x03,0x02,0x64,0x44,0x58,0xdd,0x03,0x00,
-0x60,0x46,0x25,0x44,0x0a,0xfa,0x2e,0x58,0xff,0xff,0x25,0x46,0x08,0xf2,0x09,0xf0,
-0x00,0xa8,0x40,0x47,0x1d,0x03,0x0a,0xf2,0x64,0x43,0x00,0xbb,0x27,0x42,0xda,0x82,
-0x08,0x24,0xa2,0xdb,0x00,0xa8,0xca,0x82,0x02,0x02,0xa2,0xdd,0x02,0x00,0x60,0x46,
-0x09,0xfc,0x00,0xbb,0x63,0x46,0x08,0x28,0x0a,0xfa,0x25,0x46,0x00,0x64,0x09,0xfa,
-0x0a,0xfa,0x08,0xfa,0x27,0x42,0x04,0xa2,0xa2,0xd3,0xff,0xff,0xcc,0x84,0xa2,0xdb,
-0x2f,0x58,0xff,0xff,0x25,0x46,0x01,0xf2,0x00,0xf2,0xff,0xff,0x03,0x23,0x11,0x00,
-0x00,0xa8,0x60,0x46,0x0c,0x03,0x01,0xf0,0x78,0x67,0xa0,0x80,0xf8,0x67,0x07,0x03,
-0xc0,0x84,0x01,0xfa,0x25,0x46,0x00,0x64,0x00,0xfa,0x25,0x44,0x05,0xfa,0x58,0x4f,
-0xc4,0x00,0x58,0x4f,0x27,0x00,0xd1,0xfe,0x2e,0x58,0xff,0xff,0x27,0x43,0x00,0xbb,
-0x25,0x46,0x10,0x03,0xa3,0xd3,0x08,0xfc,0x00,0xa8,0x09,0xfa,0x03,0x02,0x25,0x44,
-0xbe,0xdb,0x04,0x00,0x60,0x46,0x25,0x44,0x0a,0xfa,0x25,0x46,0x00,0x64,0x0a,0xfa,
-0x25,0x44,0xa3,0xdb,0x2f,0x58,0xff,0xff,0x9f,0xf3,0x05,0xf0,0x00,0xbe,0x9f,0xf9,
-0x25,0x44,0x02,0x02,0x9e,0xfb,0x01,0x00,0x00,0xfa,0x25,0x46,0x7c,0x64,0x01,0xfa,
-0x2f,0x58,0xff,0xff,0x9f,0xf3,0x00,0x61,0x00,0xbe,0x25,0x44,0x02,0x03,0x00,0xfa,
-0x01,0x00,0x9e,0xfb,0x60,0x46,0x00,0xf2,0x66,0x43,0x00,0xbe,0xdd,0x81,0xfb,0x02,
-0x9f,0xfd,0x33,0x45,0x45,0x93,0x33,0x44,0xfd,0xfb,0x2f,0x58,0xff,0xff,0x25,0x46,
-0x05,0xf0,0x06,0xf2,0x05,0xfa,0xd0,0x80,0x64,0x43,0x0e,0x03,0x60,0x46,0x01,0xf0,
-0x80,0x67,0xb0,0x84,0x01,0xfa,0x00,0xf0,0x00,0x64,0x00,0xfa,0x64,0x46,0x05,0xfc,
-0x46,0x45,0x58,0x4f,0xd7,0x00,0xd1,0xfe,0x2e,0x58,0xff,0xff,0x00,0x66,0x46,0x45,
-0x29,0x43,0xfc,0xa3,0x66,0x44,0xbd,0xdb,0x25,0x44,0xbd,0xdb,0x00,0x64,0xbd,0xdb,
-0x03,0x61,0x1a,0x65,0x3c,0x60,0x80,0x63,0x43,0x49,0xa3,0xd3,0x06,0xa3,0x00,0xa8,
-0xcd,0x81,0x04,0x02,0xf9,0x02,0x19,0x60,0x56,0x78,0xff,0xff,0x01,0x26,0xe6,0x00,
-0xd4,0x80,0x60,0x45,0xe3,0x05,0xf6,0xa3,0xbd,0xd1,0xbd,0xd1,0x44,0x47,0x44,0x48,
-0x44,0x45,0x13,0x60,0x1a,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0x9e,0xf3,0x33,0x41,
-0x00,0xa8,0x40,0x45,0x0f,0x03,0x60,0x46,0x80,0x60,0x7c,0x64,0x01,0xfa,0x4d,0x93,
-0x33,0x44,0xfd,0xfb,0x00,0x64,0x00,0xf0,0x00,0xfa,0xd0,0x80,0x9e,0xf9,0x02,0x02,
-0x9f,0xf9,0x08,0xfe,0x2f,0x58,0xff,0xff,0xa2,0xfe,0xff,0xff,0x12,0x05,0xa0,0xfe,
-0xff,0xff,0x07,0x05,0xa3,0xfe,0xff,0xff,0x07,0x05,0xa1,0xfe,0xff,0xff,0x0a,0x04,
-0x18,0x00,0x20,0x58,0xff,0xff,0xff,0xff,0x65,0xf3,0xff,0xff,0x80,0xbc,0x65,0xfb,
-0x10,0x00,0xff,0xff,0xff,0xff,0x0a,0x00,0x99,0xff,0x30,0x44,0x50,0xbc,0x40,0x51,
-0x98,0xff,0x99,0xff,0x30,0x44,0x7d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x3c,0x44,0xac,0x84,0x8b,0xf3,0xf9,0x02,0x02,0xa8,0x00,0x64,0x09,0x02,
-0x8b,0xfb,0x8c,0xfb,0x8d,0xfb,0xca,0xfe,0x00,0x64,0x8e,0xfb,0x1b,0x60,0x20,0x78,
-0xff,0xff,0x8b,0xf3,0x8c,0xf3,0x02,0xa8,0x02,0xa8,0x0a,0x02,0x00,0x64,0x8d,0xfb,
-0x8b,0xfb,0x8c,0xfb,0x00,0x64,0x8e,0xfb,0xca,0xfe,0x1b,0x60,0xd8,0x78,0xff,0xff,
-0x8d,0xf1,0x00,0x64,0x64,0x41,0x02,0x02,0x8c,0xfb,0xca,0xfe,0x61,0x44,0x01,0xa8,
-0xff,0xff,0x09,0x02,0x3c,0x60,0x76,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,
-0x14,0x02,0xfe,0xb4,0x8d,0xfb,0x95,0xf3,0xff,0xff,0x60,0x40,0x01,0x26,0x10,0x00,
-0x8c,0xf3,0x8e,0xf3,0x01,0xa8,0x02,0xb0,0x01,0x02,0x0a,0x03,0x3c,0x60,0x2e,0x62,
-0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x67,0x03,0x1b,0x60,0xef,0x78,0xff,0xff,
-0x3c,0x60,0x2e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x74,0x02,0xcb,0xf3,
-0xff,0xff,0xfd,0xa0,0x6d,0xf3,0x44,0x02,0x02,0xb0,0x67,0xf1,0x06,0x02,0x64,0x43,
-0x00,0xbb,0xff,0xff,0x02,0x03,0x63,0x44,0x08,0x00,0xfd,0xb4,0x6d,0xfb,0x3c,0x60,
-0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x00,0xbc,0x67,0xfb,0x44,0x03,
-0x60,0x46,0x2b,0xf2,0xff,0xff,0x01,0xb0,0x6d,0xf3,0x04,0x03,0x01,0xb0,0xff,0xff,
-0x19,0x02,0x16,0x00,0xfe,0xb4,0x6d,0xfb,0x69,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,
-0x0f,0x03,0x19,0xf2,0xff,0xff,0x00,0xbc,0xff,0xff,0x0c,0x03,0xa0,0xd3,0xff,0xff,
-0x02,0xb0,0xff,0xff,0x07,0x03,0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x02,0x03,
-0x09,0xf2,0xdb,0x00,0x0d,0xf2,0xff,0xff,0x00,0x7f,0x0d,0xfa,0x09,0xf2,0x67,0xfb,
-0x00,0xbc,0xff,0xff,0x30,0x02,0x6d,0xf3,0xff,0xff,0xfe,0xb4,0x6d,0xfb,0x2b,0x00,
-0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0d,0x03,0x13,0xf3,
-0x95,0xf3,0x00,0xa8,0x00,0xa0,0x1f,0x03,0x1e,0x03,0x22,0xf0,0x10,0x64,0xb0,0x84,
-0xa2,0xda,0x1e,0x60,0xc5,0x78,0xff,0xff,0x00,0x64,0x40,0x5c,0x65,0xf3,0xff,0xff,
-0xcb,0xf1,0x80,0x2a,0x0d,0x00,0x7f,0xb4,0x65,0xfb,0x64,0x40,0x04,0x3a,0x08,0x00,
-0x29,0x44,0x2a,0x37,0x05,0x00,0x74,0xf3,0xff,0xff,0x04,0xbc,0x74,0xfb,0xc9,0xfe,
-0x1b,0x60,0x20,0x78,0xff,0xff,0x65,0xf3,0xff,0xff,0xff,0xff,0x80,0x26,0x13,0x00,
-0x02,0x26,0x0f,0x00,0x68,0x60,0xcc,0x62,0x90,0x60,0x62,0x64,0xa2,0xdb,0x3c,0x60,
-0x9a,0x62,0x08,0x64,0xa2,0xdb,0x00,0x64,0x5a,0xdb,0x2d,0xff,0x1b,0x60,0x2a,0x78,
-0xff,0xff,0x80,0xbc,0x65,0xfb,0xcb,0xf3,0x0e,0xf0,0xfc,0xa0,0xfd,0xa0,0x01,0x03,
-0x17,0x02,0x60,0x41,0x02,0x65,0x64,0x44,0x40,0x49,0x2a,0x37,0x11,0x00,0x18,0x37,
-0x0f,0x00,0xff,0x37,0x02,0x00,0xfd,0x3b,0x06,0x00,0x61,0x44,0xfd,0xa0,0x01,0x65,
-0x02,0x03,0x00,0x64,0x66,0xfb,0x74,0xf3,0xff,0xff,0xb4,0x84,0x74,0xfb,0xc9,0xfe,
-0x29,0xf2,0x66,0xf1,0x60,0x40,0xb0,0x36,0x06,0x00,0x00,0x36,0x04,0x00,0x20,0x36,
-0x02,0x00,0xb0,0x84,0x29,0xfa,0xf7,0x60,0x0d,0x78,0xff,0xff,0x1a,0xee,0x7f,0x00,
-0x86,0x02,0x67,0x60,0x4e,0x62,0x00,0x64,0xa2,0xdb,0x29,0xf0,0xff,0xff,0x64,0x40,
-0x40,0x27,0x03,0x00,0x1c,0x60,0x32,0x78,0xff,0xff,0x3e,0xf3,0xff,0xff,0xff,0xff,
-0x04,0x2a,0x19,0x00,0x46,0x5c,0x67,0x60,0x58,0x62,0x01,0x64,0xa2,0xdb,0xf7,0x60,
-0x2e,0x64,0x40,0x44,0x99,0xff,0x30,0x44,0x41,0xbc,0x40,0x51,0x98,0xff,0xa1,0xff,
-0xff,0xff,0xbb,0x3f,0x99,0xff,0x30,0x44,0xfe,0xb4,0x40,0x51,0x98,0xff,0x67,0x60,
-0x58,0x62,0x00,0x64,0xa2,0xdb,0x42,0xf3,0x00,0x65,0x60,0x41,0xd6,0xf3,0x95,0xf3,
-0x00,0xa0,0x00,0xa0,0x09,0x03,0x23,0xf2,0x04,0x03,0x60,0x45,0x00,0x7f,0x60,0x41,
-0x03,0x00,0xff,0xff,0xff,0x36,0x03,0x61,0x44,0xf1,0x98,0xff,0x64,0x40,0x08,0x26,
-0x02,0x00,0x00,0x64,0x14,0x00,0x07,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x06,0x03,
-0x1a,0xf2,0x38,0xfb,0xff,0xff,0x1b,0xf2,0x39,0xfb,0x15,0x00,0x38,0xf3,0x39,0xf1,
-0xdc,0x84,0x38,0xfb,0x1a,0xfa,0x64,0x44,0x02,0x24,0xdc,0x84,0xff,0xb4,0x60,0x5c,
-0x02,0xfe,0x61,0x44,0x03,0xb4,0xf8,0x84,0xf8,0x84,0xf8,0x84,0xb0,0x84,0x39,0xfb,
-0x1b,0xfa,0x01,0x64,0x07,0xfa,0x61,0x5c,0x41,0xf3,0x64,0x40,0x03,0x26,0x06,0x00,
-0x60,0x40,0x01,0x2a,0x15,0x00,0x67,0x60,0x90,0x63,0x30,0x00,0x64,0x40,0x03,0x2a,
-0x06,0x00,0x60,0x40,0x08,0x2a,0x0c,0x00,0x67,0x60,0xba,0x63,0x27,0x00,0x64,0x40,
-0x01,0x2a,0x0e,0x00,0x60,0x40,0x02,0x2a,0x03,0x00,0x67,0x60,0x9e,0x63,0x1e,0x00,
-0x46,0x5c,0x22,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,0xff,0xff,
-0x60,0x40,0x04,0x2a,0xf5,0x00,0x65,0x47,0x00,0x36,0x0e,0x00,0x00,0x7f,0xe0,0x84,
-0x60,0x45,0xe0,0x84,0xe0,0x81,0xc4,0x85,0xc5,0x85,0x3d,0x60,0x4e,0x64,0xc4,0x83,
-0x02,0x61,0x67,0x44,0xda,0xfb,0x02,0x00,0x67,0x60,0xac,0x63,0x00,0x60,0x00,0xea,
-0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xeb,0xff,0xff,0xff,0xff,0x38,0xf1,0xe0,0x7f,
-0x64,0x5e,0x99,0xff,0x40,0x5a,0x64,0x47,0xe1,0x7f,0x40,0x5a,0x39,0xf3,0xff,0xff,
-0xe2,0x7f,0x40,0x5a,0x61,0x5c,0xda,0xf3,0xda,0xf9,0xd0,0x80,0xbd,0xd3,0x30,0x03,
-0x60,0x45,0xe3,0x7f,0x40,0x5a,0x65,0x47,0xe4,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,
-0xe5,0x7f,0x40,0x5a,0x65,0x47,0xe6,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,0x45,0xf1,
-0xe7,0x7f,0x40,0x5a,0x64,0x40,0x0f,0x3a,0x61,0x00,0x65,0x47,0xe8,0x7f,0xbd,0xd3,
-0x40,0x5a,0x60,0x45,0xe9,0x7f,0x40,0x5a,0x65,0x47,0xea,0x7f,0xbd,0xd3,0x40,0x5a,
-0x60,0x45,0xeb,0x7f,0x40,0x5a,0x65,0x47,0xec,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,
-0xed,0x7f,0x40,0x5a,0x65,0x47,0xee,0x7f,0xbd,0xd3,0x40,0x5a,0xef,0x7f,0x40,0x5a,
-0x20,0x60,0x00,0xeb,0x67,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,
-0x3d,0x00,0x00,0x64,0xa2,0xdb,0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x09,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,
-0x80,0x2b,0xf7,0x00,0x00,0x64,0xdb,0xfb,0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,
-0xff,0xff,0x30,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0x3a,0x40,0x40,0x27,
-0xfd,0x00,0x67,0x60,0x5c,0x62,0x00,0x64,0xa2,0xdb,0x45,0xf1,0x80,0x60,0x00,0x64,
-0xb0,0x84,0x40,0x5a,0x98,0xff,0x33,0x60,0x00,0xea,0x3f,0xf3,0xff,0xff,0x01,0xbc,
-0x3f,0xfb,0x1c,0x60,0x32,0x78,0xff,0xff,0x64,0x38,0x7e,0x00,0xe8,0x06,0x19,0xf2,
-0xff,0xff,0x00,0xb8,0x1d,0xf2,0x06,0x03,0x60,0x47,0x00,0x7f,0x53,0xfb,0x51,0xfb,
-0x60,0x5c,0x07,0x00,0x50,0xf3,0x51,0xf1,0xff,0xff,0x52,0xfb,0x53,0xf9,0x00,0x64,
-0x54,0xfb,0x95,0xf3,0xff,0xff,0x00,0xbc,0xff,0xff,0x06,0x02,0x29,0xf2,0xff,0xff,
-0x0c,0xb4,0xff,0xff,0x00,0x3a,0x02,0x00,0x13,0xf0,0x53,0xf9,0x64,0x44,0x00,0xa0,
-0x01,0x63,0x04,0x03,0xff,0xa0,0x02,0x63,0x01,0x03,0x0b,0x63,0x55,0xfd,0x46,0x5c,
-0x22,0xf2,0xff,0xff,0x60,0x40,0x10,0x26,0x40,0x00,0x21,0xf2,0x16,0xf2,0x60,0x45,
-0x45,0x4c,0xc4,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x21,0xfa,0x17,0xf0,0x2c,0x44,
-0xc0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x1f,0xfa,0x95,0xf3,0x29,0xf0,0x00,0xbc,
-0x00,0x64,0x03,0x02,0x64,0x40,0x80,0x3a,0x01,0xbc,0x4f,0xfb,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x06,0x03,0x27,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x19,0x03,
-0x1c,0x00,0xa6,0xf3,0x56,0x63,0x02,0xa8,0x29,0xf2,0x13,0x03,0x29,0xf0,0x60,0x47,
-0x64,0x40,0x08,0x2a,0x0e,0x00,0x03,0xa8,0x03,0x2e,0x0b,0x00,0x03,0xb0,0x03,0x22,
-0x62,0x63,0x81,0xf1,0xbd,0xd8,0xff,0xff,0x82,0xf1,0xbd,0xd8,0xff,0xff,0x83,0xf1,
-0xa3,0xd8,0x4c,0xf3,0x34,0xfa,0x10,0xa4,0x4c,0xfb,0x29,0xf2,0xcb,0xf3,0x60,0x40,
-0x08,0x3a,0x76,0x00,0xfd,0xa0,0xfc,0xa0,0x05,0x03,0x95,0xf3,0x03,0x03,0x60,0x40,
-0x00,0x36,0x6e,0x00,0x14,0xf2,0x21,0xf0,0xcc,0x84,0x60,0x41,0x04,0x03,0x00,0x64,
-0xcd,0x81,0xc0,0x84,0xfd,0x02,0x1f,0xf0,0x53,0xf1,0xc0,0x84,0x55,0xf1,0x64,0x41,
-0x02,0x36,0xe0,0x84,0x64,0x45,0x00,0x62,0x11,0x61,0xe0,0x84,0xcd,0x81,0xfd,0x04,
-0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,0xd6,0x82,
-0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,0x14,0xf0,
-0x60,0x43,0x00,0x60,0xaf,0x65,0x00,0x64,0x64,0x41,0xcd,0x81,0xc4,0x84,0xfd,0x02,
-0x63,0x45,0xc4,0x84,0x40,0x48,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x10,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x28,0x44,0x70,0x45,0xc4,0x85,
-0x02,0x60,0x58,0x64,0xc4,0x84,0xa4,0xf1,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xc0,0x84,0x60,0x41,0x73,0x45,0xd4,0x80,0xe1,0xf1,0x0d,0x0d,
-0x64,0x44,0x00,0x36,0x38,0x00,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x61,0x45,
-0xc4,0x84,0x73,0x45,0xd4,0x80,0xff,0xff,0x2e,0x0e,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x20,0x64,0xa2,0xdb,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x0a,0x02,0x00,0x64,
-0x40,0x5c,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x1b,0x60,0x20,0x78,0xff,0xff,
-0x26,0x00,0xe2,0xf3,0x28,0x45,0x60,0x40,0x00,0x3a,0x15,0x00,0x07,0x60,0xd0,0x64,
-0xc4,0x84,0x70,0x45,0xd4,0x80,0xff,0xff,0x0e,0x04,0x60,0x50,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x80,0x60,0x30,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x01,0x64,0xe4,0xfb,0x0c,0x00,0x28,0x44,0x68,0xfb,0x60,0x45,0x68,0x60,0xcc,0x62,
-0x80,0x60,0x10,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x67,0x60,
-0x4e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0x08,0x03,0xa2,0xdb,0x67,0x60,
-0x5a,0x62,0xa2,0xd1,0x1e,0x60,0x9a,0x62,0xa2,0xd9,0x2a,0x00,0x1e,0x60,0x98,0x64,
-0xa0,0xd1,0x95,0xf3,0x64,0x41,0x00,0xa0,0x29,0xf2,0x0b,0x02,0x60,0x40,0x80,0x3a,
-0x08,0x00,0x71,0xf3,0xdd,0x81,0xf6,0xa0,0xec,0xa0,0x03,0x04,0xdd,0x81,0x01,0x04,
-0xdd,0x81,0x25,0xf2,0xff,0xff,0x60,0x47,0x60,0x43,0xff,0xb3,0x61,0x40,0xff,0x22,
-0x04,0x00,0x23,0x60,0x58,0x4f,0x4a,0x78,0xff,0xff,0x1e,0x60,0x9a,0x64,0xa0,0xdd,
-0xb8,0xf1,0x23,0x60,0x58,0x4f,0x49,0x78,0xff,0xff,0x1e,0x60,0x9c,0x64,0xa0,0xdd,
-0x29,0xf2,0xff,0xff,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x60,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x22,0xf0,0xff,0x60,0xef,0x64,0xa0,0x84,
-0xa2,0xda,0x8e,0xf3,0x46,0x5c,0xfd,0xb4,0x01,0xbc,0x8e,0xfb,0x66,0x44,0xe0,0xfb,
-0x20,0x60,0x19,0x78,0xff,0xff,0x00,0x64,0x68,0xfb,0xe0,0xfb,0x68,0x60,0xcc,0x62,
-0x90,0x60,0x61,0x64,0xa2,0xdb,0x46,0x5c,0x8e,0xf3,0x3c,0x46,0x4e,0xf1,0xfe,0xb4,
-0x8e,0xfb,0x01,0x65,0x32,0x40,0x04,0x2b,0x64,0x45,0x02,0x22,0x03,0x00,0x1e,0x60,
-0xed,0x78,0xff,0xff,0x65,0x40,0x01,0x26,0x09,0x00,0x5a,0x60,0x5a,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xc5,0x78,0xff,0xff,0x27,0xf0,0x01,0x60,
-0x00,0x64,0xc0,0x84,0x27,0xfa,0x29,0xf0,0x00,0x60,0x0c,0x64,0xa0,0x84,0x04,0x36,
-0x02,0x00,0x0c,0x3a,0x03,0x00,0x1e,0x60,0xc5,0x78,0xff,0xff,0x60,0x41,0x61,0x40,
-0x08,0x36,0x67,0x00,0x29,0xf0,0x00,0x60,0xf0,0x64,0xa0,0x84,0xff,0xff,0x00,0x3a,
-0x07,0x00,0x5a,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x56,0x00,
-0x20,0x3a,0x07,0x00,0x5a,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x4d,0x00,0x40,0x3a,0x07,0x00,0x5a,0x60,0x26,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x44,0x00,0xb0,0x3a,0x07,0x00,0x5a,0x60,0x1c,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x3b,0x00,0x80,0x3a,0x16,0x00,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x31,0x64,0xa2,0xdb,0x71,0xf3,0xff,0xff,0xdc,0x84,0x71,0xfb,0x80,0xf3,0xff,0xff,
-0xfd,0xa4,0x60,0x47,0x01,0xbc,0x6e,0xfb,0x5a,0x60,0x14,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x23,0x00,0xa0,0x3a,0x07,0x00,0x5a,0x60,0x24,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1a,0x00,0x50,0x3a,0x07,0x00,0x5a,0x60,0x2a,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0x00,0x10,0x3a,0x07,0x00,0x5a,0x60,
-0x20,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x08,0x00,0x30,0x3a,0x06,0x00,
-0x5a,0x60,0x20,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x19,0xf2,0x0e,0x65,0x00,0xbc,0xc4,0x83,0x14,0x03,0xa3,0xd3,0xff,0xff,
-0x60,0x40,0x14,0x3a,0xdc,0x84,0xa3,0xdb,0x14,0x3a,0x0c,0x00,0x60,0x47,0x00,0x7f,
-0xfd,0xa0,0xff,0xff,0x07,0x07,0x19,0xf0,0x1f,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,
-0x00,0x64,0xa3,0xdb,0x14,0xf2,0x28,0xf2,0x60,0x41,0x60,0x45,0x1e,0x63,0x60,0x47,
-0x18,0x63,0x04,0xa3,0x63,0x45,0x00,0x63,0xcd,0x81,0xc7,0x83,0xfd,0x02,0x38,0xf0,
-0xff,0xff,0xc3,0x83,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,0x31,0x00,0x3d,0x60,
-0x1c,0x64,0xa0,0xd3,0xff,0xff,0x01,0xbc,0xa2,0xdb,0x5a,0x60,0x00,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x51,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x04,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5a,0x60,0x42,0x64,0xa0,0xd3,0x63,0x45,0xc4,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x32,0x00,
-0x59,0x60,0xec,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x53,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x59,0x60,0xf0,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x18,0x60,0xbc,0x65,
-0x53,0xf3,0xff,0xff,0xe0,0x84,0xc4,0x82,0x00,0x64,0xa2,0xdb,0x5a,0x60,0x42,0x64,
-0xa0,0xd3,0x63,0x45,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0xcb,0xf3,0x3c,0x46,0xfd,0xa0,0xfc,0xa0,0x01,0x03,0x13,0x00,
-0x22,0xf2,0xff,0xff,0x60,0x40,0x10,0x2a,0x0e,0x00,0x8c,0xf1,0x00,0x64,0x40,0x5c,
-0x13,0xfb,0xe0,0xfb,0x64,0x40,0x02,0x36,0x03,0x00,0x1b,0x60,0x20,0x78,0xff,0xff,
-0x1b,0x60,0x2d,0x78,0xff,0xff,0x3c,0x60,0x7c,0x62,0x3c,0x60,0x4c,0x64,0xa2,0xdb,
-0x3c,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xce,0xfe,0x1b,0x60,
-0x3d,0x78,0xff,0xff,0x67,0x60,0x4e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x03,0x03,0x1f,0x60,0xa1,0x78,0xff,0xff,0x0f,0xf0,0xb7,0xf1,0x64,0x41,0x01,0x2a,
-0xb6,0xf1,0x95,0xf3,0xff,0xff,0x60,0x40,0xff,0x26,0x1d,0xf0,0x26,0xf2,0xff,0xff,
-0xdc,0x84,0x26,0xfa,0x15,0xf2,0xff,0xff,0xdc,0x84,0xd0,0x80,0x15,0xfa,0x03,0x04,
-0x1f,0x60,0xa6,0x78,0xff,0xff,0x13,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x05,0x03,
-0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0xad,0x00,0x95,0xf3,0xff,0xff,0x00,0xbc,
-0x29,0xf2,0x02,0x02,0x0c,0xb4,0x00,0x36,0x7d,0x00,0x19,0xf2,0xff,0xff,0x00,0xbc,
-0x0e,0xa4,0x4f,0x03,0x60,0x43,0xa3,0xd1,0x01,0x60,0x01,0x64,0xc0,0x84,0xa3,0xdb,
-0x60,0x45,0x00,0x7f,0xec,0xa0,0xff,0xff,0x0c,0x04,0x65,0x47,0x00,0x7f,0xf9,0xa0,
-0xff,0xff,0x05,0x04,0x19,0xf0,0x1f,0x60,0x58,0x4e,0xbe,0x78,0xff,0xff,0x00,0x64,
-0xa3,0xdb,0x0d,0xf2,0xff,0xff,0x01,0xa4,0x0d,0xfa,0x00,0x7f,0xfe,0xa0,0xff,0xff,
-0x15,0x04,0x1f,0x60,0x58,0x4e,0xec,0x78,0xff,0xff,0x1d,0xf2,0xff,0xff,0x00,0x7e,
-0x60,0x47,0x53,0xfb,0x0b,0x63,0x60,0x40,0x00,0x36,0x01,0x63,0x60,0x40,0x01,0x36,
-0x02,0x63,0x55,0xfd,0x0d,0xf2,0xff,0xff,0x00,0x7e,0x0d,0xfa,0x0d,0xf2,0xff,0xff,
-0x60,0x47,0x01,0xa4,0x60,0x47,0x0d,0xfa,0x60,0x47,0x00,0x7f,0xfd,0xa0,0xff,0xff,
-0x39,0x04,0x69,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,
-0xcc,0x78,0xff,0xff,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x54,0xf3,0xff,0xff,0xdc,0x84,0xfe,0xa0,0x54,0xfb,0x23,0x04,0x00,0x64,
-0x54,0xfb,0x53,0xf3,0x52,0xf3,0x60,0x41,0xff,0xa0,0xe8,0x84,0x1b,0x03,0xff,0xa1,
-0x60,0x45,0x59,0x60,0x6a,0x63,0xa3,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf4,0x03,
-0x52,0xfb,0x61,0x43,0x53,0xfd,0x63,0x44,0x00,0x3a,0x02,0x00,0x01,0x63,0x09,0x00,
-0x01,0x3a,0x02,0x00,0x02,0x63,0x05,0x00,0x02,0x3a,0x02,0x00,0x0b,0x63,0x01,0x00,
-0x0b,0x63,0x55,0xfd,0x29,0xf0,0x08,0x67,0xb0,0x84,0xa2,0xda,0x00,0x64,0x4f,0xfb,
-0xf8,0x60,0x50,0x78,0xff,0xff,0xa0,0xf0,0x7f,0x00,0x72,0x00,0x29,0xf0,0xff,0xff,
-0x64,0x40,0x40,0x2b,0x31,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x04,0x2a,0x18,0x00,
-0x67,0x60,0x58,0x62,0x01,0x64,0xa2,0xdb,0xf8,0x60,0x69,0x64,0x40,0x44,0x99,0xff,
-0x30,0x44,0x41,0xbc,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0x99,0xff,
-0x30,0x44,0xfe,0xb4,0x40,0x51,0x98,0xff,0x67,0x60,0x58,0x62,0x00,0x64,0xa2,0xdb,
-0x99,0xff,0x3a,0x44,0x98,0xff,0x8f,0x2b,0x0f,0x00,0x50,0x27,0x0d,0x00,0x67,0x60,
-0x5c,0x62,0x00,0x64,0xa2,0xdb,0x45,0xf1,0x80,0x60,0x00,0x64,0xb0,0x84,0x99,0xff,
-0x40,0x5a,0x98,0xff,0x33,0x60,0x00,0xea,0x1c,0x60,0xa0,0x78,0xff,0xff,0x4c,0x3f,
-0x7e,0x00,0xa8,0x07,0x29,0xf2,0xff,0xff,0x60,0x40,0x08,0x3a,0x06,0x00,0x5a,0x60,
-0x5a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0x98,0x65,0xb8,0xf3,
-0xff,0xff,0xa5,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0xff,0x00,0x64,0x44,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x60,0x47,
-0x00,0x7f,0x60,0x45,0x61,0x44,0x00,0x7f,0xe8,0x84,0xa4,0x80,0x00,0xa0,0x02,0x02,
-0x06,0x03,0xfa,0x00,0x60,0x41,0x65,0x47,0x61,0x45,0xb4,0x84,0xa2,0xdb,0x2e,0x58,
-0xff,0xff,0x64,0x44,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x60,0x47,0x00,0x7f,
-0x60,0x45,0x61,0x44,0x00,0x7f,0xe0,0x84,0xa4,0x80,0xf0,0xa0,0x02,0x02,0x06,0x03,
-0xfa,0x00,0x60,0x41,0x65,0x47,0x61,0x45,0xb4,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x19,0xf2,0xff,0xff,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x47,0x00,0x7f,0x60,0x45,
-0x1d,0xf2,0xff,0xff,0x40,0x4d,0x60,0x47,0x00,0x7f,0x60,0x41,0x2d,0x44,0x00,0x7f,
-0xcd,0x81,0xe8,0x84,0x07,0x0e,0xa4,0x80,0xff,0xff,0xfa,0x03,0x60,0x45,0x61,0x47,
-0xb4,0x84,0x1d,0xfa,0x2e,0x58,0xff,0xff,0x22,0x60,0x6b,0x78,0xff,0xff,0x99,0xff,
-0x30,0x44,0xff,0xff,0x98,0xff,0x80,0x2a,0x03,0x00,0x47,0xff,0x21,0x58,0xff,0xff,
-0x47,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0xff,0xff,0x99,0xff,0x32,0x44,0x98,0xff,
-0x10,0x26,0x57,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,0x53,0x0a,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0x4e,0x00,0x04,0x27,0x04,0x00,0xb5,0xf3,0xff,0xff,0x60,0x54,
-0xcd,0xe2,0x20,0x60,0x3a,0x64,0x40,0x42,0xc4,0xe2,0x99,0xff,0x30,0x44,0x04,0xbc,
-0x1d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0x36,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,0x32,0x0a,0x99,0xff,
-0x32,0x44,0x98,0xff,0x10,0x26,0x2d,0x00,0xd0,0xf3,0x4f,0xf3,0x00,0xa0,0xff,0xff,
-0xea,0x02,0x01,0x3a,0x63,0x00,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x40,0x5e,0x02,0xbc,
-0xff,0xff,0xff,0xff,0x40,0x5e,0x98,0xff,0x20,0x60,0x64,0x64,0x40,0x42,0x99,0xff,
-0x30,0x44,0x44,0xbc,0x5d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,
-0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x0c,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,
-0x08,0x0a,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x03,0x00,0x21,0x60,0x3b,0x78,
-0xff,0xff,0x20,0x60,0x84,0x64,0x40,0x40,0x20,0x60,0xb2,0x64,0x40,0x41,0x99,0xff,
-0x30,0x44,0xe0,0xbc,0xf9,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,
-0x28,0x60,0x60,0x62,0xa2,0xd3,0x13,0xf3,0x00,0xa0,0x00,0xa0,0x13,0x03,0x67,0x60,
-0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,
-0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,
-0xc5,0x78,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,
-0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,
-0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,0xff,0xff,0x21,0x60,0x1d,0x64,
-0x40,0x40,0x20,0x60,0xe2,0x64,0x40,0x42,0x99,0xff,0x30,0x44,0x44,0xbc,0x5d,0xb4,
-0x40,0x51,0x98,0xff,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0xb2,0x00,0xd0,0xf3,
-0x64,0xe2,0x00,0xa0,0x45,0xff,0x47,0xff,0x16,0x02,0xab,0x0a,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0xa6,0x00,0x1e,0x60,0x9a,0x65,0xa5,0xd3,0xff,0xff,0x01,0xa8,
-0xff,0xff,0x09,0x02,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x40,0x5e,0x02,0xbc,0xff,0xff,
-0xff,0xff,0x40,0x5e,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0xd0,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0xf8,0x02,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x89,0x00,
-0x64,0xe2,0x45,0xff,0x47,0xff,0x29,0x0a,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,
-0x24,0x00,0x13,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,
-0x01,0x64,0xa2,0xdb,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,
-0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x1e,0x60,0x9a,0x65,0xa5,0xd3,0xff,0xff,0x00,0xa8,0xcc,0x84,0x27,0x03,
-0xa5,0xdb,0x25,0x03,0x20,0x60,0xcf,0x78,0xff,0xff,0x20,0x60,0x75,0x78,0xff,0xff,
-0x1e,0x00,0x28,0x60,0x60,0x62,0xa2,0xd3,0x13,0xf3,0x00,0xa0,0x00,0xa0,0x13,0x03,
-0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,
-0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,
-0x1e,0x60,0xc5,0x78,0xff,0xff,0xc2,0x02,0x20,0x60,0xcf,0x78,0xff,0xff,0x13,0xf3,
-0xff,0xff,0x00,0xa8,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,
-0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,
-0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,0xff,0xff,0x29,0xf2,
-0xff,0xff,0x60,0x40,0x40,0x2b,0x25,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,
-0x0b,0x00,0x99,0xff,0x3a,0x5c,0x98,0xff,0x64,0x40,0x40,0x27,0x09,0x00,0x64,0x40,
-0x0f,0x2b,0x02,0x00,0x33,0x60,0x00,0xea,0x98,0xff,0x20,0x60,0xb2,0x78,0xff,0xff,
-0x99,0xff,0x3a,0x5c,0x98,0xff,0x64,0x40,0x10,0x2b,0xf6,0x00,0xdb,0xf3,0xff,0xff,
-0xff,0xff,0x01,0x26,0x06,0x00,0x8b,0xff,0x74,0x40,0x74,0x40,0x88,0xff,0x01,0x64,
-0xdb,0xfb,0x21,0xf3,0xff,0xff,0xc4,0xb4,0x21,0xfb,0x21,0x60,0x9d,0x64,0x40,0x40,
-0x01,0x64,0x22,0xfb,0x99,0xff,0x30,0x44,0x40,0xbc,0x59,0xb4,0x40,0x51,0x98,0xff,
-0xe2,0xf1,0x68,0x60,0xcc,0x62,0x80,0x60,0x70,0x64,0xa2,0xdb,0x64,0x40,0x01,0x26,
-0xff,0xff,0xa0,0xfe,0x1a,0xff,0x00,0x64,0x68,0xfb,0xff,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x95,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x07,0x02,0x13,0xf3,0xff,0xff,
-0x00,0xa0,0x00,0x64,0x02,0x03,0x13,0xfb,0xe3,0x00,0x28,0x60,0x60,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0x00,0x64,0x02,0x03,0xa2,0xdb,0xda,0x00,0x21,0xf3,0xff,0xff,
-0xff,0xff,0x01,0x2a,0x0d,0x00,0xfe,0xb4,0x21,0xfb,0x01,0x64,0x4e,0xfb,0x1e,0x60,
-0x98,0x65,0xb8,0xf3,0xff,0xff,0xa5,0xdb,0xff,0xff,0x22,0x60,0x3c,0x78,0xff,0xff,
-0x02,0x2a,0x62,0x00,0x27,0xf2,0xff,0xff,0xdc,0x84,0x27,0xfa,0x5a,0x60,0x46,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x17,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0xe8,0x84,0xe0,0x84,
-0x60,0x45,0x18,0x60,0x56,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x53,0xf3,0xff,0xff,0xe0,0x84,0x60,0x45,0x18,0x60,0xbc,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x53,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x5a,0x60,0x4a,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x21,0xf3,0xff,0xff,0xfd,0xb4,
-0x21,0xfb,0x02,0x64,0x4e,0xfb,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x00,0x36,
-0x5e,0x00,0x95,0xf1,0x00,0x63,0xd3,0x80,0xff,0xff,0x04,0x02,0xb9,0xf1,0xff,0xff,
-0x64,0x45,0x09,0x00,0x25,0xf2,0xff,0xff,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0x0f,0xb4,0xff,0xff,0x60,0x45,0x1e,0x60,0x98,0x63,0xa3,0xd3,0xff,0xff,0xd4,0x80,
-0xdc,0x84,0x45,0x03,0xa3,0xdb,0x43,0x00,0x10,0x2a,0x07,0x00,0x21,0xf3,0x2b,0xf0,
-0xef,0xb4,0x21,0xfb,0x20,0x60,0x19,0x78,0xff,0xff,0x21,0xf3,0xff,0xff,0xdf,0xb4,
-0x21,0xfb,0x2b,0xf0,0x02,0x64,0x64,0x40,0x01,0x26,0x01,0x64,0x4e,0xfb,0x2f,0x00,
-0x00,0x63,0x95,0xf3,0x68,0xfd,0x00,0xbc,0xff,0xff,0x29,0x02,0x28,0x0a,0x70,0x44,
-0xac,0x80,0xff,0xff,0x24,0x02,0x32,0x40,0x02,0x27,0x21,0x00,0x1e,0x60,0x9c,0x65,
-0xa5,0xd3,0xff,0xff,0x00,0xa8,0xff,0xff,0x1a,0x03,0x22,0x60,0x5d,0x64,0x40,0x42,
-0x99,0xff,0x30,0x44,0x04,0xbc,0x1d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x0d,0x0a,0x70,0x44,0xac,0x80,0xff,0xff,0x09,0x02,0x1e,0x60,0x9c,0x65,
-0xa5,0xd3,0xff,0xff,0xcc,0x84,0xa5,0xdb,0xff,0xff,0xf0,0x02,0x00,0x00,0x99,0xff,
-0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1d,0x60,
-0x96,0x78,0xff,0xff,0xa4,0xe2,0xff,0xff,0xa4,0xe2,0x73,0x44,0xa4,0xfb,0x80,0xf3,
-0x60,0x45,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc4,0x93,0x00,0x60,0x01,0x71,
-0x95,0xf1,0xcb,0xf3,0x64,0x40,0x00,0x36,0x05,0x00,0x8a,0xff,0x80,0x60,0x00,0x75,
-0x88,0xff,0x03,0x00,0xfc,0xa0,0xff,0xff,0x18,0x02,0xe1,0xf3,0x80,0xf1,0x00,0xa0,
-0x60,0x45,0x13,0x03,0x67,0x60,0x7c,0x62,0x64,0x41,0xd5,0x84,0xdc,0x84,0xa2,0xdb,
-0x00,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0x00,0x7f,0xbf,0xb4,0x40,0x5c,0x98,0xff,
-0x68,0x60,0xcc,0x62,0x80,0x60,0x60,0x64,0xa2,0xdb,0xe4,0xf3,0xe3,0xf1,0x00,0xa0,
-0xff,0xff,0x03,0x03,0x64,0x50,0x00,0x64,0xe4,0xfb,0x5e,0xf3,0xff,0xff,0x60,0x41,
-0xfd,0xb4,0xa2,0xdb,0x61,0x44,0x01,0xb0,0x02,0xb0,0x0a,0x03,0x09,0x02,0x3c,0x60,
-0xae,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x01,0x64,0x02,0x02,0x75,0xfb,
-0xc9,0xfe,0x73,0x44,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x10,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0xcb,0xf3,0x62,0xf3,0xfc,0xa0,0x00,0xa0,
-0x69,0x02,0x17,0x02,0x64,0xf3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x12,0x03,0x64,0xfb,
-0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,
-0x65,0x44,0xa2,0xdb,0x57,0x02,0x63,0xf3,0x64,0xfb,0x67,0x60,0x8a,0x62,0x01,0x64,
-0xa2,0xdb,0x67,0x60,0x8a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0x4a,0x03,
-0xa2,0xdb,0x67,0x60,0x84,0x65,0x67,0x60,0x82,0x63,0xa5,0xd1,0xa3,0xd3,0x64,0x40,
-0x00,0x36,0x40,0x03,0xcc,0x84,0xa3,0xdb,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x50,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x33,0x02,0x64,0x44,
-0xa3,0xdb,0x67,0x60,0x86,0x63,0x67,0x60,0x8c,0x65,0xbd,0xd3,0xa3,0xdb,0xc4,0xa0,
-0xff,0xff,0x11,0x04,0xc4,0xa4,0xa3,0xdb,0xa5,0xdb,0xf0,0x60,0x00,0x64,0x60,0x50,
-0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,
-0x65,0x44,0xa2,0xdb,0x17,0x00,0x60,0x47,0xe0,0x84,0xe0,0x84,0x70,0x45,0xd4,0x80,
-0xff,0xff,0x10,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x67,0x60,0x8c,0x65,0x00,0x64,
-0xa3,0xdb,0xa5,0xdb,0x1b,0x60,0x2a,0x78,0xff,0xff,0x64,0x41,0x40,0x60,0x0b,0x65,
-0x2b,0x44,0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,0x94,0x84,0xf3,0x83,0xcd,0x81,
-0xff,0xff,0xf8,0x02,0x2f,0x58,0x40,0x4b,0x00,0x62,0x01,0x64,0xd4,0x80,0xe0,0x84,
-0x1b,0x03,0xd4,0x80,0xe0,0x84,0x16,0x03,0x61,0x44,0x11,0x61,0xe0,0x84,0xcd,0x81,
-0xfd,0x04,0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,
-0xd6,0x82,0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,
-0x2f,0x58,0xff,0xff,0xe9,0x81,0xf2,0x82,0x61,0x44,0xfa,0x00,0x12,0xf1,0x7f,0x00,
-0xda,0x00,0xa1,0xff,0xff,0xff,0xbc,0x3f,0x40,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,
-0x03,0x00,0x18,0x60,0x17,0x78,0xff,0xff,0x3d,0x46,0x29,0xf0,0xff,0xff,0x64,0x40,
-0x40,0x2b,0x27,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x02,0x26,0x1a,0x00,0x0f,0xf2,
-0xff,0xff,0x60,0x40,0x04,0x2a,0x00,0x00,0x99,0xff,0x3b,0x44,0x98,0xff,0xff,0xff,
-0x0f,0x2b,0x05,0x00,0x40,0x27,0xf8,0x00,0xf5,0x60,0xbc,0x78,0xff,0xff,0x9c,0xf3,
-0xff,0xff,0xff,0xff,0x02,0x26,0xd5,0x00,0x67,0x60,0xca,0x62,0x01,0x64,0xa2,0xdb,
-0xd0,0x00,0x0f,0xf2,0xff,0xff,0x60,0x40,0x04,0x2a,0xcb,0x00,0x17,0x60,0xa6,0x78,
-0xff,0xff,0x0f,0xf2,0xff,0xff,0x08,0xbc,0x0f,0xfa,0x3c,0x60,0x64,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa8,0x60,0x46,0x01,0x02,0xbc,0x00,0x0f,0xf0,0xff,0xff,0xff,0xff,
-0x64,0x40,0x00,0x3a,0x04,0x00,0x0f,0xf0,0x70,0x64,0xb0,0x84,0xa2,0xda,0x67,0x60,
-0x62,0x62,0x00,0x64,0xa2,0xdb,0x40,0xfb,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,
-0x3d,0x46,0x0f,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0xcb,0xfe,0x40,0xff,0xbc,0xfe,
-0x29,0xf2,0xff,0xff,0xff,0xff,0x40,0x2b,0x9c,0x00,0x67,0x60,0x58,0x64,0xa0,0xd3,
-0xff,0xff,0xff,0xa0,0xff,0xff,0x95,0x02,0x04,0xff,0x93,0x00,0xf4,0x46,0x7e,0x00,
-0x00,0x10,0x7f,0xf1,0x10,0x60,0xdc,0xe0,0x43,0x45,0x65,0xf3,0x44,0x46,0x60,0x40,
-0x02,0x2a,0x03,0x00,0x23,0x60,0xc8,0x78,0xff,0xff,0x01,0x64,0x65,0xfb,0x99,0xff,
-0x00,0x6b,0x3e,0x44,0x70,0xb4,0x40,0x5e,0x3d,0x44,0xf7,0xb4,0x90,0xbc,0x40,0x5d,
-0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x98,0xff,0x20,0x44,0x60,0xbc,0x40,0x40,0x01,0x64,
-0x60,0x47,0x99,0xfb,0x05,0x64,0x9a,0xfb,0xff,0xff,0xdf,0xfe,0x19,0xff,0x23,0x60,
-0xa6,0x64,0x9b,0xfb,0xf8,0x60,0x89,0x78,0xff,0xff,0x1f,0xf3,0xff,0xff,0xff,0xff,
-0x20,0x26,0x05,0x00,0x13,0x60,0x5a,0x63,0x14,0x60,0x3e,0x65,0x04,0x00,0x14,0x60,
-0x3e,0x63,0x15,0x60,0x22,0x65,0x80,0xe1,0x02,0x00,0x01,0x16,0xfe,0x00,0xbd,0xd1,
-0xff,0xff,0x64,0x48,0x64,0x47,0x00,0x7f,0x60,0x41,0x80,0xbc,0x60,0x4a,0xff,0xff,
-0xff,0xff,0xa1,0xff,0xff,0xff,0xd7,0x80,0xff,0xff,0xef,0x02,0x68,0x40,0x67,0x60,
-0x80,0x62,0xa2,0xd3,0x00,0x7c,0xa2,0xd9,0x60,0x40,0x01,0x2a,0x09,0x00,0x40,0x65,
-0xa4,0x85,0x99,0xff,0x3c,0x44,0xbf,0xb4,0xb4,0x84,0x00,0x7f,0x40,0x5c,0x98,0xff,
-0x20,0x44,0x60,0xbc,0x40,0x40,0x80,0xe1,0x14,0x60,0x7a,0x63,0xa3,0xd1,0x67,0x60,
-0x70,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x80,0xa2,0xd9,0x16,0x03,0x01,0x64,0xd8,0xfb,
-0xd7,0xf3,0xff,0xff,0xff,0xff,0x01,0x26,0xfb,0x00,0x64,0x48,0x64,0x47,0x00,0x7f,
-0x80,0xbc,0x60,0x4a,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x68,0x40,0x00,0x64,0xd8,0xfb,0x66,0x60,0xd4,0x62,0x17,0x60,0x52,0x7c,
-0xa2,0xd3,0x99,0xff,0xe0,0x84,0x40,0xd1,0x00,0x63,0x64,0x40,0x01,0x26,0x10,0x63,
-0xdd,0xfd,0x00,0x65,0x64,0x40,0x02,0x26,0x04,0x65,0x3c,0x44,0xfb,0xb4,0xb4,0x84,
-0x40,0x5c,0x98,0xff,0x26,0x44,0x7f,0xfb,0x02,0x7f,0x99,0xfb,0x05,0x64,0x9a,0xfb,
-0xff,0xff,0xdf,0xfe,0x19,0xff,0x24,0x60,0x28,0x64,0x9b,0xfb,0x9c,0xf3,0x69,0x65,
-0xb4,0x84,0x99,0xff,0x40,0x51,0x98,0xff,0xf8,0x60,0x89,0x78,0xff,0xff,0x65,0xf3,
-0xff,0xff,0x02,0xbc,0x65,0xfb,0x25,0x43,0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,
-0x43,0x45,0x20,0xbc,0x40,0x40,0x65,0xf3,0xff,0xff,0x60,0x40,0x80,0x2a,0x03,0x00,
-0x24,0x60,0x55,0x78,0xff,0xff,0x00,0xee,0x04,0xbc,0x65,0xfb,0xff,0xff,0xdf,0xfe,
-0x19,0xff,0xff,0xff,0x99,0xff,0x00,0x6b,0x3c,0x44,0x40,0xb4,0x80,0xbc,0x40,0x5c,
-0x00,0xed,0x04,0xee,0xff,0xff,0xff,0xff,0x00,0xee,0x98,0xff,0x00,0x64,0x65,0xfb,
-0x55,0x60,0xfc,0xe0,0xff,0xff,0xff,0xff,0x25,0x43,0x24,0x60,0x9d,0x78,0xff,0xff,
-0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,0x20,0xbc,0x40,0x40,0x24,0x60,0x9d,0x78,
-0xff,0xff,0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x98,0xff,0x99,0xff,
-0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0x99,0xff,0x3e,0x44,0x02,0xbc,
-0x00,0x7f,0x40,0x5e,0x98,0xff,0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,0x20,0xbc,
-0x40,0x40,0x80,0xe1,0x01,0x64,0xd8,0xfb,0xd7,0xf3,0xff,0xff,0xff,0xff,0x01,0x26,
-0xfb,0x00,0x64,0x47,0x3f,0xb4,0xe0,0x85,0x64,0x44,0x80,0x2b,0x09,0x00,0x00,0x7f,
-0x60,0x48,0x65,0x44,0x80,0xbc,0x60,0x4a,0xff,0xff,0xff,0xff,0x0b,0x16,0xfe,0x00,
-0x65,0x49,0xff,0xff,0xff,0xff,0xa1,0xff,0x00,0x64,0x68,0x5e,0x60,0x5c,0x08,0x60,
-0x0a,0x64,0xa0,0xd9,0x00,0x64,0xd8,0xfb,0x9c,0xf3,0x69,0x65,0xb4,0x84,0x99,0xff,
-0x40,0x51,0x98,0xff,0x00,0x64,0xbf,0xdb,0x20,0x44,0x20,0x2a,0x07,0x00,0x07,0xb4,
-0x04,0x36,0xc3,0xfe,0x06,0x36,0xcc,0xfe,0x07,0x36,0xd8,0xfe,0x20,0x44,0xd8,0xb4,
-0x40,0x40,0x20,0x44,0x40,0x2a,0x08,0x00,0x9f,0xfe,0xff,0xff,0x24,0x05,0xbf,0xb4,
-0x40,0x40,0x9b,0xf7,0xff,0xff,0xff,0xff,0x3c,0x60,0x8e,0x62,0xa2,0xd3,0xda,0x83,
-0x00,0xa8,0x02,0x61,0x1b,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,0x00,0xa8,0x02,0x61,
-0x15,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,0x00,0xa8,0x04,0x61,0x0f,0x02,0xdb,0x82,
-0x5a,0xd3,0xda,0x83,0x00,0xa8,0x06,0x61,0x09,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,
-0x00,0xa8,0x07,0x61,0x03,0x02,0xf8,0x60,0x89,0x78,0xff,0xff,0xa3,0xd1,0x40,0x44,
-0x62,0x43,0x20,0x44,0x07,0xb5,0xd4,0x85,0x35,0x80,0x24,0x45,0x13,0x60,0x32,0x64,
-0x44,0xd7,0xff,0xff,0xff,0xff,0x80,0xe1,0x43,0x45,0x20,0x44,0x20,0xbc,0x40,0x40,
-0x64,0x43,0xbd,0xd3,0xbd,0xd1,0xff,0xff,0x10,0x2b,0x01,0x00,0x0b,0x00,0x01,0x16,
-0xfe,0x00,0x64,0x49,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x44,
-0x00,0x7f,0xa3,0xdb,0x25,0x43,0x98,0x00,0x80,0xe1,0x43,0x45,0x20,0x44,0x20,0xbc,
-0x40,0x40,0x64,0x43,0xbd,0xd3,0xbd,0xd1,0x40,0x44,0x10,0x2b,0x01,0x00,0x1a,0x00,
-0xa3,0xd3,0xff,0xff,0x01,0x16,0xfe,0x00,0x60,0x48,0x64,0x44,0x00,0x7f,0x60,0x4a,
-0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x40,0x01,0x16,0xfe,0x00,
-0x64,0x49,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x45,0xd4,0x80,
-0xff,0xff,0xe8,0x02,0x25,0x43,0xd7,0x00,0x3c,0x60,0xac,0x61,0xa1,0xd3,0x00,0x63,
-0x00,0xa8,0x59,0xd1,0x31,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x2c,0x02,
-0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x27,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,
-0x59,0xd1,0x22,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x1d,0x02,0x59,0xd3,
-0x00,0x63,0x00,0xa8,0x59,0xd1,0x18,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,
-0x13,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x0e,0x02,0x59,0xd3,0x00,0x63,
-0x00,0xa8,0x59,0xd1,0x09,0x02,0xf8,0x60,0x89,0x78,0xff,0xff,0x27,0x60,0xb5,0x78,
-0xff,0xff,0x27,0x60,0x8f,0x78,0xff,0xff,0x49,0xdd,0x60,0x40,0x02,0x36,0xf9,0x00,
-0x03,0x36,0xf4,0x00,0x01,0x36,0x28,0x00,0x05,0x3a,0xbe,0x00,0xa4,0xd3,0x5a,0xd3,
-0x9c,0x85,0xa4,0x84,0xa2,0xdb,0xb8,0x00,0x84,0xe2,0x04,0x60,0x00,0x71,0x1e,0xf3,
-0x14,0xf3,0x00,0xbd,0xcc,0x84,0x08,0x03,0x14,0xfb,0x06,0x02,0x65,0x44,0x14,0xfb,
-0x8a,0xff,0x80,0x60,0x00,0x75,0x88,0xff,0x73,0x44,0xce,0xfb,0x1e,0x60,0x92,0x62,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x0a,0x02,0x1e,0x60,0x94,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x03,0x00,0x27,0x60,0x88,0x78,0xff,0xff,0x66,0x60,
-0xb4,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x06,0x03,0xa2,0xdb,0x04,0x02,
-0x3a,0x44,0x02,0xbc,0x40,0x5a,0x3b,0xff,0x6e,0xf3,0xff,0xff,0x60,0x47,0xff,0x23,
-0x05,0x00,0xcc,0x84,0x60,0x47,0xff,0x22,0x00,0x64,0x6e,0xfb,0xa4,0xf3,0xff,0xff,
-0x10,0xa4,0xa4,0xfb,0x68,0xf3,0x10,0xa5,0x00,0xa0,0x73,0x41,0x49,0x03,0xd5,0x80,
-0xff,0xff,0x27,0x03,0x70,0x45,0xc4,0x85,0x02,0x60,0x58,0x64,0xc4,0x84,0xa4,0xf1,
-0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x40,0x4d,0xc0,0x84,
-0x60,0x41,0x73,0x45,0xd4,0x80,0xe1,0xf1,0x14,0x0d,0x64,0x44,0x00,0x36,0x30,0x00,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x61,0x45,0xc4,0x84,0x73,0x45,0xd4,0x80,
-0xff,0xff,0x07,0x0d,0x67,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,
-0x1f,0x02,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x06,0x02,0x01,0x64,0x13,0xfb,
-0x00,0x64,0x68,0xfb,0xc0,0xfe,0x14,0x00,0xe3,0xf1,0x2d,0x44,0xc0,0x84,0x70,0x45,
-0xd4,0x80,0xff,0xff,0x0d,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x31,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0x64,0xe4,0xfb,
-0x66,0x60,0xb2,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x12,0x03,0xa2,0xdb,
-0x10,0x02,0xe0,0xf3,0x76,0xf3,0x60,0x45,0x77,0xf3,0xd4,0x80,0xd4,0x80,0x01,0x03,
-0x08,0x02,0x28,0x60,0x60,0x62,0x80,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,
-0xa2,0xdb,0x3d,0x60,0x1a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x0c,0x03,
-0xcc,0x84,0xa2,0xdb,0x09,0x02,0x3d,0x60,0x16,0x63,0xbd,0xd1,0x18,0x60,0xc8,0x64,
-0xa0,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,0x32,0x44,0x01,0x2a,0x30,0x00,0x68,0x60,
-0xd4,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x3b,0x02,0x68,0x60,0xda,0x61,
-0xa1,0xd3,0xff,0xff,0xcc,0x84,0xff,0xff,0xa1,0xdb,0x33,0x02,0x20,0x40,0x40,0x2a,
-0x03,0x00,0x01,0x64,0xa1,0xdb,0x2d,0x00,0xff,0x64,0xa1,0xdb,0x7f,0xf3,0xff,0xff,
-0x01,0xa4,0x7f,0xfb,0xf2,0xa0,0xff,0xff,0x02,0x06,0x01,0x64,0x7f,0xfb,0x60,0x41,
-0x20,0x44,0x40,0xbc,0x40,0x40,0x24,0x60,0xbd,0x64,0x9b,0xfb,0x61,0x44,0x02,0x7f,
-0x99,0xfb,0x05,0x64,0x9a,0xfb,0xff,0xff,0xdf,0xfe,0x19,0xff,0x12,0x00,0x95,0xf3,
-0x65,0xf3,0x00,0xa0,0xff,0xff,0x0d,0x03,0x02,0x2a,0x0b,0x00,0x16,0x60,0xbc,0x62,
-0xa2,0xd3,0x7f,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x03,0x03,0x20,0x40,0x40,0x2a,
-0xd8,0x00,0x67,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x31,0x03,
-0xa2,0xdb,0x2f,0x02,0x01,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0x40,0xbc,0x00,0x7f,
-0x40,0x5c,0x98,0xff,0x68,0x60,0xcc,0x62,0x80,0x60,0x61,0x64,0xa2,0xdb,0x22,0xf3,
-0xff,0xff,0x01,0xb4,0xff,0xff,0x06,0x03,0x67,0x60,0x8e,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0xe1,0xf3,0xe3,0xf1,0x60,0x47,0xe0,0x84,0xe0,0x84,0xc0,0x84,
-0x70,0x45,0xd4,0x80,0xff,0xff,0x0d,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,
-0x80,0x60,0x31,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x00,0x64,
-0xe4,0xfb,0x67,0x60,0x7e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x16,0x03,
-0xa2,0xdb,0x14,0x02,0x00,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0xbf,0xb4,0x00,0x7f,
-0x40,0x5c,0x98,0xff,0x68,0x60,0xcc,0x62,0x80,0x60,0x60,0x64,0xa2,0xdb,0x67,0x60,
-0x7c,0x63,0xe1,0xf3,0x80,0xf3,0x60,0x45,0xd4,0x84,0xa3,0xdb,0x67,0x60,0x8c,0x62,
-0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x10,0x03,0xa2,0xdb,0x04,0x60,0x00,0x65,
-0x70,0x44,0xc4,0x84,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0xe1,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x31,0x02,0x28,0x44,0xcc,0x84,0x40,0x48,0x2d,0x02,0x67,0x60,0x50,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xff,0x03,0x22,0x03,0x00,0x01,0x2a,0x1a,0x00,0x07,0x00,
-0x67,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x01,0xac,0xa2,0xdb,0x12,0x03,0x99,0xff,
-0x3c,0x44,0x40,0xbc,0x00,0x7f,0x40,0x5c,0x98,0xff,0xcb,0xf3,0x0a,0x65,0xfc,0xa0,
-0xfd,0xa0,0x10,0x03,0x0f,0x03,0xfb,0xa0,0xff,0xff,0x0c,0x03,0x01,0x60,0xf4,0x65,
-0x09,0x00,0x99,0xff,0x3c,0x44,0xbf,0xb4,0x40,0x5c,0x67,0x60,0x4a,0x62,0xa2,0xd3,
-0x98,0xff,0x60,0x45,0x45,0x48,0x2b,0x44,0xcc,0x84,0x40,0x4b,0x40,0x02,0x03,0x60,
-0xe8,0x64,0x40,0x4b,0x2c,0x44,0xcc,0x84,0x40,0x4c,0x05,0x02,0x1e,0x64,0x4b,0xf3,
-0x40,0x4c,0xdc,0x84,0x4b,0xfb,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xfd,0xa0,0x07,0x03,
-0x06,0x03,0xfb,0xa0,0xff,0xff,0x03,0x03,0x05,0x60,0xdc,0x64,0x24,0x00,0x5a,0x60,
-0x74,0x63,0xa3,0xd1,0x59,0x60,0xec,0x62,0xa2,0xd3,0xff,0xff,0xc0,0x83,0x67,0x60,
-0x44,0x62,0xa2,0xd1,0xa2,0xdd,0xd3,0x84,0xff,0xff,0xfe,0x27,0x13,0x00,0x01,0x27,
-0x0f,0x00,0xc0,0x26,0x0b,0x00,0x30,0x26,0x07,0x00,0x0f,0x26,0x03,0x00,0x03,0x60,
-0xde,0x64,0x09,0x00,0xf0,0x64,0x07,0x00,0x73,0x64,0x05,0x00,0x38,0x64,0x03,0x00,
-0x1e,0x64,0x01,0x00,0x0f,0x64,0x60,0x5c,0x67,0x60,0x4a,0x62,0xa2,0xd9,0x01,0x60,
-0x3a,0x61,0xa1,0xd3,0x61,0x43,0x00,0xa8,0x60,0x41,0x03,0x02,0xf8,0x60,0x89,0x78,
-0xff,0xff,0x59,0xd3,0x00,0x66,0x00,0xa8,0xcc,0x84,0x02,0x03,0xa1,0xdb,0xf6,0x00,
-0x49,0xd3,0xa3,0xdb,0x00,0xa8,0x60,0x43,0x5b,0xd3,0x06,0x03,0x00,0xa8,0xcc,0x84,
-0x02,0x02,0x01,0x66,0x01,0x00,0xa3,0xdb,0x06,0xa1,0xa1,0xd3,0x59,0xd1,0x60,0x45,
-0xa5,0xd3,0x59,0xd1,0xb0,0x84,0xa5,0xdb,0x64,0x47,0x06,0x36,0xcd,0xfe,0x07,0x37,
-0xd9,0xfe,0x66,0x40,0x00,0x3a,0xd3,0x00,0xf8,0x60,0x89,0x78,0xff,0xff,0x01,0x60,
-0x3a,0x61,0x00,0x64,0xa1,0xdb,0x25,0x60,0x2d,0x78,0xff,0xff,0x27,0x60,0x94,0x64,
-0x40,0x41,0x44,0x42,0x24,0x00,0x01,0x60,0x3a,0x66,0xa6,0xd3,0x04,0xa1,0x60,0x43,
-0xa1,0xd3,0xc9,0x81,0x60,0x45,0x00,0xbb,0xa1,0xdb,0xbe,0xd3,0x09,0x03,0xd4,0x84,
-0x9c,0x84,0xdc,0x84,0xff,0xff,0x04,0x0e,0xa3,0xd1,0x63,0x46,0x64,0x43,0xf2,0x00,
-0x9c,0x84,0xdc,0x85,0x49,0xdd,0x61,0x44,0x00,0xbb,0xa6,0xdb,0x02,0x03,0x65,0x44,
-0xbe,0xdb,0x25,0x60,0x2d,0x78,0xff,0xff,0x25,0x60,0x2d,0x64,0x40,0x41,0x01,0x60,
-0x3a,0x66,0xa6,0xd3,0xff,0xff,0x00,0xa8,0xd0,0x80,0x10,0x03,0x02,0x03,0x60,0x46,
-0xf8,0x00,0x58,0xd3,0xa4,0xd3,0x60,0x45,0x00,0x63,0xa4,0xdd,0x58,0xd3,0x02,0xa8,
-0xc4,0x83,0x01,0x03,0xa2,0xdd,0x62,0x44,0xc8,0x84,0xa6,0xdb,0x21,0x58,0x22,0x41,
-0x28,0x60,0xaa,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x1a,0x64,0xbd,0xdb,
-0xbd,0xdb,0x06,0x64,0xa3,0xdb,0x1f,0x60,0x18,0x62,0x58,0x60,0xd2,0x64,0xa2,0xdb,
-0x1e,0x60,0xea,0x62,0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x28,0x60,
-0x92,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x12,0x64,0xbd,0xdb,0xbd,0xdb,
-0x06,0x64,0xa3,0xdb,0x1f,0x60,0x10,0x62,0x5c,0x60,0x14,0x64,0xa2,0xdb,0x1e,0x60,
-0xea,0x62,0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x00,0x60,0x7a,0x66,
-0x32,0x64,0x61,0xfb,0x1e,0x60,0x92,0x64,0xa0,0xd3,0x03,0xfa,0x0f,0x4e,0x00,0x60,
-0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,
-0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,
-0x0e,0x4f,0x66,0x44,0x7b,0xfb,0x00,0x64,0x28,0xfa,0x01,0x60,0x48,0x64,0x29,0xfa,
-0x00,0x64,0x38,0xfa,0x28,0x60,0x9e,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,
-0x16,0x64,0xbd,0xdb,0x02,0x64,0xbd,0xdb,0x06,0x64,0xa3,0xdb,0x1f,0x60,0x14,0x62,
-0x64,0x60,0x3f,0x64,0xa2,0xdb,0x1e,0x60,0xf0,0x62,0x64,0x60,0x4a,0x64,0xa2,0xdb,
-0x2f,0x58,0xff,0xff,0x0f,0x4e,0x00,0x60,0x48,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,
-0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x7a,0xfb,0x08,0x64,
-0x28,0xfa,0xff,0x60,0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0xff,0xff,0x31,0xfa,
-0x32,0xfa,0x33,0xfa,0x12,0x60,0x20,0x64,0x0e,0xfa,0x28,0x60,0x62,0x63,0x00,0x64,
-0xa3,0xdb,0x06,0xa3,0x1e,0x60,0xf6,0x64,0xbd,0xdb,0x04,0x64,0xbd,0xdb,0x06,0x64,
-0xa3,0xdb,0x1e,0x60,0xf4,0x62,0x56,0x60,0x3d,0x64,0xa2,0xdb,0x28,0x60,0x6e,0x63,
-0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1e,0x60,0xfa,0x64,0xbd,0xdb,0x08,0x64,0xbd,0xdb,
-0x06,0x64,0xa3,0xdb,0x1e,0x60,0xf8,0x62,0x56,0x60,0x48,0x64,0xa2,0xdb,0x1e,0x60,
-0xe8,0x62,0x56,0x60,0x2d,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x28,0x60,0x7a,0x63,
-0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x0a,0x64,0xbd,0xdb,0xbd,0xdb,0x06,0x64,
-0xa3,0xdb,0x1f,0x60,0x08,0x62,0x52,0x60,0x9f,0x64,0xa2,0xdb,0x1e,0x60,0xea,0x62,
-0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x00,0x64,0x40,0x40,0x0f,0x4e,
-0x00,0x60,0x6c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,
-0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,
-0x08,0xfe,0x0e,0x4f,0x66,0x44,0x79,0xfb,0x0f,0x4e,0x00,0x60,0x6c,0x61,0x41,0x4d,
-0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,
-0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,
-0x78,0xfb,0x0f,0x4e,0x00,0x60,0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x77,0xfb,0x08,0x64,0x28,0xfa,
-0xf0,0x60,0x20,0x64,0x0e,0xfa,0x00,0x64,0x38,0xfa,0x00,0x60,0x90,0x64,0x29,0xfa,
-0x0f,0x4e,0x00,0x60,0xab,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,
-0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x76,0xfb,0x08,0x64,0x28,0xfa,0x18,0x60,
-0x20,0x64,0x0e,0xfa,0x00,0x60,0x80,0x64,0x29,0xfa,0x00,0x64,0x19,0xfa,0x1e,0x60,
-0xec,0x62,0x43,0x60,0x6e,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xe4,0x62,
-0x2c,0x60,0x33,0x64,0xa2,0xdb,0x28,0x60,0xb6,0x62,0x00,0x64,0xa2,0xdb,0x06,0xa2,
-0x1e,0x60,0xfe,0x64,0xa2,0xdb,0x06,0x64,0x5a,0xdb,0x5a,0xdb,0x28,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x06,0xa2,0x1f,0x60,0x02,0x64,0xa2,0xdb,0x06,0x64,0x5a,0xdb,
-0x5a,0xdb,0x28,0x60,0xce,0x62,0x00,0x64,0xa2,0xdb,0x06,0xa2,0x1f,0x60,0x06,0x64,
-0xa2,0xdb,0x06,0x64,0x5a,0xdb,0x5a,0xdb,0xab,0xf1,0x28,0x60,0xd2,0x62,0xa2,0xd9,
-0x1e,0x60,0xfc,0x62,0x2d,0x60,0x2a,0x64,0xa2,0xdb,0x1f,0x60,0x00,0x62,0x2d,0x60,
-0x35,0x64,0xa2,0xdb,0x1f,0x60,0x04,0x62,0x2d,0x60,0x40,0x64,0xa2,0xdb,0x1e,0x60,
-0xb2,0x62,0x00,0x60,0x02,0x64,0xa2,0xdb,0x29,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x03,0x64,0x5e,0xfb,
-0x2d,0x60,0x58,0x4e,0x1e,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,
-0x8f,0x64,0xa0,0x84,0xa2,0xdb,0xac,0xf1,0x28,0x60,0xc6,0x62,0xa2,0xd9,0x3c,0x60,
-0xb6,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb2,0x62,0x00,0x60,0x74,0x64,0xa2,0xdb,0x29,0x60,0x7c,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x67,0x60,0x76,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,
-0x02,0x2a,0x03,0x00,0x2b,0x60,0x0f,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,
-0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,0xa0,0x80,0x9c,0x84,0x03,0x03,0xa0,0x84,
-0xa2,0xdb,0xd6,0x00,0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,0x0e,0x03,0xa0,0x84,
-0xa2,0xdb,0x01,0x65,0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0xff,0x60,0xf7,0x65,
-0x5e,0xf3,0xff,0xff,0xa4,0x84,0xa2,0xdb,0xc3,0x00,0x00,0x60,0x10,0x64,0xa0,0x80,
-0x9c,0x84,0xc7,0x03,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,
-0xf0,0x65,0xa5,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x1e,0x0d,0xad,0xf3,0xff,0xff,
-0xc0,0x84,0xa5,0xdb,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x66,0x60,0xfc,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf8,0xa0,0xff,0xff,0x01,0x04,
-0x08,0x64,0xa2,0xdb,0x67,0x60,0x02,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa2,0xdb,0x66,0x60,0xf2,0x65,0xa5,0xd1,0x58,0xf3,
-0xff,0xff,0xd0,0x80,0x60,0x41,0x0d,0x06,0xa5,0xdb,0x66,0x60,0xf8,0x63,0x66,0x60,
-0xf6,0x65,0xa5,0xd1,0x61,0x44,0xd0,0x84,0xff,0xff,0x01,0x05,0x00,0x64,0xa3,0xdb,
-0x16,0x00,0x66,0x60,0xf8,0x63,0xa3,0xd1,0x58,0xf3,0xff,0xff,0xd0,0x80,0x60,0x41,
-0x0e,0x05,0xa5,0xdb,0x66,0x60,0xf6,0x65,0xa5,0xd1,0x61,0x44,0xd0,0x84,0xff,0xff,
-0x01,0x05,0x00,0x64,0xa3,0xdb,0x66,0x60,0xfa,0x62,0x01,0x64,0xa2,0xdb,0x66,0x60,
-0xfa,0x63,0xbd,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x2e,0x03,0xbd,0xd3,0xa3,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x28,0x06,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x21,0x03,0x61,0x60,0xc8,0x62,0x06,0x64,0xa2,0xdb,0x66,0x60,
-0xfa,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,
-0xe8,0x84,0xe8,0x84,0xc4,0xfb,0x66,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x67,0x60,0x00,0x63,0xbd,0xd3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x49,0x03,0xbd,0xd3,0xa3,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x43,0x06,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x3c,0x03,
-0x66,0x60,0xf4,0x63,0xbd,0xd1,0xa3,0xd3,0xff,0xff,0xe8,0x84,0xe8,0x85,0x64,0x44,
-0xd4,0x85,0x58,0xf3,0x01,0x05,0x00,0x65,0xd4,0x80,0xff,0xff,0x10,0x06,0x66,0x60,
-0xf6,0x65,0xa5,0xd3,0xff,0xff,0xe8,0x84,0xc2,0xf3,0xe8,0x85,0x58,0xf3,0xc4,0x85,
-0xd4,0x80,0xff,0xff,0x21,0x05,0x61,0x60,0xc8,0x62,0x08,0x64,0xa2,0xdb,0x67,0x60,
-0x00,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,
-0xe8,0x84,0xe8,0x84,0xc4,0xfb,0x66,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0xc2,0xf3,0x58,0xf1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x0d,0x04,0x61,0x60,0xc8,0x62,0x06,0x64,0xa2,0xdb,0x59,0x60,0xb6,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x59,0x60,
-0x9c,0x62,0xa2,0xd1,0xc3,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x0d,0x07,0x61,0x60,
-0xc8,0x62,0x02,0x64,0xa2,0xdb,0x59,0x60,0xb6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x66,0x60,0xba,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0x60,0x45,0x0b,0x03,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xff,0xff,0xd4,0x80,
-0xff,0xff,0x35,0x04,0x66,0x60,0xba,0x62,0x00,0x64,0xa2,0xdb,0x66,0x60,0xb8,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x2a,0x02,0x66,0x60,0xbc,0x62,0xa2,0xd1,
-0x66,0x60,0xbe,0x63,0xa3,0xd3,0xff,0xff,0xd0,0x84,0xfe,0xa0,0xff,0xff,0x1f,0x04,
-0xe0,0x84,0xe0,0x84,0xd0,0x80,0xff,0xff,0x1a,0x04,0x66,0x60,0xbc,0x62,0x64,0x44,
-0x01,0xa4,0xa2,0xdb,0x1e,0x60,0x94,0x62,0x66,0x60,0xba,0x63,0xa2,0xd3,0xff,0xff,
-0x03,0xa4,0xa3,0xdb,0x61,0x60,0xc8,0x62,0x04,0x64,0xa2,0xdb,0x59,0x60,0xb8,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x66,0x60,
-0xb6,0x62,0xa2,0xd1,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x0d,0x04,0x61,0x60,0xc8,0x62,0x04,0x64,0xa2,0xdb,0x59,0x60,0xba,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x67,0x60,0x76,0x62,
-0xa2,0xd3,0xff,0xff,0x60,0x40,0x03,0x22,0x11,0x00,0x60,0x45,0xfd,0xb4,0xa2,0xdb,
-0x20,0x44,0xb4,0x84,0x40,0x40,0x61,0x60,0xc8,0x62,0x10,0x64,0xa2,0xdb,0x59,0x60,
-0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x37,0x00,0x1e,0x60,0xb2,0x62,
-0x00,0x60,0x64,0x64,0xa2,0xdb,0x2b,0x60,0x32,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,
-0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,
-0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x29,0x60,0xb5,0x78,0xff,0xff,
-0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,0xe2,0x03,0xa0,0x84,0xa2,0xdb,0x01,0x65,
-0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0xff,0x60,0xf7,0x65,0x5e,0xf3,0xff,0xff,
-0xa4,0x84,0xa2,0xdb,0x29,0x60,0xb5,0x78,0xff,0xff,0xad,0xf1,0x28,0x60,0xc6,0x62,
-0xa2,0xd9,0x2b,0x60,0x68,0x64,0x7c,0xfb,0x2c,0x60,0x68,0x78,0xff,0xff,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,0xdf,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,
-0x92,0x65,0xf4,0x56,0x7e,0x00,0x00,0x10,0x66,0x60,0xee,0x63,0x00,0x64,0xbd,0xdb,
-0xa5,0xd3,0xa3,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xf0,0xa0,0xfc,0xa0,
-0x08,0x03,0x13,0x02,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x0c,0x03,0x3c,0x60,0xb2,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x29,0x60,0x5d,0x78,0xff,0xff,0x88,0xf1,0x27,0x60,0xe0,0x63,
-0xd3,0x80,0xc4,0xf1,0x25,0x03,0x00,0x64,0xc4,0xfb,0x66,0x60,0xf4,0x65,0x58,0xf3,
-0xa5,0xdb,0xa3,0xd3,0xc0,0x85,0xd4,0x80,0x5b,0xd3,0x1a,0x04,0x60,0x43,0x63,0x42,
-0x06,0x65,0x46,0xd3,0x5a,0xd3,0x40,0x48,0x5a,0xd3,0x40,0x4c,0x40,0x4d,0x81,0xf3,
-0x28,0x45,0xd4,0x80,0x5a,0xd3,0x09,0x02,0x2c,0x45,0xd4,0x80,0x5a,0xd3,0x05,0x02,
-0x2d,0x45,0xd4,0x80,0x63,0x42,0x01,0x02,0x03,0x00,0x2c,0x60,0x2a,0x78,0xff,0xff,
-0xc2,0xf1,0x58,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x14,0x06,0x59,0x60,0x9c,0x62,
-0xa2,0xd3,0xc3,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x0c,0x05,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x29,0x60,
-0x5d,0x78,0xff,0xff,0x1e,0x60,0xb2,0x62,0x00,0x60,0x74,0x64,0xa2,0xdb,0x2b,0x60,
-0xeb,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,
-0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,
-0xa2,0xdb,0x2b,0x60,0x9b,0x78,0xff,0xff,0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,
-0x10,0x03,0xa0,0x84,0xa2,0xdb,0x01,0x65,0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,
-0xff,0x60,0xf7,0x65,0x5e,0xf3,0xff,0xff,0xa4,0x84,0xa2,0xdb,0x2b,0x60,0x9b,0x78,
-0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0xcd,0x03,0xa0,0x84,0xa2,0xdb,
-0x3c,0x60,0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,
-0x1d,0xff,0x2b,0x60,0x5e,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,
-0x20,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0xb6,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x00,0x64,0x5e,0xfb,0x1e,0x60,
-0xa8,0x62,0xa2,0xd1,0x08,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x1e,0x60,
-0xb2,0x62,0x00,0x60,0x02,0x64,0xa2,0xdb,0x29,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb2,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x2c,0x60,
-0x71,0x64,0x5a,0xdb,0xcf,0xfe,0x8c,0xf3,0xff,0xff,0xff,0xa0,0x02,0x64,0x2a,0x02,
-0x8c,0xfb,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,
-0x1e,0x60,0xb2,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x2c,0x60,0x8a,0x64,0x5a,0xdb,
-0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,
-0xff,0x64,0xa0,0x84,0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,
-0xa1,0x84,0x5a,0xdb,0x20,0x44,0x40,0x2a,0x10,0x00,0x20,0xbc,0x40,0x40,0x11,0x60,
-0x48,0x65,0x2f,0x60,0x58,0x4e,0x23,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x14,0x78,
-0xff,0xff,0x2f,0x60,0x58,0x4e,0x42,0x78,0xff,0xff,0x59,0x60,0x20,0x64,0x5b,0xfb,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xef,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x0f,0x4e,
-0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,0xb2,0x62,0x10,0x60,
-0x00,0x64,0xa2,0xdb,0x2c,0x60,0xce,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x20,0x44,0x20,0x2a,0x10,0x00,0xdf,0xb4,0x40,0x40,0x01,0x60,0x48,0x65,0x2f,0x60,
-0x58,0x4e,0x23,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x2f,0x60,
-0x58,0x4e,0x42,0x78,0xff,0xff,0x3c,0x60,0xb6,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xef,0x60,
-0xef,0x64,0xa0,0x84,0xa2,0xdb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x7c,0xf7,
-0xff,0xff,0xff,0xff,0x59,0xf1,0x28,0x44,0xd0,0x84,0x0f,0xa4,0x03,0x0e,0xe8,0x84,
-0xe8,0x84,0x04,0x00,0xe2,0xa4,0xe8,0x84,0xe8,0x87,0xf0,0xbf,0xc0,0x84,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x5a,0xf1,0x28,0x44,0xd0,0x84,0x1f,0xa4,0x06,0x0e,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x07,0x00,0xc2,0xa4,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe8,0x87,0xf8,0xbf,0xc0,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x5a,0xf1,0x59,0xf3,0x64,0x45,0xd4,0x84,0x80,0x65,0xc4,0x87,0x01,0x05,0x00,0x64,
-0xff,0xb4,0x58,0xfb,0x2e,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,
-0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xb0,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x2e,0x60,0xc1,0x78,0xff,0xff,
-0xcb,0xf3,0xff,0xff,0x60,0x40,0x03,0x36,0x2e,0x00,0x1e,0x60,0xb2,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x40,0x40,0x22,0xf1,0x00,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0xff,
-0xff,0xa0,0xff,0xff,0x11,0x02,0x26,0x46,0x00,0xf4,0x57,0x60,0x58,0x4e,0x59,0x78,
-0xff,0xff,0x66,0x60,0xbe,0x62,0xa2,0xd9,0x66,0x60,0xbc,0x63,0xa3,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x01,0x06,0xa3,0xd9,0x58,0x60,0x58,0x4e,0x6c,0x78,0xff,0xff,
-0x65,0x44,0x00,0xa0,0xff,0xff,0x01,0x03,0xe1,0xfb,0x00,0x65,0x2e,0x60,0x58,0x4e,
-0xd4,0x78,0xff,0xff,0x09,0x00,0x6d,0xf3,0xff,0xff,0x04,0xb4,0x04,0xbc,0x03,0x03,
-0x2e,0x60,0xc1,0x78,0xff,0xff,0x6d,0xfb,0x26,0x46,0x20,0xf2,0xa0,0x65,0x01,0x37,
-0x50,0x65,0x02,0x37,0x1e,0x65,0x03,0x37,0x0f,0x65,0x28,0x60,0xda,0x63,0x00,0xf4,
-0x02,0xf2,0xff,0xff,0xd4,0x84,0xbd,0xdb,0x03,0xf2,0x01,0x05,0xcc,0x84,0xbd,0xdb,
-0x04,0xf2,0x01,0x05,0xcc,0x84,0xbd,0xdb,0x05,0xf2,0x01,0x05,0xcc,0x84,0xa3,0xdb,
-0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,0x23,0xf0,0x00,0x61,0xd0,0x84,
-0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x24,0xf0,
-0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,
-0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x28,0xf0,0x01,0x03,0xcc,0x84,
-0xd0,0x84,0xa3,0xdb,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x92,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x5e,0xf3,0xcb,0xf3,0x04,0xb0,0xff,0xff,
-0x35,0x03,0x03,0x3a,0x35,0x00,0x73,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,0x30,0x00,
-0xa3,0xd3,0x4b,0xd1,0x4b,0xd3,0xc0,0x9c,0xc0,0x84,0x4b,0xd1,0x00,0xa0,0x03,0xa0,
-0x01,0x03,0x1f,0x02,0x80,0x60,0x00,0x65,0x64,0x44,0xa4,0x85,0xe8,0x84,0xb4,0x84,
-0xe8,0x84,0xb4,0x84,0xa3,0xdb,0x60,0x45,0xfa,0x64,0xd4,0x80,0xff,0x60,0x06,0x64,
-0xd4,0x80,0x14,0x07,0x13,0x04,0x66,0x60,0xa0,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x4d,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x03,0x00,0x01,0x64,0x4d,0xfb,
-0x07,0x00,0x66,0x60,0x9a,0x62,0x10,0x64,0xa2,0xdb,0x06,0x00,0x00,0x64,0x4d,0xfb,
-0x01,0x64,0x23,0xfb,0xff,0xff,0x1a,0xff,0x5e,0xf3,0xff,0xff,0x04,0xb0,0x08,0xb0,
-0x06,0x03,0x7e,0x02,0x5f,0xf3,0xff,0xff,0xff,0xa4,0x5f,0xfb,0x7b,0x02,0x26,0x46,
-0x00,0xf4,0x02,0xf2,0x5a,0xd2,0x40,0x48,0x40,0x4c,0x5a,0xd2,0x5a,0xd2,0x40,0x4d,
-0x60,0x41,0x5a,0xd0,0x80,0xf9,0x40,0x63,0xad,0x80,0xf0,0xa3,0x09,0x02,0x3c,0x03,
-0x2d,0x41,0x2c,0x44,0x40,0x4d,0x28,0x44,0x40,0x4c,0x00,0x64,0x40,0x48,0xf4,0x00,
-0xd1,0x80,0x01,0x02,0x31,0x04,0x10,0xa3,0x80,0x60,0x00,0x65,0xa5,0x80,0xcf,0x83,
-0x08,0x02,0x28,0x44,0x60,0x88,0x2c,0x44,0x70,0x8c,0x2d,0x44,0x70,0x8d,0xf1,0x81,
-0xf5,0x00,0xe7,0xa3,0x64,0x44,0x00,0xa0,0x00,0x62,0x02,0x02,0x00,0x61,0x1c,0x00,
-0xe0,0x84,0xde,0x82,0xfd,0x04,0x42,0xfe,0xf8,0x84,0x62,0x45,0xc7,0x83,0x60,0x45,
-0x02,0xfe,0xd5,0x84,0x02,0x05,0x01,0x05,0x61,0x44,0xcf,0x83,0x60,0x41,0x08,0x03,
-0x28,0x44,0x60,0x88,0x2c,0x44,0x70,0x8c,0x2d,0x44,0x70,0x8d,0xf1,0x81,0xf1,0x00,
-0xce,0x82,0xe9,0x81,0xfd,0x02,0xf1,0x81,0x02,0xf2,0xff,0xff,0x60,0x47,0xe8,0x84,
-0xe8,0x84,0x5a,0xd2,0x3f,0xb5,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xb4,0x84,0x61,0x45,0xd4,0x84,0xc0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x60,0x53,0x80,0xf3,0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x61,0x44,0xd4,0x84,0xa4,0xfb,0x04,0x60,0x00,0x71,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x95,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x05,0x64,0x5f,0xfb,0x5e,0xf3,0x26,0x46,0x0c,0xbc,0xa2,0xdb,0xff,0x60,0x00,0x65,
-0x25,0xf2,0xff,0xff,0x24,0x88,0x60,0x47,0x24,0x8c,0x2c,0x60,0x58,0x4e,0xf8,0x78,
-0xff,0xff,0x0c,0x48,0x2d,0x60,0x58,0x4e,0x08,0x78,0xff,0xff,0x2d,0x60,0x58,0x4e,
-0x1e,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x40,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x3c,0x60,0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,
-0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,
-0xdf,0x64,0xa0,0x84,0xa2,0xdb,0x26,0x46,0x2f,0x58,0xff,0xff,0x65,0x44,0x00,0xa0,
-0x40,0x48,0x13,0x03,0x66,0x60,0x2a,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf6,0xa0,
-0xa2,0xdb,0x0f,0x04,0x00,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,
-0x40,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x66,0x60,0x2a,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x60,0x2c,0x62,0xa2,0xd3,0x28,0x45,0xc4,0x84,0xa2,0xdb,0x66,0x60,
-0x2e,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xce,0xa0,0xa2,0xdb,0x14,0x06,0x32,0x64,
-0xa2,0xdb,0x66,0x60,0x30,0x62,0xa2,0xd1,0x66,0x60,0x32,0x64,0xc0,0x82,0xa2,0xd1,
-0x66,0x60,0x2c,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x84,0xa2,0xdb,0xe0,0x85,0x59,0x60,
-0x9c,0x62,0x65,0x44,0xa2,0xdb,0x66,0x60,0x30,0x62,0xa2,0xd1,0x66,0x60,0x32,0x64,
-0xc0,0x82,0x28,0x44,0xa2,0xdb,0x66,0x60,0x30,0x62,0x64,0x44,0x9e,0xa0,0x02,0xa4,
-0x01,0x02,0x00,0x64,0xa2,0xdb,0x2e,0x58,0xff,0xff,0x7b,0xf5,0xff,0xff,0x81,0xf1,
-0x2b,0xf8,0x31,0xf8,0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,
-0x2d,0xf8,0x33,0xf8,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,
-0xbf,0xf1,0x30,0xf8,0xff,0xff,0x00,0x64,0x22,0xfa,0x06,0x60,0x20,0x64,0x0e,0xfa,
-0x65,0x44,0x29,0xfa,0x2e,0x58,0xff,0xff,0x66,0x60,0xcc,0x62,0x2e,0x44,0xa2,0xdb,
-0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,
-0x1e,0x60,0xb2,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x2f,0x60,0x61,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,0xfe,0x61,
-0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x66,0x60,0xcc,0x62,0xa2,0xd3,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x27,0x42,0xa2,0xd3,0xa2,0xd1,0xac,0x86,
-0x0e,0xf2,0x57,0x03,0x60,0x40,0x02,0x2a,0x54,0x00,0x0b,0xf2,0xff,0xff,0x00,0xa4,
-0x44,0x45,0x39,0x02,0x21,0x44,0xf7,0xa0,0xff,0xff,0x35,0x07,0x5c,0x81,0x22,0x44,
-0x00,0x7c,0xd0,0x80,0xff,0xff,0x01,0x02,0x46,0x42,0x48,0xf3,0xff,0xff,0x60,0x41,
-0x02,0xfa,0x40,0xa1,0x7c,0x63,0x84,0xa1,0x00,0xf2,0x03,0x06,0x01,0xfc,0x60,0x46,
-0xfa,0x00,0x80,0x60,0x7c,0x64,0x01,0xfa,0x66,0x43,0x25,0x46,0x05,0xfc,0x06,0xfc,
-0x01,0xf0,0x03,0x67,0xb0,0x84,0x00,0xf0,0x3c,0x7e,0x01,0xfa,0x04,0x64,0x03,0xfa,
-0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,0xff,0xff,0x0e,0xfa,0x0f,0xfa,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x64,0x64,0xa2,0xdb,0x25,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xb9,0x00,0x0f,0x4e,0x44,0x45,0x64,0x46,0x3c,0x60,0x88,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0xa3,0x00,0x2f,0x58,0xff,0xff,0x3c,0x60,0x6a,0x64,0x40,0x47,0x58,0x4f,0x9c,0x00,
-0x3c,0x60,0x46,0x64,0x40,0x47,0x58,0x4f,0x08,0x00,0x3c,0x60,0x70,0x64,0x40,0x47,
-0x58,0x4f,0x03,0x00,0x30,0x60,0x7f,0x78,0xff,0xff,0x27,0x42,0xa2,0xd3,0xa2,0xd1,
-0xac,0x86,0x0e,0xf2,0x46,0x03,0x60,0x40,0x02,0x2a,0x43,0x00,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x28,0x03,0x3c,0x60,0x46,0x64,0x27,0x45,0xd4,0x80,0xff,0xff,
-0x22,0x02,0x00,0x64,0x13,0xfb,0x22,0xf2,0xff,0xff,0xff,0xff,0x10,0x26,0x0f,0x00,
-0x1c,0xf2,0xff,0xff,0x03,0xb4,0xff,0xff,0x00,0x36,0x15,0x00,0x01,0x36,0x13,0x00,
-0x02,0x36,0x05,0x00,0x22,0xf2,0xff,0xff,0x00,0xa8,0xff,0xff,0x0c,0x03,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x40,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x16,0x00,0x0f,0x4e,0x44,0x45,0x64,0x46,0x3c,0x60,0x88,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0xb4,0x00,0x2f,0x58,0xff,0xff,0x02,0x64,0x01,0x00,0x01,0x64,0x40,0x55,0x3b,0xff,
-0x48,0x00,0xb2,0xfe,0xff,0xff,0xf9,0x05,0xb3,0xfe,0xff,0xff,0xf4,0x05,0xb0,0xfe,
-0xff,0xff,0x91,0x05,0xb1,0xfe,0xff,0xff,0x26,0x05,0x3b,0x00,0x48,0xf1,0x0f,0x4e,
-0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,
-0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,
-0x0e,0x4f,0x26,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x64,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x22,0x41,0x00,0xb9,0x21,0x44,
-0x08,0x24,0x46,0x42,0x5c,0x81,0x3e,0x41,0x22,0x44,0x00,0xb9,0xac,0x86,0x09,0x02,
-0xd5,0x03,0x31,0x40,0x01,0x2a,0x05,0x00,0x09,0xf0,0x02,0x5e,0x44,0x42,0x21,0x44,
-0x4c,0x81,0x96,0xf3,0x21,0x45,0xd4,0x80,0xff,0xff,0xc8,0x07,0x58,0x4f,0x04,0x00,
-0x00,0x00,0xa1,0xff,0xff,0xff,0xbe,0x3f,0x3c,0x60,0x34,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x40,0x03,0x46,0x48,0x03,0x60,0x3c,0x64,0x01,0xfa,0x02,0xf0,
-0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,
-0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,
-0x08,0xfe,0x0e,0x4f,0x76,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x3a,0x64,0xa2,0xdb,
-0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0xf2,0x00,0x63,
-0x00,0xfc,0x05,0xf0,0x06,0xfc,0x66,0x43,0x05,0xfc,0x28,0x46,0x00,0xfa,0x04,0xfa,
-0x04,0x64,0x03,0xfa,0x05,0xf8,0x06,0xf8,0x08,0x64,0x0e,0xfa,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x40,0x64,0xa2,0xdb,0x28,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd1,0xfe,0x4b,0x00,0x20,0x44,0x01,0x2a,0x4b,0x00,0x02,0x2a,0x22,0x00,
-0x0f,0x4e,0x00,0x60,0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,
-0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x08,0xfe,0x0e,0x4f,0x35,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x3a,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x20,0x44,
-0xfd,0xb4,0x40,0x40,0x91,0xf1,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,
-0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x13,0x03,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x40,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x64,0x0e,0xfa,0x20,0x44,0xfe,0xb4,0x40,0x40,0x04,0x64,0x40,0x55,
-0x3b,0xff,0x2f,0x58,0xff,0xff,0xb8,0xfe,0xff,0xff,0x02,0x24,0x97,0xf7,0xff,0xff,
-0xff,0xff,0xba,0xfe,0xff,0xff,0x05,0x05,0xb9,0xfe,0xbb,0xfe,0x30,0x60,0x7f,0x78,
-0xff,0xff,0x36,0x44,0x00,0x7f,0xfc,0xa0,0x60,0x45,0x05,0x05,0x0e,0x60,0xd4,0x64,
-0x44,0xd7,0xff,0xff,0xff,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0x7f,0x60,0xc0,0x64,
-0x24,0x45,0xa4,0x80,0x7f,0x67,0x02,0x61,0x13,0x02,0x20,0x44,0x01,0x2a,0x03,0x00,
-0x7f,0x67,0x07,0x61,0x0d,0x00,0x48,0xf1,0x25,0x44,0x64,0x45,0x91,0xfb,0xd4,0x80,
-0x7f,0x67,0x05,0x61,0x05,0x07,0x20,0x44,0x03,0xbc,0x40,0x40,0xd1,0xfe,0x00,0x67,
-0x23,0x58,0xff,0xff,0x24,0x44,0x36,0x60,0x58,0x4f,0x2b,0x78,0xff,0xff,0x03,0x61,
-0x03,0x03,0x31,0x60,0xfa,0x78,0xff,0xff,0x24,0x44,0x40,0xb0,0xff,0xff,0x48,0x03,
-0x25,0x46,0x66,0x5c,0xd1,0xf9,0x0e,0xf0,0xff,0xff,0x64,0x40,0x08,0x2a,0x1f,0x00,
-0x3c,0x60,0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x18,0x03,0x0f,0x4e,
-0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x0e,0xf2,0xff,0xff,0xf7,0xb4,0x0e,0xfa,0xd1,0xf5,
-0x22,0xf0,0xff,0x60,0xef,0x64,0xa0,0x84,0xa2,0xda,0x00,0x64,0x1c,0xfa,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x0e,0xf2,0xff,0xff,0x02,0xbc,0x0e,0xfa,0x3c,0x60,0x46,0x64,
-0x40,0x47,0x2f,0x60,0x58,0x4f,0xe3,0x78,0xff,0xff,0x32,0x60,0xf0,0x78,0xff,0xff,
-0x59,0x60,0xe4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x25,0x46,0x3c,0x60,0x28,0x65,0x08,0xf2,
-0xff,0xff,0xd4,0x80,0x03,0x61,0x40,0x03,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,
-0x25,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x28,0xf0,0xfd,0x7f,
-0x04,0x7e,0x64,0x40,0x02,0x26,0x40,0xbc,0x0e,0xf0,0x64,0x40,0x04,0x26,0x80,0xbc,
-0xc0,0x22,0xff,0x7f,0x64,0x40,0x08,0x26,0x08,0xbc,0x0e,0xfa,0x3f,0xf0,0xff,0xff,
-0x28,0xf2,0x38,0xf2,0x60,0x41,0x08,0x2a,0x64,0x47,0x38,0xfa,0x60,0x45,0x49,0xf3,
-0x00,0x63,0xd4,0x80,0x22,0xfc,0x01,0x04,0x07,0x00,0x22,0xf0,0x08,0x64,0xb0,0x84,
-0xa2,0xda,0x32,0x60,0x08,0x78,0xff,0xff,0x39,0x60,0x58,0x4f,0x90,0x78,0xff,0xff,
-0x05,0x04,0x22,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0x14,0x00,0x32,0x60,0x58,0x4f,
-0xf5,0x78,0xff,0xff,0x05,0x61,0x03,0x04,0x32,0x60,0xf2,0x78,0xff,0xff,0x25,0x46,
-0xcb,0xf3,0x95,0xf3,0xfe,0xa0,0x00,0xa0,0x05,0x07,0x04,0x02,0x22,0xf0,0x04,0x64,
-0xb0,0x84,0xa2,0xda,0x24,0x44,0x01,0x2b,0x3a,0x00,0x02,0x27,0x38,0x00,0x3c,0x60,
-0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x46,0x48,0x10,0x63,0x25,0x46,
-0xbf,0xd0,0x28,0x46,0xff,0xd8,0x25,0x46,0xfb,0x1d,0x64,0x44,0x00,0xa8,0x28,0x44,
-0x03,0x02,0x28,0x46,0x05,0xfa,0x06,0xfa,0x16,0x63,0x6a,0x61,0x25,0x46,0xa3,0xd0,
-0x28,0x46,0xc9,0x81,0xbd,0xd8,0xfa,0x02,0x0e,0xf0,0xff,0x60,0xfc,0x64,0xa0,0x84,
-0x0e,0xfa,0x00,0x64,0x0f,0xfa,0x00,0x64,0x25,0x46,0x00,0xfa,0x66,0x44,0x05,0xfa,
-0x3c,0x60,0x88,0x62,0x3c,0x60,0x34,0x64,0xa2,0xdb,0x25,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xd1,0xfe,0x00,0x64,0x0e,0xfa,0x28,0x46,0x0e,0xf0,
-0xff,0x60,0xfb,0x64,0xa0,0x84,0x0e,0xfa,0x22,0xf2,0x66,0x43,0x00,0xa8,0x60,0x5c,
-0x08,0x60,0x0a,0x64,0xa0,0xdd,0x64,0x44,0x69,0x02,0x95,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x44,0x03,0x26,0x44,0x0a,0x36,0x00,0x63,0x14,0x36,0x01,0x63,0x37,0x36,
-0x02,0x63,0x6e,0x36,0x03,0x63,0x13,0xfc,0x26,0x44,0xff,0x27,0x06,0x00,0x26,0xf2,
-0x26,0xf2,0x60,0x45,0x60,0x47,0xd4,0x84,0x01,0x00,0x60,0x47,0xff,0x65,0xa4,0x84,
-0x1d,0xfa,0x00,0x64,0x15,0xfa,0x27,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x15,0x02,
-0x1e,0x60,0x98,0x65,0x25,0xf2,0xff,0xff,0x0f,0xb4,0xb8,0xf1,0x00,0x7f,0xd0,0x80,
-0x60,0x5c,0x06,0x05,0x67,0x60,0x6e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0xa5,0xd9,0x64,0x40,0x00,0x3a,0xb8,0xf9,0x11,0x00,0x29,0xf0,0x08,0x67,0xb0,0x84,
-0xa2,0xda,0x1e,0x65,0x29,0xf2,0xff,0xff,0x60,0x40,0x03,0x2b,0x18,0x65,0x65,0x44,
-0x04,0xa4,0x64,0x40,0x40,0x27,0x08,0xa4,0x21,0xfa,0x08,0x00,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x72,0x78,0xff,0xff,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0x47,0xf3,0x03,0x03,0x24,0x47,0x0f,0xb4,0x02,0x00,0xe8,0x84,0xe8,0x84,
-0x1c,0xfa,0x3c,0x60,0x88,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x0c,0x00,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd3,0xfe,0x0e,0xf0,0x24,0x44,0x02,0x27,0x02,0x00,0x01,0x27,0x22,0x00,
-0x64,0x40,0x08,0x2a,0x1f,0x00,0x3c,0x60,0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,
-0x60,0x46,0x18,0x03,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,
-0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,
-0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x0e,0xf2,0xff,0xff,
-0xf7,0xb4,0x0e,0xfa,0x00,0x67,0x01,0x00,0x7f,0x67,0x23,0x58,0xff,0xff,0x0f,0x4e,
-0x25,0x46,0x38,0xf2,0x05,0x48,0x00,0xa8,0x60,0x41,0x66,0x44,0x0a,0x03,0x00,0xf2,
-0x42,0xfe,0xac,0x86,0x01,0xf2,0x1e,0x03,0x7f,0xb5,0xd5,0x81,0x66,0x44,0xf7,0x07,
-0x25,0x46,0x05,0xf0,0x06,0xfa,0x05,0xfa,0xd0,0x80,0x64,0x43,0x12,0x03,0x60,0x46,
-0x01,0xf0,0x80,0x67,0xb0,0x84,0x01,0xfa,0x00,0xf0,0x00,0x64,0x00,0xfa,0x64,0x46,
-0x05,0xfc,0x46,0x45,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,
-0x08,0x45,0x02,0xfe,0x2e,0x58,0xff,0xff,0x20,0x44,0x40,0xb0,0x7f,0x67,0x02,0x61,
-0x03,0x03,0x34,0x60,0x2d,0x78,0xff,0xff,0x00,0x64,0x24,0x45,0x80,0x26,0x01,0x64,
-0x95,0xfb,0x59,0x60,0x54,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x4a,0xf1,
-0x43,0xf1,0x64,0x40,0x01,0x2a,0x08,0x00,0x64,0x40,0x01,0x2a,0x05,0x00,0x1e,0x60,
-0xa0,0x63,0x09,0x60,0x2b,0x64,0x19,0x00,0xc7,0xf1,0x1e,0x60,0xa0,0x63,0x64,0x45,
-0x80,0x27,0x19,0x00,0x64,0x44,0x00,0xac,0xff,0xff,0x0d,0x02,0x02,0x60,0x52,0x64,
-0xbd,0xdb,0x03,0x60,0x1c,0x64,0xbd,0xdb,0x7f,0x60,0xff,0x64,0xbd,0xdb,0x7f,0x60,
-0xff,0x64,0xbd,0xdb,0x07,0x00,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,
-0xff,0xff,0xbd,0xdb,0x36,0x00,0x80,0x67,0x94,0x81,0x61,0x44,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0x61,0x44,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe0,0x84,0xbd,0xdb,0x0d,0x60,0x18,0x65,0x61,0x44,0xd4,0x80,0xff,0xff,0x01,0x06,
-0x65,0x44,0xe0,0x85,0xc4,0x85,0xe0,0x84,0xf4,0x66,0x7e,0x00,0x00,0x10,0xe0,0x84,
-0xe0,0x84,0xc4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe0,0x84,
-0xbd,0xdb,0x06,0x60,0x8c,0x65,0x61,0x44,0xd4,0x80,0xff,0xff,0x01,0x06,0x65,0x44,
-0xe0,0x85,0xc4,0x85,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc4,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0x1e,0x60,0xa0,0x63,0x04,0x61,0xbd,0xd1,
-0x90,0x65,0x64,0x44,0xd4,0x80,0x65,0x44,0x01,0x05,0xbf,0xdb,0x09,0x60,0x2b,0x65,
-0x64,0x44,0xd4,0x80,0xcd,0x81,0x02,0x06,0x65,0x44,0xbf,0xdb,0xf0,0x02,0x00,0x61,
-0x41,0x56,0xc7,0xfe,0x30,0x60,0x7f,0x78,0xff,0xff,0x36,0x47,0xff,0x23,0x06,0x00,
-0x00,0x7f,0x60,0x41,0x7f,0x67,0x34,0x60,0x2d,0x78,0xff,0xff,0x99,0xff,0x00,0x60,
-0x00,0xeb,0x00,0x60,0x00,0xea,0x98,0xff,0x20,0x44,0x80,0xbc,0x40,0x40,0x59,0x60,
-0x6a,0x63,0xc9,0xf3,0xa3,0xdb,0x00,0x63,0x60,0x40,0x01,0x26,0x09,0x00,0x01,0xa3,
-0x60,0x40,0x02,0x26,0x05,0x00,0x01,0xa3,0x60,0x40,0x04,0x26,0x01,0x00,0x01,0xa3,
-0x60,0x41,0x17,0x60,0xde,0x65,0xa5,0xdd,0x61,0x44,0x08,0x2a,0x03,0x00,0x03,0x63,
-0x08,0x64,0x0c,0x00,0x04,0x2a,0x03,0x00,0x02,0x63,0x04,0x64,0x07,0x00,0x02,0x2a,
-0x03,0x00,0x01,0x63,0x02,0x64,0x02,0x00,0x00,0x63,0x01,0x64,0x50,0xfb,0x51,0xfd,
-0x95,0xf3,0xff,0xff,0x00,0xa0,0x00,0x64,0x2d,0x03,0xa1,0xfb,0xa2,0xfb,0xa3,0xfb,
-0xff,0xff,0x80,0xf3,0x88,0xff,0x00,0x75,0x00,0x72,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x60,0x53,0xed,0xe2,0xbf,0xf3,0xff,0xff,0xff,0xb4,0x60,0x52,0x8a,0xff,
-0xbd,0xf1,0x81,0xf9,0xbe,0xf1,0xff,0xff,0x82,0xf9,0xbf,0xf1,0x83,0xf9,0x17,0x60,
-0xdc,0x63,0xa3,0xd3,0x00,0x65,0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,0x04,0x26,
-0x02,0x65,0x60,0x40,0x08,0x26,0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,0xa2,0xdb,
-0x00,0x67,0x10,0x00,0xc9,0xf3,0x00,0x65,0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,
-0x04,0x26,0x02,0x65,0x60,0x40,0x08,0x26,0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,
-0xa2,0xdb,0x00,0x67,0x23,0x58,0xff,0xff,0x7f,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,
-0x7f,0x67,0x02,0x61,0x3a,0x02,0x59,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,
-0xa2,0xdb,0xff,0x60,0xfe,0x64,0x32,0x45,0x24,0x92,0x02,0x61,0x41,0x56,0xc7,0xfe,
-0x30,0x60,0x7f,0x78,0xff,0xff,0x94,0xf1,0x20,0x44,0x64,0x40,0xff,0x26,0x24,0x00,
-0x7f,0xb4,0x40,0x40,0x00,0x64,0x40,0x5e,0x3c,0x60,0x64,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x0f,0xf2,0x18,0x03,0x00,0xa8,0xff,0xff,0x15,0x03,0x0f,0x4e,
-0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0xe0,0x00,0x00,0x67,0x23,0x58,0xff,0xff,0x00,0x61,
-0x00,0x7c,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,0x25,0x44,
-0xa0,0xd1,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,0x7f,0x60,
-0xc0,0x64,0x24,0x45,0xa4,0x80,0x02,0x61,0x25,0x02,0x25,0x45,0x12,0x60,0xfc,0x63,
-0x05,0x61,0xbd,0xd3,0xbd,0xd1,0xd4,0x80,0xbd,0xd3,0xcd,0x81,0x02,0x03,0x19,0x03,
-0xf8,0x00,0x40,0x4c,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x01,0x03,0x2c,0x58,0x0c,0x61,0x05,0x67,
-0x02,0x00,0x04,0x61,0x7f,0x67,0x23,0x58,0xff,0xff,0x03,0x4e,0x0c,0x60,0x6e,0x62,
-0xa2,0xd7,0x58,0x43,0xff,0xff,0x0e,0x43,0x41,0x47,0x7e,0x60,0xc0,0x64,0x24,0x45,
-0xa4,0x80,0x02,0x61,0x26,0x02,0x25,0x45,0xfc,0x2b,0x22,0x00,0x0e,0x60,0xd8,0x63,
-0x6a,0x61,0x24,0x44,0x01,0x27,0x11,0x00,0xbd,0xd3,0xa3,0xd1,0xd4,0x80,0xcd,0x81,
-0x08,0x24,0x64,0x58,0x08,0xa3,0xf8,0x02,0x15,0xf5,0x22,0xf2,0xff,0xff,0x00,0xa8,
-0x00,0x61,0x01,0x02,0x04,0x61,0x00,0x67,0x0d,0x00,0x27,0x40,0x04,0x3a,0xfb,0x00,
-0xbd,0xd3,0xbe,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,0x08,0xa3,0xf5,0x02,
-0x04,0x61,0x7f,0x67,0x23,0x58,0xff,0xff,0x4b,0xd3,0x15,0xf5,0x60,0x41,0x22,0xf0,
-0xe9,0x85,0x64,0x44,0xff,0x22,0xdc,0x84,0xc4,0x84,0x22,0xfa,0x64,0x44,0xc2,0x82,
-0x00,0xa8,0xc2,0x84,0x08,0x24,0xd8,0x84,0xbf,0xd1,0xd8,0x85,0x64,0x43,0x58,0x4f,
-0x61,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x67,0x60,0x6a,0x62,0x01,0x64,
-0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x05,0x00,0x01,0x64,0x90,0xfb,0x01,0x67,0x85,0xfb,0xff,0xff,
-0x15,0xf5,0xff,0xff,0x22,0xf2,0xbf,0xd1,0xff,0xff,0x62,0x43,0xcc,0x84,0xe0,0x85,
-0x09,0x06,0xbf,0xd1,0x64,0x41,0xd5,0x80,0x64,0x43,0x01,0x06,0x65,0x41,0x48,0x65,
-0x58,0x4f,0x55,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x58,0x60,0x5a,0x63,
-0xa3,0xd3,0x15,0xf5,0x60,0x41,0xe8,0x84,0xdc,0x84,0x22,0xfa,0xfc,0x60,0x80,0x64,
-0x5a,0xda,0xda,0x85,0x04,0xa3,0x58,0x4f,0x25,0x00,0x00,0x67,0x00,0x61,0x23,0x58,
-0xff,0xff,0x15,0xf5,0x22,0xf2,0xbf,0xd1,0xff,0xff,0x62,0x43,0xcc,0x84,0xe0,0x81,
-0x15,0x06,0xbf,0xd1,0x64,0x45,0xd5,0x80,0x64,0x43,0xfc,0xa3,0x04,0x06,0x65,0x41,
-0xe9,0x84,0xdc,0x84,0x22,0xfa,0x44,0x65,0x04,0xa1,0x58,0x4f,0x28,0x00,0x58,0x60,
-0x5a,0x62,0xa2,0xd3,0xff,0xff,0xcc,0x84,0xe0,0x84,0xa2,0xdb,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x41,0x4d,0x00,0xa1,0x80,0x64,0x17,0x03,0x65,0x42,0xd4,0x85,
-0x2d,0x41,0x55,0x8d,0xff,0xff,0x02,0x04,0x65,0x41,0x02,0x00,0x00,0x64,0x40,0x4d,
-0xca,0x84,0xbd,0xd1,0xc9,0x81,0x58,0xd8,0xfc,0x02,0x2d,0x41,0x00,0xa1,0xd8,0x85,
-0x04,0x03,0x00,0xf4,0x7c,0x65,0x04,0x62,0xeb,0x00,0x2f,0x58,0xff,0xff,0x41,0x4d,
-0x01,0xf2,0x65,0x42,0x7f,0xb5,0x2d,0x41,0x00,0xa1,0x55,0x8d,0x0e,0x03,0x02,0x04,
-0x65,0x41,0x02,0x00,0x00,0x64,0x40,0x4d,0xca,0x84,0x58,0xd0,0xc9,0x81,0xbd,0xd9,
-0xfc,0x02,0x00,0xf4,0x01,0xf2,0x04,0x62,0xed,0x00,0x2f,0x58,0xff,0xff,0x66,0x44,
-0x93,0xfb,0x8a,0xf1,0x02,0x64,0xc0,0x84,0xe8,0x84,0x22,0xfa,0xf1,0x60,0x01,0x64,
-0x23,0xfa,0x5a,0x8d,0x89,0xf1,0x27,0x60,0xe0,0x63,0x44,0x4b,0x43,0x4c,0x2b,0x45,
-0xd7,0x80,0xbe,0xd1,0x0b,0x05,0x2d,0x45,0x64,0x43,0x44,0x61,0x35,0x60,0x58,0x4f,
-0x5d,0x78,0xff,0xff,0x45,0x4d,0x2c,0x43,0x04,0xa3,0xf0,0x00,0x93,0xf1,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x66,0x44,0x92,0xfb,0xc6,0xfe,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x66,0x44,
-0x93,0xfb,0x72,0xf1,0x01,0x60,0x00,0x64,0xc0,0x81,0x28,0x60,0xe2,0x63,0x00,0x64,
-0x40,0x4b,0xf1,0x60,0x02,0x64,0x23,0xfa,0xda,0x85,0xa3,0xd3,0xff,0xff,0xff,0xff,
-0x01,0x2a,0x0b,0x00,0x41,0x4c,0x10,0x61,0x35,0x60,0x58,0x4f,0x5d,0x78,0xff,0xff,
-0x2b,0x44,0xdc,0x84,0x40,0x4b,0x2c,0x41,0xf0,0xa3,0xcd,0x81,0x10,0xa3,0xed,0x02,
-0x93,0xf1,0xff,0xff,0x64,0x46,0x2b,0x44,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xdc,0x84,
-0x22,0xfa,0x3c,0x60,0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x66,0x44,0x93,0xfb,0x3d,0x60,0x4c,0x64,0xa0,0xd1,0x02,0x64,
-0xc0,0x84,0xe8,0x84,0x22,0xfa,0xf1,0x60,0x04,0x64,0x23,0xfa,0xda,0x85,0x3d,0x60,
-0x4e,0x63,0x64,0x41,0x35,0x60,0x58,0x4f,0x5d,0x78,0xff,0xff,0x93,0xf1,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x25,0x44,0x1a,0xf1,0x1b,0xf1,0xd0,0x80,0xd0,0x80,0x0e,0x04,0x08,0x06,0x1c,0xf1,
-0x1d,0xf1,0xd0,0x80,0xd0,0x80,0x08,0x04,0x02,0x06,0x48,0xfe,0x05,0x00,0x25,0x46,
-0x01,0xf0,0x03,0x67,0xa0,0x85,0x94,0x80,0x2f,0x58,0xff,0xff,0x15,0xf5,0x00,0x60,
-0xf1,0x64,0x22,0xfa,0x25,0x44,0x23,0xfa,0x01,0x60,0xa8,0x64,0x40,0x4d,0x46,0x4c,
-0xfc,0x60,0x00,0x64,0x40,0x4b,0xfe,0x60,0x00,0x64,0x36,0x63,0x46,0x61,0xc8,0x84,
-0x2b,0x46,0x58,0xd0,0x2c,0x46,0x59,0xd8,0xfb,0x1f,0x2d,0x41,0x00,0xb9,0x84,0xa1,
-0x08,0x03,0x04,0x24,0x00,0x61,0x41,0x4d,0x00,0xf4,0x02,0x61,0x7a,0x63,0x46,0x4c,
-0xef,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,0x40,0x4b,
-0x4b,0xd3,0x15,0xf5,0x60,0x41,0xd8,0x84,0xe8,0x84,0x22,0xfa,0x25,0x44,0x23,0xfa,
-0xbf,0xd3,0x66,0x45,0x48,0x63,0xc8,0x84,0x2b,0x46,0x58,0xd0,0x65,0x46,0xc9,0x81,
-0xbd,0xd8,0xfa,0x02,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,
-0x40,0x4b,0x4b,0xd3,0x15,0xf5,0x60,0x41,0x22,0xf0,0xe9,0x85,0x64,0x44,0xff,0x22,
-0xdc,0x84,0xc4,0x84,0x22,0xfa,0x64,0x44,0xc2,0x82,0x00,0xa8,0xc2,0x84,0x08,0x24,
-0xd8,0x84,0xbf,0xd1,0xc9,0x83,0x64,0x41,0xc9,0x81,0x66,0x45,0x2b,0x46,0x59,0xd0,
-0x65,0x46,0x58,0xd8,0xfb,0x1f,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x02,0x64,0x22,0xfa,0xfc,0xa3,0xa3,0xd3,0x25,0x43,0xa0,0xd3,0x23,0xfc,0xdc,0x84,
-0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,
-0x25,0x44,0x23,0xfa,0x65,0xf3,0xff,0xff,0x02,0xb4,0x01,0x64,0x08,0x24,0x02,0x64,
-0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,
-0x25,0x44,0x23,0xfa,0x50,0xf3,0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x15,0xf5,0x04,0x64,0x22,0xfa,0x25,0x44,0x23,0xfa,0x58,0xf3,0x24,0xfa,0xff,0xff,
-0x59,0xf3,0x5a,0xf1,0x80,0x65,0xc4,0x87,0x00,0x7f,0x25,0xfa,0x64,0x44,0xc4,0x87,
-0x00,0x7f,0x26,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf0,
-0x17,0x60,0xc6,0x65,0x22,0xf2,0xa5,0xd9,0x02,0xa8,0x64,0x41,0x0f,0x02,0x00,0xb9,
-0xff,0xff,0x0c,0x03,0x16,0x60,0xbc,0x62,0xa2,0xd9,0x7f,0xf3,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x04,0x02,0x01,0x63,0x08,0x60,0x2a,0x64,0xa0,0xdd,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x20,0x63,0x17,0x60,0x0e,0x61,0x46,0x64,0x58,0xd0,
-0x59,0xd9,0xfd,0x1f,0x24,0xf0,0x20,0x64,0xd0,0x81,0x17,0x60,0x12,0x64,0x0d,0x06,
-0xc0,0x83,0x01,0x2a,0x06,0x00,0xcf,0x83,0xa3,0xd3,0xcd,0x81,0x00,0x7f,0xbd,0xdb,
-0x04,0x03,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x67,0x00,0x61,0x23,0x58,
-0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,0x02,0xa8,0x59,0x60,0x1e,0x62,0x09,0x02,
-0xa2,0xd9,0x64,0x41,0x32,0x44,0x02,0xb5,0x00,0xb9,0xd4,0x84,0x08,0x28,0x02,0xbc,
-0x40,0x52,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,
-0x02,0xa8,0x01,0x60,0x92,0x65,0x37,0x02,0xa5,0xd9,0x17,0x60,0xd4,0x62,0x00,0x61,
-0x00,0x64,0x01,0x65,0x64,0x40,0x01,0x2a,0x02,0x00,0x01,0xa1,0x02,0x7e,0x64,0x40,
-0x02,0x2a,0x09,0x00,0x01,0xa1,0xa5,0x80,0xff,0xff,0x02,0x03,0x04,0x7e,0x03,0x00,
-0x04,0x7f,0xa2,0xdb,0x02,0xa2,0x64,0x40,0x04,0x2a,0x09,0x00,0x01,0xa1,0xa5,0x80,
-0xff,0xff,0x02,0x03,0x0b,0x7e,0x03,0x00,0x0b,0x7f,0xa2,0xdb,0x02,0xa2,0x64,0x40,
-0x08,0x2a,0x08,0x00,0x01,0xa1,0xa5,0x80,0xff,0xff,0x02,0x03,0x16,0x7e,0x02,0x00,
-0x16,0x7f,0xa2,0xdb,0xa5,0x80,0xff,0xff,0x02,0x03,0x00,0x7f,0xa2,0xdb,0x17,0x60,
-0xd2,0x62,0x61,0x43,0xa2,0xdd,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x59,0x60,
-0xa6,0x63,0x00,0x60,0xd5,0x61,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf0,0x3d,0x60,0x20,0x62,0xa2,0xd9,
-0x17,0x60,0x06,0x62,0xa2,0xd9,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x24,0xf0,0x66,0x60,0xb4,0x65,0x03,0x60,0xe8,0x64,0x64,0x40,0x00,0x36,0x03,0x00,
-0xa5,0xdb,0x01,0x64,0x40,0x5a,0x17,0x60,0x74,0x64,0xa0,0xd9,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf2,0x99,0xff,0x40,0x5b,0x98,0xff,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf2,0x99,0xff,0x40,0x5a,0x98,0xff,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x01,0x65,0x67,0x60,0x8e,0x61,0x0b,0x00,
-0x02,0x65,0x67,0x60,0x9c,0x61,0x07,0x00,0x04,0x65,0x67,0x60,0xaa,0x61,0x03,0x00,
-0x08,0x65,0x67,0x60,0xb8,0x61,0x41,0xf3,0xff,0xff,0xb4,0x84,0x41,0xfb,0x15,0xf5,
-0x46,0x64,0x00,0x60,0x0c,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x22,0xf2,0xff,0xff,
-0xf8,0xa0,0x0f,0x64,0x01,0x03,0x07,0x64,0x45,0xfb,0x67,0x44,0xd9,0xfb,0xda,0xfb,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,0x25,0x44,
-0x23,0xfa,0x43,0xf3,0x83,0xb4,0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0xbc,0xf3,0xff,0xff,0x00,0xa4,0xff,0xff,0x16,0x03,0x15,0xf5,0x43,0xf3,0x24,0xf2,
-0x60,0x41,0x83,0xb5,0xff,0x60,0x7c,0x7c,0xa1,0x81,0xb5,0x84,0x43,0xfb,0xff,0xff,
-0x01,0x2a,0x09,0x00,0x1e,0x60,0xa0,0x63,0x09,0x60,0x2b,0x64,0xbd,0xdb,0xbd,0xdb,
-0xbd,0xdb,0xff,0xff,0xbd,0xdb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x22,0xf2,0x24,0xf0,0x02,0xa8,0xff,0xff,0x05,0x02,0x00,0x64,0x64,0x40,0x00,0x3a,
-0x03,0x64,0xd5,0xfb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x66,0x60,0xd8,0x62,
-0x01,0x64,0xa2,0xdb,0x17,0x60,0x06,0x62,0x05,0x64,0xa2,0xdb,0x01,0x60,0x7a,0x63,
-0x66,0x60,0xda,0x65,0x03,0x61,0xbd,0xd1,0x00,0x7f,0x64,0x5e,0xa5,0xdb,0xda,0x85,
-0x64,0x47,0x00,0x7f,0xa5,0xdb,0xcd,0x81,0xda,0x85,0xf5,0x02,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,0x02,0xa8,0x1f,0xf3,0x14,0x02,
-0x60,0x40,0x10,0x2a,0x11,0x00,0x17,0x60,0x7e,0x62,0xa2,0xd9,0x00,0x64,0x64,0x40,
-0x01,0x26,0x20,0x64,0xc5,0xfb,0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0x03,0xa8,
-0xff,0xff,0x02,0x02,0xc5,0xf3,0x47,0xfb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x15,0xf5,0x19,0x60,0x2a,0x65,0x26,0xf2,0x25,0xf0,0x60,0x41,0x64,0x43,0xeb,0x83,
-0x00,0x7f,0xe0,0x84,0x44,0xd1,0x61,0x47,0x93,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,
-0xeb,0x83,0x93,0x83,0x0f,0x60,0xf0,0x65,0xa7,0x85,0x02,0x61,0x44,0x60,0x4e,0x63,
-0xc7,0x83,0xa3,0xd3,0xff,0xff,0x60,0x40,0x80,0x2b,0x13,0x00,0x65,0x44,0xff,0xa1,
-0x08,0xa5,0xf4,0x02,0x00,0x65,0x7e,0x61,0x54,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,
-0xff,0xff,0x60,0x40,0x80,0x2b,0x05,0x00,0x65,0x44,0xff,0xa1,0x08,0xa5,0xf4,0x02,
-0x2e,0x00,0x2d,0xf0,0xff,0xff,0x64,0x47,0x00,0x7f,0xe0,0x84,0x60,0x45,0xe0,0x84,
-0xe0,0x81,0xc4,0x85,0xc5,0x85,0x80,0x67,0xb4,0x84,0xbd,0xdb,0x24,0xf2,0xbd,0xdb,
-0xff,0xff,0x25,0xf2,0xbd,0xdb,0x26,0xf0,0xff,0xff,0xa3,0xd9,0x3d,0x60,0x4e,0x63,
-0x27,0xf2,0xc7,0x83,0xbd,0xdb,0x28,0xf2,0xbd,0xdb,0xff,0xff,0x29,0xf2,0xbd,0xdb,
-0x2a,0xf2,0xff,0xff,0xbd,0xdb,0x2b,0xf2,0xbd,0xdb,0xff,0xff,0x2c,0xf2,0xbd,0xdb,
-0x2d,0xf2,0xff,0xff,0xbd,0xdb,0x41,0xf3,0x04,0x65,0xb4,0x84,0x41,0xfb,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x19,0x60,0x2a,0x65,0x26,0xf2,0x25,0xf0,
-0x60,0x41,0x64,0x43,0xeb,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,0x61,0x47,0x93,0x83,
-0x00,0x7f,0xe0,0x84,0x44,0xd1,0xeb,0x83,0x93,0x83,0x0f,0x60,0xf0,0x65,0xa7,0x85,
-0x02,0x61,0x44,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,0x02,0xa3,0x60,0x40,0x80,0x2b,
-0x0d,0x00,0x24,0xf0,0xbd,0xd3,0x50,0xfe,0x25,0xf0,0xd0,0x80,0xbd,0xd3,0x26,0xf0,
-0xd0,0x80,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x20,0x01,0x65,0x44,0xff,0xa1,
-0x08,0xa5,0xe7,0x02,0x00,0x65,0x7e,0x61,0x54,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,
-0x02,0xa3,0x60,0x40,0x80,0x2b,0x0d,0x00,0x24,0xf0,0xbd,0xd3,0x50,0xfe,0x25,0xf0,
-0xd0,0x80,0xbd,0xd3,0x26,0xf0,0xd0,0x80,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x05,0x01,0x65,0x44,0xff,0xa1,0x08,0xa5,0xe7,0x02,0x06,0x00,0xfa,0xa3,0xa3,0xd3,
-0xff,0xff,0xe0,0x84,0xe8,0x84,0xa3,0xdb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x66,0x44,0x93,0xfb,0x00,0x60,0x92,0x64,0x22,0xfa,0xf1,0x60,0x03,0x64,0x23,0xfa,
-0x48,0x65,0x38,0x64,0x40,0x4c,0x1d,0x60,0x90,0x63,0xbd,0xd3,0xff,0xff,0x00,0xa0,
-0xbd,0xd1,0x12,0x03,0x43,0x48,0x60,0x43,0x64,0x41,0xbd,0xd3,0xa5,0xda,0x2c,0x44,
-0xc8,0x84,0x40,0x4c,0x04,0x02,0x00,0xf4,0x78,0x64,0x40,0x4c,0x02,0x62,0xcd,0x81,
-0xda,0x85,0xf3,0x02,0x28,0x43,0xe9,0x00,0x93,0xf1,0x3c,0x60,0x88,0x62,0x3c,0x60,
-0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,
-0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x1f,0xf3,0x15,0xf5,
-0x24,0xf0,0x60,0x40,0x20,0x2a,0x13,0x00,0x64,0x47,0x00,0x7f,0xfe,0xa4,0x82,0xa0,
-0x60,0x45,0x0d,0x05,0x14,0x60,0x3e,0x62,0x46,0xd9,0x67,0x60,0x72,0x62,0xa2,0xd3,
-0x64,0x40,0x12,0x37,0x01,0xbc,0x64,0x40,0x14,0x37,0x02,0xbc,0xa2,0xdb,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x28,0x60,0xda,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,
-0xff,0xff,0xbd,0xdb,0xbd,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,0x1a,0xff,0x23,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0xfb,0x02,0x15,0xf5,0x05,0x64,0x22,0xfa,0x25,0x44,
-0x23,0xfa,0xd4,0xf3,0x24,0xfa,0xff,0xff,0xa1,0xf3,0x25,0xfa,0xa2,0xf3,0xff,0xff,
-0x26,0xfa,0xa3,0xfb,0x27,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x22,0xf2,0xff,0xff,0xfb,0xa0,0x28,0x60,0xda,0x63,0x0b,0x02,0x24,0xf2,0xbd,0xdb,
-0x25,0xf2,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,
-0x1a,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x38,0xf2,0xff,0xff,0xff,0xa0,
-0x02,0x64,0x03,0x02,0x38,0xfa,0x60,0x47,0x3f,0xfa,0x28,0xf2,0xff,0xff,0x08,0xb0,
-0x60,0x47,0x07,0xb5,0x20,0x02,0xa6,0xf3,0x71,0x02,0x02,0xa8,0x01,0xa8,0x1c,0x02,
-0x39,0xf0,0x2b,0xf8,0x3a,0xf0,0xff,0xff,0x2c,0xf8,0x3b,0xf0,0x2d,0xf8,0xff,0xff,
-0x81,0xf1,0x2e,0xf8,0x82,0xf1,0xff,0xff,0x2f,0xf8,0x83,0xf1,0x30,0xf8,0xff,0xff,
-0x3c,0xf0,0x31,0xf8,0x3d,0xf0,0xff,0xff,0x32,0xf8,0x3e,0xf0,0x85,0xf3,0xff,0xff,
-0x33,0xf8,0x08,0xbc,0x29,0xfa,0x52,0x00,0x50,0xfe,0x3c,0xf0,0xbd,0xf3,0x2e,0xf8,
-0xd0,0x80,0x3d,0xf0,0xbe,0xf3,0x2f,0xf8,0xd0,0x80,0x3e,0xf0,0xbf,0xf3,0x30,0xf8,
-0xd0,0x80,0x85,0xf3,0x07,0x01,0x90,0xf3,0x60,0x41,0x04,0xb0,0x61,0x44,0x02,0x02,
-0x42,0xfe,0x3d,0x00,0x4a,0xf1,0x43,0xf1,0x64,0x40,0x01,0x2a,0x0f,0x00,0x28,0xf0,
-0x64,0x40,0x01,0x2a,0x0b,0x00,0x64,0x40,0x81,0x26,0x08,0x00,0x38,0xf2,0x60,0x5c,
-0x00,0xa8,0x64,0x44,0x03,0x03,0x60,0x47,0x40,0xbc,0x60,0x47,0x08,0xbc,0x29,0xfa,
-0x90,0xf3,0xff,0xff,0x04,0xb0,0x39,0xf0,0x21,0x02,0x02,0xb0,0x39,0xf0,0x0f,0x03,
-0x2b,0xf8,0x3a,0xf0,0xff,0xff,0x2c,0xf8,0x3b,0xf0,0x2d,0xf8,0xff,0xff,0x81,0xf1,
-0x31,0xf8,0x82,0xf1,0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x0f,0x00,0x31,0xf8,
-0x3a,0xf0,0xff,0xff,0x32,0xf8,0x3b,0xf0,0x33,0xf8,0xff,0xff,0x81,0xf1,0x2b,0xf8,
-0x82,0xf1,0xff,0xff,0x2c,0xf8,0x83,0xf1,0x2d,0xf8,0x00,0x00,0x02,0xfe,0x2f,0x58,
-0xff,0xff,0x00,0x64,0x15,0xfa,0x16,0xfa,0x1c,0xfa,0xff,0xff,0x07,0xfa,0x19,0xfa,
-0x1e,0x60,0xa0,0x65,0x95,0xf1,0x51,0xf3,0x64,0x40,0x01,0x2a,0x02,0x00,0x13,0xf2,
-0xff,0xff,0xe0,0x84,0x44,0xd1,0xca,0xf9,0x1e,0x63,0x29,0xf0,0x73,0x60,0xff,0x64,
-0xa0,0x84,0x03,0x2b,0x18,0x63,0x29,0xfa,0x04,0xa3,0x64,0x40,0x40,0x27,0x08,0xa3,
-0x43,0x4b,0x21,0xfc,0x56,0x61,0x64,0x40,0x01,0x27,0x62,0x61,0x38,0xf0,0xa1,0xd2,
-0x44,0x4d,0x60,0x40,0x01,0x26,0x22,0x00,0xca,0xf1,0xc3,0x81,0xd1,0x80,0x63,0x45,
-0x20,0x06,0x64,0x43,0xd7,0x85,0x45,0x4c,0xc8,0xf1,0x0f,0xf2,0xd3,0x80,0x01,0x65,
-0x01,0x05,0x00,0x65,0xb4,0x84,0x0f,0xfa,0x00,0x63,0x2d,0x44,0x2c,0x45,0x60,0x41,
-0xd4,0x84,0xdf,0x83,0xfc,0x07,0x14,0xfc,0x61,0x44,0x17,0xfa,0x29,0xf0,0x04,0x64,
-0x60,0x47,0xb0,0x84,0x29,0xfa,0x2c,0x43,0x16,0xfc,0x0f,0x00,0x2d,0x44,0x17,0xfa,
-0x0a,0x00,0x2d,0x44,0x17,0xfa,0x2b,0x45,0xc8,0xf1,0xc4,0x81,0xd1,0x80,0x0f,0xf2,
-0x01,0x04,0x01,0xbc,0x0f,0xfa,0x01,0x64,0x14,0xfa,0x2e,0x58,0xff,0xff,0xcb,0xf3,
-0x2b,0xf2,0xfd,0xa0,0xff,0xff,0x4c,0x02,0x60,0x40,0x01,0x26,0x39,0x00,0x2b,0xf2,
-0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,
-0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0x0e,0x4c,0x3a,0x60,0x58,0x4e,0xc5,0x78,0xff,0xff,0x0c,0x4e,
-0x14,0x03,0x19,0xfc,0x0a,0xa3,0x3c,0x64,0xa3,0xdb,0xfe,0xa3,0xa3,0xd3,0xff,0xff,
-0xff,0xff,0x01,0x26,0x00,0x7f,0x02,0x26,0x01,0x7f,0x04,0x26,0x02,0x7f,0x08,0x26,
-0x03,0x7f,0x1d,0xfa,0x00,0x64,0x0d,0xfa,0x13,0x00,0x19,0xfc,0x50,0xf3,0xf1,0x00,
-0x17,0x60,0xdc,0x64,0xa0,0xd1,0x39,0x60,0xe2,0x64,0x19,0xfa,0x64,0x44,0x08,0x26,
-0x03,0x7f,0x04,0x26,0x02,0x7f,0x02,0x26,0x01,0x7f,0x01,0x26,0x00,0x7f,0xe9,0x00,
-0x2e,0x58,0xff,0xff,0x28,0x60,0xe2,0x65,0x00,0x7f,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x44,0xd3,0x62,0x43,0x60,0x40,0x01,0x2a,0x10,0x00,0x02,0xa3,0x2b,0xf2,
-0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,0x2d,0xf2,0xd0,0x80,0xbd,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x02,0x02,0xf8,0xa3,0x1e,0x00,0x72,0xf1,0x38,0x60,
-0xe2,0x63,0x64,0x41,0xff,0x22,0x17,0x00,0xbd,0xd1,0x2b,0xf2,0x50,0xfe,0x64,0x40,
-0x01,0x26,0x04,0x00,0xcd,0x81,0x0e,0xa3,0xf7,0x02,0x0d,0x00,0xbd,0xd1,0x2c,0xf2,
-0xd0,0x80,0xbd,0xd1,0x2d,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xcd,0x81,
-0xe3,0x01,0x08,0xa3,0xe9,0x02,0x00,0x63,0x00,0xbb,0x2e,0x58,0xff,0xff,0x0f,0xf3,
-0x2c,0x65,0x60,0x47,0xff,0xb4,0xd4,0x80,0xff,0xff,0x04,0x28,0x06,0x00,0xe0,0x85,
-0x15,0x60,0xa0,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0xff,0x67,0x23,0x58,0xff,0xff,
-0x3b,0x60,0x2d,0x64,0x97,0xfb,0xff,0xff,0x2d,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,
-0x10,0xf3,0x7f,0xfb,0x02,0x7f,0x99,0xfb,0x02,0x60,0xee,0x64,0x98,0xfb,0x07,0x64,
-0x9a,0xfb,0x3b,0x60,0x2d,0x64,0x97,0xfb,0xdf,0xfe,0x00,0x64,0x19,0xff,0x30,0x60,
-0x7f,0x78,0xff,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x68,0x60,0xdc,0x61,0x11,0xf3,
-0xff,0xff,0xa1,0xdb,0x68,0x60,0xd2,0x61,0x10,0xf3,0xff,0xff,0xa1,0xdb,0x68,0x60,
-0xd4,0x61,0xff,0xff,0x02,0x36,0x06,0x00,0x03,0x36,0x06,0x00,0x04,0x36,0x13,0x00,
-0x00,0x64,0x16,0x00,0x00,0x64,0x14,0x00,0x16,0x60,0xb6,0x63,0xbd,0xd3,0x81,0xfb,
-0xbd,0xd3,0xff,0xff,0x82,0xfb,0xa3,0xd3,0x83,0xfb,0x68,0x60,0xda,0x62,0xff,0x64,
-0xa2,0xdb,0x01,0x64,0x05,0x00,0x68,0x60,0xda,0x62,0xff,0x64,0xa2,0xdb,0x01,0x64,
-0xa1,0xdb,0x00,0x60,0x01,0x64,0x32,0x45,0x34,0x92,0x00,0x67,0x23,0x58,0xff,0xff,
-0x00,0x60,0x02,0xe8,0x3c,0x60,0x6b,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0xff,0xff,
-0x62,0xff,0xff,0xff,0x1a,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x99,0xff,0x3e,0x44,
-0xfc,0xb4,0x00,0x7f,0x40,0x5e,0x98,0xff,0xbc,0xff,0x0d,0x63,0x58,0x4f,0xf4,0x76,
-0x7e,0x00,0x00,0x10,0x46,0x00,0x99,0xff,0x3d,0x44,0xf7,0xb4,0x40,0x5d,0x98,0xff,
-0x0d,0x63,0x58,0x4f,0x3e,0x00,0x99,0xff,0x3e,0x44,0x77,0xb4,0x08,0xbc,0x00,0x7f,
-0x40,0x5e,0x98,0xff,0x0d,0x63,0x58,0x4f,0x34,0x00,0x99,0xff,0x3c,0x44,0x10,0xbc,
-0x00,0x7f,0x40,0x5c,0x98,0xff,0x99,0xff,0x3d,0x44,0xef,0xb4,0x40,0x5d,0x98,0xff,
-0xb5,0xff,0xff,0xff,0x6c,0x40,0x11,0x60,0x03,0xe8,0x01,0x60,0x03,0xe8,0xff,0xff,
-0xff,0xff,0xff,0xff,0x0e,0x60,0x00,0x6b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x46,0xff,0x47,0xff,0xf9,0x60,0xfe,0x64,0x32,0x45,0x24,0x92,0x99,0xff,0x35,0x47,
-0xff,0xb4,0x60,0x5c,0x08,0x60,0x0e,0x64,0xa0,0xd9,0x64,0x44,0x98,0xff,0x11,0x60,
-0x8e,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0xff,0xff,0x62,0xff,0x00,0x67,0x23,0x58,
-0xff,0xff,0xff,0xff,0xfe,0x1f,0x2f,0x58,0xff,0xff,0x99,0xff,0x1e,0x65,0x3d,0x44,
-0xe1,0x81,0xf9,0xb4,0x02,0x24,0x04,0xbc,0x00,0x7f,0x40,0x5d,0x02,0x63,0xff,0xff,
-0xfe,0x1f,0x02,0xbc,0x40,0x5d,0xf4,0x1f,0x98,0xff,0x99,0xff,0x10,0xf3,0xff,0xff,
-0x60,0x47,0x60,0x45,0x60,0x47,0xa4,0x81,0x65,0x44,0xff,0xad,0x04,0x60,0xff,0xe5,
-0x3c,0x44,0x04,0x60,0xff,0xe5,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5c,0x04,0x60,
-0xff,0xe5,0x3c,0x44,0x04,0x60,0xff,0xe5,0x00,0x7f,0x60,0x5c,0x08,0x60,0x0a,0x64,
-0xa0,0xd9,0x11,0xf3,0xff,0xff,0x60,0x47,0x60,0x45,0x60,0x47,0xa4,0x81,0x65,0x44,
-0xff,0xad,0x3d,0x44,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5d,0x3d,0x44,0x00,0x7f,
-0x60,0x5c,0x08,0x60,0x0c,0x64,0xa0,0xd9,0x12,0xf3,0xff,0xff,0x60,0x47,0x60,0x45,
-0x60,0x47,0xa4,0x81,0x65,0x44,0xff,0xad,0x04,0x60,0xff,0xe5,0x3e,0x44,0x04,0x60,
-0xff,0xe5,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5e,0x00,0x6b,0x04,0x60,0xff,0xe5,
-0x3e,0x44,0x04,0x60,0xff,0xe5,0x00,0x7f,0x60,0x5c,0x08,0x60,0x0e,0x64,0xa0,0xd9,
-0x98,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x10,0xf1,0x3c,0x60,0xa8,0x62,0xa2,0xd9,
-0xca,0x82,0x22,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,0xff,0xff,0x10,0xf1,0xff,0xff,
-0x7f,0xf9,0x3c,0x60,0xa6,0x62,0x08,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,0xff,0xff,
-0x10,0xf1,0x3c,0x60,0xa8,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0x3b,0x60,
-0x13,0x78,0xff,0xff,0x3c,0x60,0xa6,0x62,0x02,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,
-0xff,0xff,0x01,0x65,0x01,0x00,0x00,0x65,0x10,0xf1,0x15,0x60,0x46,0x63,0xbd,0xd9,
-0x11,0xf1,0xbd,0xd9,0xff,0xff,0x12,0xf1,0xa3,0xd9,0x65,0x40,0x01,0x2a,0x06,0x00,
-0x00,0x64,0x10,0xfb,0xff,0xff,0x3b,0x60,0x1b,0x78,0xff,0xff,0x00,0x67,0x23,0x58,
-0xff,0xff,0x10,0xf1,0x67,0x60,0x50,0x62,0xa2,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,
-0x00,0x67,0x23,0x58,0xff,0xff,0x08,0xe1,0xa1,0xff,0xff,0xff,0x43,0xff,0x01,0xe1,
-0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x98,0xff,0x99,0xff,0x3d,0x44,
-0xef,0xb4,0x40,0x5d,0x98,0xff,0x0d,0x63,0x58,0x4f,0x76,0x00,0x99,0xff,0x3e,0x44,
-0x77,0xb4,0x80,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0x0d,0x63,0x58,0x4f,0x6c,0x00,
-0xff,0x6d,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x01,0xbc,0x00,0x7f,0x40,0x5e,0x01,0x60,
-0x00,0x6b,0x98,0xff,0x0d,0x63,0x58,0x4f,0x5f,0x00,0x99,0xff,0x3d,0x44,0xf7,0xb4,
-0x40,0x5d,0x98,0xff,0x99,0xff,0x3d,0x44,0x08,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,
-0x10,0xf3,0xff,0xff,0x60,0x5c,0x99,0xff,0x07,0x60,0x00,0xe8,0x98,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe8,0xff,0xff,0xff,0xff,
-0xff,0xff,0x64,0x4d,0xa1,0xff,0x64,0x47,0x60,0x4d,0xff,0xff,0xff,0xff,0xa1,0xff,
-0xbb,0xff,0x64,0x44,0xa1,0xff,0x60,0x4d,0xa1,0xff,0x60,0x47,0x60,0x4d,0x64,0x44,
-0xa1,0xff,0x60,0x4d,0xa1,0xff,0x60,0x47,0x60,0x4d,0x64,0x44,0xa1,0xff,0x60,0x4d,
-0xa1,0xff,0x60,0x47,0x60,0x4d,0x11,0xf3,0xff,0xff,0x00,0xa8,0x60,0x43,0x05,0x02,
-0x64,0x44,0xa1,0xff,0xff,0xff,0x60,0x4c,0xfc,0x00,0x63,0x46,0x43,0x4f,0x3f,0xf2,
-0xff,0xff,0x60,0x47,0x60,0x5c,0xff,0x65,0x2f,0x46,0x64,0x43,0x00,0xf4,0x01,0xf2,
-0x04,0x62,0xa4,0x81,0xe2,0xd2,0xff,0xff,0xa1,0xff,0xda,0x82,0xc9,0x81,0x60,0x4c,
-0xf9,0x1c,0xf4,0x1d,0xf1,0x1e,0x02,0x02,0x00,0xf4,0x04,0x62,0xa2,0xd2,0xff,0xff,
-0xa1,0xff,0xff,0xff,0x60,0x4d,0xe8,0x00,0xff,0xff,0xfe,0x1f,0x2f,0x58,0xff,0xff,
-0x6a,0x60,0x0c,0x78,0xff,0xff,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x80,0x60,0x00,0x64,
-0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x2b,0x00,0x74,0xf3,0x60,0xf1,0x00,0xa0,0xb0,0x84,
-0x0c,0x03,0x60,0xfb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x00,0x64,0x74,0xfb,0x75,0xf3,0xff,0xff,0x00,0xa0,
-0x00,0x64,0x15,0x03,0x75,0xfb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x02,0x64,
-0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x0a,0x00,0xab,0xfe,0xff,0xff,0xd0,0x05,
-0xaa,0xfe,0xff,0xff,0xd0,0x05,0xa9,0xfe,0xff,0xff,0xd6,0x05,0xff,0xff,0xa1,0xff,
-0xff,0xff,0xbd,0x3f,0x0e,0x57,0x67,0x60,0xca,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,
-0x01,0x26,0x0c,0x65,0x45,0x48,0x0f,0x4e,0x00,0x60,0x06,0x61,0x41,0x4d,0x40,0xa1,
-0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,
-0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x15,0x03,0x02,0x64,
-0x22,0xfa,0xf2,0x60,0x00,0x64,0x5a,0xda,0x28,0x44,0x5a,0xda,0x08,0x65,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x45,0x54,0x3b,0xff,0x37,0x58,0xff,0xff,0x92,0xf3,0x15,0x61,
-0x00,0xa8,0x60,0x46,0x15,0x02,0x0f,0x4e,0x00,0x60,0x2e,0x61,0x41,0x4d,0x40,0xa1,
-0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,
-0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x21,0x03,0x15,0x61,
-0x00,0x64,0x92,0xfb,0x58,0x60,0x2e,0x63,0x16,0x64,0x22,0xfa,0xf1,0x60,0x00,0x64,
-0x23,0xfa,0x48,0x65,0x00,0x64,0xa3,0xd1,0xbd,0xdb,0xa5,0xd8,0xcd,0x81,0xda,0x85,
-0xfa,0x02,0x3c,0x60,0x82,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,0x45,0x54,0x3b,0xff,0xa6,0xfe,
-0x8e,0x00,0xa6,0xfe,0xff,0xff,0xc2,0x05,0xa7,0xfe,0xff,0xff,0x08,0x05,0xa5,0xfe,
-0xff,0xff,0x1f,0x05,0xa4,0xfe,0xff,0xff,0x01,0x05,0x81,0x00,0x80,0x00,0x36,0x45,
-0x0e,0x60,0xd0,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0x94,0xf3,0xff,0xff,0x01,0xb0,
-0x00,0x64,0x0f,0x03,0x94,0xfb,0x31,0x44,0xfe,0xb4,0x40,0x51,0x1e,0x60,0xe0,0x62,
-0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x3d,0x60,0x2f,0x78,
-0xff,0xff,0x3e,0x60,0x8d,0x78,0xff,0xff,0x16,0x60,0xb6,0x63,0xbd,0xd3,0xbd,0xd1,
-0xbd,0xd1,0xb0,0x84,0xb0,0x84,0xff,0xff,0x0b,0x02,0x8c,0xfb,0x31,0x44,0xfe,0xb4,
-0x40,0x51,0x0d,0x7c,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x3e,0x60,0x8d,0x78,0xff,0xff,
-0x0f,0xf3,0x94,0xf1,0x60,0x47,0x07,0xb4,0x01,0x61,0x03,0x03,0xcc,0x84,0xe1,0x81,
-0xfd,0x02,0xa1,0x80,0xb1,0x83,0x03,0x03,0x3e,0x60,0x8d,0x78,0xff,0xff,0x94,0xfd,
-0x31,0x44,0x01,0xbc,0x40,0x51,0xd1,0xfe,0x1f,0xf3,0xff,0xff,0xff,0xff,0x20,0x26,
-0x18,0x00,0x17,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,
-0xe4,0x65,0xbb,0x63,0x0b,0x00,0x60,0x40,0x02,0x3a,0x03,0x00,0xe0,0x65,0xb9,0x63,
-0x05,0x00,0xb8,0x63,0xe6,0x65,0x60,0x40,0x03,0x36,0xe4,0x65,0x13,0x60,0x6c,0x62,
-0x15,0x00,0x17,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,
-0xe4,0x65,0xcb,0x63,0x0b,0x00,0x60,0x40,0x02,0x3a,0x03,0x00,0xe0,0x65,0xc9,0x63,
-0x05,0x00,0xc8,0x63,0xe6,0x65,0x60,0x40,0x03,0x36,0xe4,0x65,0x67,0x60,0x72,0x62,
-0xa2,0xd3,0xff,0xff,0x14,0x60,0x4e,0x62,0x60,0x41,0x01,0x26,0x03,0x00,0x65,0x5e,
-0x12,0x7f,0xa2,0xdb,0x61,0x40,0x02,0x26,0x03,0x00,0x63,0x5e,0x14,0x7f,0x5a,0xdb,
-0x00,0x64,0x65,0xfb,0x17,0x60,0x44,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0x01,0x06,0x04,0x64,0xa2,0xdb,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,0x95,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x26,0x03,0x00,0x60,0x08,0x63,0x01,0x60,0x78,0x61,
-0x16,0x60,0xb4,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x72,0x63,0x16,0x60,
-0x40,0x61,0x16,0x60,0xbe,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x1a,0x63,
-0x01,0x60,0x00,0x61,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x00,0x60,0xfa,0x64,0xe3,0xfb,
-0xc0,0xf1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1b,0x00,0x0d,0x60,0xac,0x64,0xe3,0xfb,0x32,0x40,0x01,0x26,0x0f,0x00,
-0x16,0x60,0xbc,0x62,0xa2,0xd3,0xa5,0xf3,0x60,0x41,0x00,0x36,0x04,0x00,0xcd,0x81,
-0xe8,0x84,0xfd,0x02,0x04,0x05,0xc0,0xf1,0x16,0x60,0xbc,0x62,0xa2,0xd9,0x0f,0x4e,
-0x42,0x60,0x58,0x4f,0x60,0x78,0xff,0xff,0x0e,0x4f,0xda,0xfe,0x3d,0x60,0x2f,0x78,
-0xff,0xff,0x66,0x44,0x00,0xa8,0x0e,0x57,0x17,0x03,0x00,0x64,0x40,0x46,0xcb,0xfe,
-0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,
-0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x37,0x58,0xff,0xff,0x1f,0xf3,0xff,0xff,
-0xff,0xff,0x10,0x2a,0x34,0x00,0x17,0x60,0x44,0x62,0xa2,0xd3,0xff,0xff,0x60,0x45,
-0xd5,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x19,0x03,0x17,0x60,0x80,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x45,0x17,0x60,0x48,0x63,0xff,0x60,0xff,0x61,0xbd,0xd3,0xdd,0x81,
-0xd4,0x80,0x61,0x44,0x03,0x06,0xfc,0xa0,0xff,0xff,0xf8,0x02,0x17,0x60,0x44,0x62,
-0xa2,0xd3,0x61,0x45,0xd4,0x80,0xff,0xff,0x01,0x06,0x60,0x45,0x66,0x60,0xd4,0x62,
-0x65,0x44,0xa2,0xdb,0xe0,0x85,0x17,0x60,0x3a,0x64,0x44,0xd3,0xff,0xff,0x13,0x60,
-0x98,0x62,0x3e,0x7f,0xa2,0xdb,0x14,0x60,0x7a,0x62,0x3e,0x7f,0xa2,0xdb,0x2e,0x58,
-0xff,0xff,0x3c,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0e,0xf2,
-0x62,0x03,0x60,0x47,0xfd,0x37,0x3a,0x00,0xff,0x36,0x17,0x00,0xf0,0x36,0x0a,0x00,
-0xff,0xb5,0x1e,0x60,0xaa,0x62,0x46,0xd1,0x00,0x60,0x01,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xdc,0x00,0x0f,0x4e,0x46,0x45,0x3c,0x60,
-0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,
-0x0e,0x4f,0x59,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xbb,0x00,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd3,0xfe,0x59,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xa2,0x00,0xac,0xfe,
-0xff,0xff,0x0c,0x05,0xad,0xfe,0xff,0xff,0x12,0x05,0xae,0xfe,0xff,0xff,0x99,0x05,
-0xaf,0xfe,0xff,0xff,0x3a,0x05,0x3d,0x60,0x2f,0x78,0xff,0xff,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x20,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0xf4,0x00,0x1e,0x60,
-0xf4,0x65,0x0a,0x61,0x07,0x00,0xa2,0xdd,0x58,0x4f,0x64,0x58,0xff,0xff,0x00,0xb9,
-0xff,0xff,0x08,0x03,0x00,0x63,0xa5,0xd1,0x5a,0xd3,0xda,0x85,0x00,0xa8,0xcd,0x81,
-0xf2,0x02,0xf8,0x02,0xe0,0x00,0x1e,0x60,0xa6,0x62,0x1e,0x60,0xde,0x65,0x3f,0x60,
-0x8b,0x63,0x00,0x64,0x5a,0xdb,0xd6,0x80,0xff,0xff,0x04,0x03,0x5a,0xdb,0x5a,0xdb,
-0x5a,0xdd,0xf9,0x00,0x1e,0x60,0xf2,0x65,0x00,0x64,0x5a,0xdb,0xd6,0x80,0xff,0xff,
-0x02,0x03,0x5a,0xdd,0xfb,0x00,0x2f,0x58,0xff,0xff,0x1e,0x60,0xaa,0x64,0x40,0x41,
-0x1e,0x60,0xa8,0x63,0xa3,0xd1,0x00,0x64,0xd0,0x80,0x09,0x61,0x08,0x03,0xbd,0xdb,
-0xa3,0xd3,0xff,0xff,0xb0,0x84,0xcd,0x81,0xa3,0xdb,0x06,0xa3,0xf9,0x02,0x1e,0x60,
-0xe0,0x63,0xa3,0xd1,0x00,0x64,0xd0,0x80,0x0a,0x61,0x16,0x03,0xbd,0xdb,0x64,0x44,
-0xfe,0xa3,0x02,0xa3,0xcd,0x81,0xe8,0x84,0xe3,0x03,0x02,0x05,0xe1,0x03,0xf9,0x00,
-0x40,0x42,0xa3,0xd3,0x43,0x44,0x00,0xa8,0x41,0x43,0x02,0x03,0x58,0x4f,0x60,0x58,
-0x22,0x44,0x23,0x41,0x24,0x43,0xed,0x00,0x21,0x43,0x1e,0x60,0xe0,0x65,0xd7,0x80,
-0xbd,0xd1,0xbd,0xd3,0x01,0x02,0x8f,0x00,0xa0,0x84,0xbd,0xd1,0x43,0x41,0xf7,0x03,
-0x3f,0x60,0x90,0x64,0x64,0x58,0x40,0x4f,0x29,0xf2,0xff,0xff,0x60,0x40,0x08,0x26,
-0x03,0x00,0x40,0x60,0x2e,0x78,0xff,0xff,0x60,0x40,0x18,0x36,0x17,0x00,0x0c,0x60,
-0x44,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0f,0xf0,0xbd,0xf1,0x64,0x44,0x60,0x22,
-0x10,0x00,0x31,0xf2,0x32,0xf2,0xd0,0x80,0xbe,0xf1,0x0b,0x02,0xd0,0x80,0x33,0xf2,
-0x08,0x02,0xbf,0xf1,0xff,0xff,0xd0,0x80,0x0f,0xf0,0x03,0x02,0x41,0x60,0x76,0x78,
-0xff,0xff,0x00,0xf4,0xaa,0x60,0xaa,0x65,0x02,0xf2,0x03,0xf0,0xd4,0x80,0x03,0x64,
-0x16,0x02,0xd0,0x80,0x00,0x64,0x04,0xf0,0x12,0x02,0xd0,0x80,0xf8,0x7f,0x06,0x02,
-0x26,0x46,0x22,0xf0,0x20,0x67,0xb0,0x84,0xa2,0xda,0x09,0x00,0xd0,0x80,0xff,0xff,
-0x06,0x02,0x26,0x46,0x22,0xf0,0x40,0x67,0xb0,0x84,0xa2,0xda,0x00,0x00,0x26,0x46,
-0x0f,0xf2,0x81,0xf1,0x29,0xf2,0x60,0x40,0x20,0x2a,0x11,0x00,0x5c,0x63,0x60,0x40,
-0x02,0x2b,0x62,0x63,0xbd,0xd2,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x5c,0x02,0xd0,0x80,
-0xa3,0xd2,0x83,0xf1,0x58,0x02,0xd0,0x80,0xff,0xff,0x55,0x02,0x00,0x00,0x64,0x60,
-0x58,0x4f,0x5a,0x78,0xff,0xff,0x41,0x60,0x4c,0x78,0xff,0xff,0x26,0x46,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x0c,0x26,0x47,0x00,0x95,0xf1,0x00,0x63,0xd3,0x80,0xff,0xff,
-0xf2,0x02,0x60,0x40,0xb0,0x3a,0x07,0x00,0x5a,0x60,0xa8,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x23,0x00,0x10,0x3a,0x07,0x00,0x5a,0x60,0xac,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1a,0x00,0x30,0x3a,0x07,0x00,0x5a,0x60,0xac,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0x00,0xc0,0x3a,0x07,0x00,0x5a,0x60,
-0xae,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x08,0x00,0xa0,0x3a,0x1e,0x00,
-0x5a,0x60,0xb0,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x48,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x2d,0x00,0xcb,0xf3,0xff,0xff,0x60,0x40,0x03,0x3a,
-0x0a,0x00,0x68,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x26,0x46,0x04,0x02,0x41,0x60,
-0x58,0x4e,0x8c,0x78,0xff,0xff,0x41,0x60,0x76,0x78,0xff,0xff,0x50,0x3a,0x0c,0x00,
-0x5a,0x60,0xb6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x50,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0d,0x00,0x40,0x3a,0x0e,0x00,0x5a,0x60,0xb2,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x4a,0x64,0xa0,0xd7,0x58,0x4f,
-0xff,0xff,0x41,0x60,0x72,0x78,0xff,0xff,0x90,0x3a,0x10,0x00,0x41,0x60,0x58,0x4e,
-0x7d,0x78,0xff,0xff,0x0c,0x60,0x52,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x5a,0x60,
-0xa4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xeb,0x00,0x80,0x3a,0x6e,0x00,
-0xcb,0xf3,0x20,0x40,0x40,0x26,0x32,0x00,0xfd,0xa0,0xfc,0xa0,0x01,0x03,0x29,0x02,
-0x81,0xf1,0x31,0xf2,0x32,0xf2,0xd0,0x80,0x82,0xf1,0xae,0x02,0xd0,0x80,0x33,0xf2,
-0x83,0xf1,0xaa,0x02,0xd0,0x80,0x70,0xf3,0xa7,0x02,0xdc,0x84,0xcb,0xf1,0x70,0xfb,
-0x00,0x64,0x71,0xfb,0x0a,0x60,0x02,0x64,0x6e,0xfb,0x64,0x40,0x03,0x36,0x50,0x00,
-0x5a,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x4c,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0c,0x60,0x4e,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,
-0x3d,0x00,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x08,0x04,0x0c,0x60,0x50,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x41,0x60,0x72,0x78,0xff,0xff,0x16,0x60,0x42,0x62,
-0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x81,0xf3,0x29,0x02,0x00,0xa0,0x31,0xf0,0x13,0x03,
-0xd0,0x80,0x82,0xf3,0x32,0xf0,0x0f,0x02,0xd0,0x80,0x83,0xf3,0x33,0xf0,0x0b,0x02,
-0xd0,0x80,0x70,0xf3,0x08,0x02,0xdc,0x84,0x70,0xfb,0x00,0x64,0x71,0xfb,0x0a,0x60,
-0x02,0x64,0x6e,0xfb,0x13,0x00,0x59,0x60,0x20,0x64,0xa0,0xd3,0xff,0xff,0x60,0x40,
-0xff,0x22,0x0c,0x00,0x68,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x07,0x02,0x0f,0x4e,
-0x26,0x46,0x67,0x60,0x58,0x4f,0x13,0x78,0xff,0xff,0x0e,0x4f,0x26,0x46,0x56,0x00,
-0x29,0xf0,0x3d,0x60,0x0e,0x65,0x0a,0x64,0x64,0x40,0x10,0x2b,0xa5,0xdb,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x30,0x64,0xa2,0xdb,0x41,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0xa4,0xf3,0x5e,0xf1,0x60,0x45,0x73,0x44,0x64,0x40,0x04,0x2a,0x04,0x00,0xd4,0x84,
-0xe7,0xa0,0x96,0x0e,0x95,0x04,0x5a,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x0c,0x60,0x4c,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0c,0x60,0x4e,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0xd2,0x00,0x66,0x60,0xd8,0x62,0xa2,0xd3,0xff,0xff,
-0x81,0xa0,0xff,0xa0,0x23,0x03,0x0f,0x02,0x41,0x60,0x58,0x4e,0xee,0x78,0xff,0xff,
-0xff,0xa1,0x26,0x46,0x1b,0x02,0x66,0x60,0xd8,0x62,0x7f,0x64,0xa2,0xdb,0xba,0xff,
-0xff,0xff,0xaf,0xff,0x13,0x00,0x3c,0x60,0x82,0x62,0x3c,0x60,0x6a,0x64,0xa2,0xdb,
-0x26,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0x64,0x40,0x46,
-0xd2,0xfe,0x08,0x00,0x66,0x44,0x00,0xbc,0xff,0xff,0x04,0x03,0x3e,0x60,0x58,0x4e,
-0x91,0x78,0xff,0xff,0x56,0xf7,0xff,0xff,0xff,0xff,0x76,0xf3,0x3c,0x45,0xd4,0x80,
-0x28,0x60,0x60,0x62,0x07,0x02,0x80,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,
-0xa2,0xdb,0x71,0xfb,0x2e,0x58,0xff,0xff,0x26,0x46,0x28,0x60,0xda,0x63,0x00,0xf4,
-0x02,0xf2,0xbd,0xdb,0xff,0xff,0x03,0xf2,0xbd,0xdb,0x04,0xf2,0xff,0xff,0xbd,0xdb,
-0x05,0xf2,0xa3,0xdb,0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,0x23,0xf0,
-0x00,0x61,0xd0,0x84,0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,
-0x03,0xa9,0x24,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,
-0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,
-0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x28,0xf0,
-0x01,0x03,0xcc,0x84,0xd0,0x84,0xa3,0xdb,0x26,0x0e,0x28,0x60,0xda,0x63,0xbd,0xd3,
-0x00,0x65,0x00,0x61,0xd4,0x84,0xbd,0xd3,0xf1,0x81,0x01,0xa9,0x42,0xfe,0x01,0x03,
-0xcc,0x84,0xf1,0x81,0x01,0x65,0xd4,0x84,0xf1,0x81,0xbd,0xd3,0x03,0xb1,0x03,0xa9,
-0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0x00,0x65,0xd4,0x84,0xf1,0x81,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x42,0xfe,0x01,0x03,0xcc,0x84,0xd4,0x84,0x04,0x0e,0x66,0x60,
-0x9a,0x62,0x01,0x64,0xa2,0xdb,0x26,0x46,0x2e,0x58,0xff,0xff,0x66,0x60,0xd6,0x62,
-0x2e,0x44,0xa2,0xdb,0x00,0x64,0x38,0xf2,0x40,0x48,0x40,0x4c,0xa0,0xa0,0xff,0xff,
-0x43,0x04,0x00,0xf4,0x01,0xf2,0x04,0x63,0x60,0x41,0x66,0x60,0xe6,0x62,0x00,0x64,
-0xa2,0xdb,0x42,0x60,0x58,0x4e,0x44,0x78,0xff,0xff,0x36,0x03,0xff,0x65,0xd4,0x80,
-0xff,0xff,0xf7,0x02,0x00,0x64,0xdc,0x84,0x40,0x4d,0x42,0x60,0x58,0x4e,0x44,0x78,
-0xff,0xff,0x2a,0x03,0xff,0x65,0xd4,0x80,0x60,0x42,0x2d,0x44,0xf4,0x03,0xfa,0xa0,
-0xff,0xff,0xe3,0x04,0x06,0x64,0x40,0x4d,0x66,0x60,0xda,0x65,0x62,0x44,0x05,0x00,
-0x42,0x60,0x58,0x4e,0x44,0x78,0xff,0xff,0x17,0x03,0xa5,0xd1,0xda,0x85,0xd0,0x80,
-0xff,0xff,0xdc,0x02,0x2d,0x44,0xcc,0x84,0x40,0x4d,0xf2,0x02,0x06,0x64,0x40,0x4d,
-0x66,0x60,0xda,0x65,0x66,0x60,0xe6,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf0,0xa0,
-0xa2,0xdb,0xe6,0x02,0x01,0x61,0x01,0x00,0x00,0x61,0x66,0x60,0xd6,0x62,0xa2,0xd3,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x2c,0x44,0xcc,0x84,0x40,0x4c,0x11,0x0e,
-0xcd,0x81,0xff,0xff,0x05,0x0d,0x00,0xf4,0x01,0xf2,0x04,0x63,0xcc,0x84,0x60,0x41,
-0x28,0x44,0x01,0xac,0x40,0x48,0x07,0x02,0xbd,0xd2,0x01,0xb8,0x60,0x47,0x00,0x7f,
-0x05,0x00,0xdc,0x84,0x03,0x00,0xa3,0xd2,0xff,0xff,0x00,0x7f,0x2e,0x58,0xff,0xff,
-0x01,0x64,0xcb,0xfb,0x1e,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,0x00,0x60,0x08,0x63,
-0x01,0x60,0x78,0x61,0x16,0x60,0xb4,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,
-0x72,0x63,0x16,0x60,0x40,0x61,0x16,0x60,0xbe,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,
-0xd5,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x14,0x03,0x18,0x60,0xd0,0x63,0x17,0x60,
-0x82,0x61,0x13,0x64,0xbd,0xd1,0xa1,0xd9,0xff,0xa4,0x02,0xa1,0xfb,0x02,0x1f,0x60,
-0x38,0x63,0x0a,0xa3,0x05,0x64,0xa3,0xdb,0x0c,0xa3,0xa3,0xdb,0x04,0xa3,0xa3,0xdb,
-0x25,0x00,0x1f,0x60,0x56,0x62,0x00,0x64,0xa2,0xdb,0x1f,0x60,0x1c,0x63,0xa5,0xf3,
-0x01,0x61,0x60,0x45,0x65,0x44,0xe8,0x85,0x05,0x64,0x02,0x28,0x00,0x64,0xbd,0xdb,
-0x00,0xa0,0xff,0xff,0x0d,0x03,0x1f,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,
-0xa2,0xdb,0xff,0xa0,0xff,0xff,0x04,0x02,0x1f,0x60,0x54,0x62,0x61,0x44,0xa2,0xdb,
-0xdd,0x81,0xff,0xff,0x61,0x44,0xf2,0xa0,0xff,0xff,0xe4,0x06,0x59,0x60,0x1e,0x61,
-0x16,0x60,0x42,0x64,0x20,0x63,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0xa8,0xf1,0x80,0xf9,
-0x1f,0xf1,0x66,0x60,0xf6,0x65,0x0a,0x64,0x64,0x40,0x10,0x2a,0x04,0x64,0xa5,0xdb,
-0x32,0x40,0x01,0x26,0x07,0x00,0x00,0x60,0x1a,0x63,0x01,0x60,0x00,0x61,0x00,0x64,
-0x59,0xdb,0xfe,0x1f,0x40,0x40,0xc0,0xf3,0xff,0xff,0x40,0x4a,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xca,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x42,0x60,0xd6,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x08,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xca,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x42,0x60,0xfc,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,
-0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x61,0x60,0xc8,0x62,0x0c,0x64,0xa2,0xdb,0x64,0x60,0x58,0x62,0x01,0x64,
-0xa2,0xdb,0xd5,0xf1,0x03,0x64,0x64,0x40,0x00,0x3a,0xd5,0xfb,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x02,0x02,0x00,0x64,0x62,0xfb,0x67,0x60,0x74,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x40,0x00,0x3a,0x2b,0x00,0x01,0x64,0xa2,0xdb,0x12,0x60,0x34,0x65,
-0x72,0x44,0xb4,0x83,0x00,0x7f,0x60,0x5c,0x10,0x61,0x40,0x60,0x0b,0x65,0x63,0x44,
-0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,0x94,0x84,0xf3,0x83,0xcd,0x81,0xff,0xff,
-0xf8,0x02,0x60,0x47,0x60,0x45,0x00,0x7f,0x39,0xfb,0x65,0x44,0x00,0x7e,0xb0,0x84,
-0x38,0xfb,0x66,0x60,0xaa,0x63,0xbd,0xf3,0xff,0xff,0x02,0xbc,0xbd,0xdb,0x39,0xf3,
-0xbe,0xf1,0x60,0x47,0x00,0x7e,0xb0,0x84,0xbd,0xdb,0x38,0xf3,0xa3,0xdb,0x0f,0x4e,
-0x5c,0x60,0x58,0x4f,0x1f,0x78,0xff,0xff,0x0e,0x4f,0x0f,0x4e,0x58,0x60,0x58,0x4f,
-0xdd,0x78,0xff,0xff,0x0e,0x4f,0x0f,0x4e,0x5e,0x60,0x58,0x4f,0x53,0x78,0xff,0xff,
-0x0e,0x4f,0x0f,0x4e,0x44,0x60,0x58,0x4f,0x1e,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,
-0xc8,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0x02,0x64,0x8b,0xfb,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xe0,0x62,
-0xa2,0xd1,0x00,0x60,0xf4,0x86,0x7e,0x00,0x00,0x10,0x1f,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x1e,0x60,0xc8,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x1e,0x60,0xca,0x62,0x00,0x60,0x08,0x64,0xa2,0xdb,0x43,0x60,
-0x92,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x8b,0xf3,0x00,0x65,0xd4,0x80,
-0xff,0xff,0x0b,0x03,0x1e,0x60,0xca,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x43,0x60,
-0x92,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xca,0x62,0x20,0x60,0x00,0x64,
-0xa2,0xdb,0x43,0x60,0xb4,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x3c,0x60,
-0xa2,0x62,0xca,0x82,0x06,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0x5a,0xdb,0xbe,0xfe,0x3c,0x60,0x28,0x61,0x43,0x60,0x58,0x4e,
-0xef,0x78,0xff,0xff,0x3c,0x60,0x2e,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,
-0x3c,0x60,0x46,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x3c,0x60,0x6a,0x61,
-0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x3c,0x60,0x4c,0x61,0x43,0x60,0x58,0x4e,
-0xef,0x78,0xff,0xff,0x3c,0x60,0x76,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,
-0xc5,0xfe,0x01,0x64,0xcb,0xfb,0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,
-0x2f,0x58,0xff,0xff,0xa1,0xd3,0x0e,0x57,0x00,0xa8,0x60,0x46,0x28,0x03,0x09,0xf0,
-0x0e,0xf2,0x44,0x4c,0x20,0xb0,0x01,0xb0,0x0b,0x03,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x15,0x00,
-0x14,0x02,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,
-0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x2c,0x44,0xd5,0x00,0x37,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x02,0x64,0xa2,0xdb,0x44,0x60,0x36,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x16,0x60,0x42,0x62,0xa2,0xd3,
-0xff,0xff,0x03,0xa8,0x04,0xa8,0x02,0x03,0x04,0x03,0x06,0x00,0x45,0x60,0x25,0x78,
-0xff,0xff,0x45,0x60,0x6b,0x78,0xff,0xff,0x02,0x64,0xcb,0xfb,0x00,0x64,0x81,0xfb,
-0x82,0xfb,0x83,0xfb,0x66,0x60,0xc0,0x62,0x01,0x64,0xa2,0xdb,0x16,0x60,0x44,0x64,
-0x5b,0xfb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,
-0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x44,0x60,0x67,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x59,0x60,0xb4,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x88,0xf1,0x27,0x60,0xe0,0x63,0xd3,0x80,
-0xff,0xff,0x03,0x03,0x4b,0x60,0x47,0x78,0xff,0xff,0xc0,0xf3,0xc6,0xf3,0x40,0x4a,
-0x00,0xa8,0xff,0xff,0x03,0x03,0x45,0x60,0x6b,0x78,0xff,0xff,0x3e,0x60,0x58,0x4e,
-0xae,0x78,0xff,0xff,0x02,0x64,0xcb,0xfb,0x64,0x60,0x58,0x62,0xa2,0xd3,0xff,0xff,
-0x9d,0xa0,0x01,0xa4,0x03,0x05,0xec,0xa0,0xa2,0xdb,0x18,0x06,0xa9,0xf1,0x28,0x60,
-0x7e,0x64,0xa0,0xd9,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,
-0x44,0x60,0xc7,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xe1,0xf3,0xff,0xff,
-0x00,0xa0,0x60,0x5c,0x17,0x03,0x28,0x60,0x7e,0x64,0xa0,0xd9,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x44,0x60,0xc7,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xd5,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x18,0x03,0x66,0x60,0xc2,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0x88,0xa0,0xa2,0xdb,0x10,0x04,0x00,0x64,0xa2,0xdb,0x03,0x64,0xd5,0xfb,0x0a,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x1f,0x60,0x1c,0x63,0x0e,0x64,0x00,0x7c,
-0xcc,0x84,0xbd,0xd9,0xfd,0x02,0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x40,
-0x03,0x22,0x33,0x00,0x01,0x2a,0x0c,0x00,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,
-0xf0,0x65,0xad,0xf3,0xa5,0xd3,0x60,0x45,0xc4,0x84,0xd0,0x80,0xff,0xff,0x25,0x0d,
-0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x45,0xfd,0xb4,0xa2,0xdb,0x20,0x44,
-0xb4,0x84,0x40,0x40,0x59,0x60,0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,
-0x10,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,0x1e,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x44,0x60,0x49,0x78,0xff,0xff,
-0x05,0x64,0xcb,0xfb,0xc5,0xf3,0x47,0xfb,0x00,0x64,0xc0,0xf1,0x85,0xfb,0x44,0x4a,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,
-0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,0x2d,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,
-0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,
-0x45,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,
-0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x01,0x64,0x8c,0xfb,0x02,0x64,0x90,0xfb,0x01,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,0xd5,0xf3,0xff,0xff,
-0xfd,0xa0,0xff,0xff,0x05,0x05,0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,
-0x0c,0x60,0x48,0x62,0x67,0x60,0xd6,0x64,0xa2,0xdb,0x59,0x60,0x74,0x62,0x28,0x60,
-0xe2,0x64,0xa2,0xdb,0x77,0xf5,0x00,0x64,0x22,0xfa,0x38,0xfa,0x08,0x64,0x28,0xfa,
-0x18,0x60,0x20,0x64,0x0e,0xfa,0x90,0x64,0x29,0xfa,0x66,0x60,0xa4,0x62,0x12,0x60,
-0x34,0x65,0x72,0x44,0xb4,0x84,0xa2,0xdb,0xc0,0xf3,0x7f,0xfb,0x66,0x60,0x96,0x63,
-0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xdb,0xff,0xff,0x71,0xfb,0x70,0xfb,0x81,0xfb,
-0xff,0xff,0x82,0xfb,0x83,0xfb,0xff,0xff,0xc5,0xf3,0x47,0xfb,0x16,0x60,0xbe,0x64,
-0xa0,0xd3,0x86,0xfb,0x66,0x60,0xa2,0x63,0x06,0x64,0xa3,0xdb,0x01,0x64,0x73,0xfb,
-0x39,0x60,0xf2,0x62,0x00,0x64,0xa2,0xdb,0x5e,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x90,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x63,0x02,0x64,0xcb,0xfb,0xa3,0xdb,0x28,0x60,
-0xd2,0x63,0x39,0x60,0xb2,0x7c,0x10,0x65,0x00,0x64,0x47,0xdb,0xd3,0x80,0x47,0xdb,
-0xfc,0x04,0x16,0x60,0x44,0x64,0x5b,0xfb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,
-0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,
-0xda,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x59,0x60,0xb4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x39,0x60,
-0xf2,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x09,0x03,0x4b,0x60,0x58,0x4e,
-0x2b,0x78,0xff,0xff,0x04,0x02,0x46,0x60,0x97,0x78,0xff,0xff,0xd2,0x00,0x59,0x60,
-0x20,0x62,0xa2,0xd3,0x01,0x63,0x60,0x40,0x00,0x3a,0x6e,0xfd,0x0a,0x61,0x66,0x60,
-0xa4,0x62,0x40,0x60,0x0b,0x65,0xa2,0xd3,0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,
-0x94,0x84,0xf3,0x83,0xcd,0x81,0xff,0xff,0xf8,0x02,0xa2,0xdb,0x64,0xa3,0x28,0x60,
-0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,
-0x46,0x60,0x23,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x00,0x64,0x6e,0xfb,0x39,0x60,0xf2,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0x60,0x45,0x67,0x02,0x66,0x60,0xa2,0x62,0xa2,0xd3,0xff,0xff,0xcc,0x84,
-0xa2,0xdb,0xbc,0x02,0x59,0x60,0x20,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x10,0x03,0x66,0x60,0xa8,0x62,0xa2,0xd1,0x73,0xf3,0xff,0xff,0xfd,0xa0,0xfe,0xa0,
-0x2e,0x03,0x07,0x03,0x64,0x44,0x00,0x3a,0x2a,0x00,0x65,0x44,0x00,0xa0,0xff,0xff,
-0x26,0x03,0x16,0x60,0x88,0x62,0xa2,0xd1,0x04,0x7f,0x64,0x40,0xff,0x26,0x08,0x7f,
-0x60,0x43,0x28,0x60,0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,
-0x04,0x64,0xa2,0xdb,0x46,0x60,0x6d,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x45,0x60,0x93,0x78,0xff,0xff,0x66,0x60,
-0xaa,0x63,0xbd,0xd3,0x81,0xfb,0xbd,0xd3,0xff,0xff,0x82,0xfb,0xa3,0xd3,0xff,0xff,
-0xdc,0x84,0xa3,0xdb,0x83,0xfb,0xff,0xff,0xc1,0xf3,0x86,0xfb,0x73,0xf3,0xff,0xff,
-0x04,0xbc,0x73,0xfb,0x04,0x64,0x5e,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,0x91,0x64,
-0xa2,0xdb,0x66,0x60,0x9e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x47,0x60,
-0x1a,0x78,0xff,0xff,0x39,0x60,0xf2,0x63,0xbd,0xd1,0x81,0xf9,0xbd,0xd1,0xff,0xff,
-0x82,0xf9,0xbd,0xd1,0x83,0xf9,0x08,0xa3,0xbd,0xd1,0x80,0xf9,0xff,0xff,0xbd,0xd1,
-0xbd,0xd1,0x59,0x60,0x20,0x65,0xa5,0xd9,0x02,0xa2,0x62,0x45,0x64,0x41,0xdd,0x81,
-0xe9,0x81,0xbd,0xd1,0xa5,0xd9,0xda,0x82,0xcd,0x81,0x62,0x45,0xfa,0x02,0x3a,0x60,
-0x26,0x63,0xbd,0xd3,0x00,0x61,0x60,0x45,0xbd,0xd3,0xff,0xff,0x7f,0xb4,0x02,0x36,
-0x01,0xb9,0x04,0x36,0x02,0xb9,0x0b,0x36,0x04,0xb9,0x16,0x36,0x08,0xb9,0x65,0x44,
-0xcc,0x84,0x60,0x45,0xf1,0x02,0xc9,0xf1,0x61,0x44,0xa0,0x84,0xc9,0xfb,0x00,0x61,
-0x60,0x45,0x3a,0x60,0x28,0x63,0x65,0x40,0x01,0x2a,0x03,0x00,0x02,0x64,0xbd,0xdb,
-0xdd,0x81,0x65,0x40,0x02,0x2a,0x03,0x00,0x04,0x64,0xbd,0xdb,0xdd,0x81,0x65,0x40,
-0x04,0x2a,0x03,0x00,0x0b,0x64,0xbd,0xdb,0xdd,0x81,0x65,0x40,0x08,0x2a,0x03,0x00,
-0x16,0x64,0xbd,0xdb,0xdd,0x81,0x3a,0x60,0x26,0x63,0x61,0x44,0xbd,0xdb,0x17,0x60,
-0xd2,0x65,0x61,0x44,0xa5,0xdb,0xda,0x82,0x62,0x45,0xbd,0xd3,0xbd,0xd1,0x60,0x47,
-0xb0,0x87,0xa5,0xda,0xda,0x85,0xcd,0x81,0xcd,0x81,0x01,0x03,0xf6,0x02,0x3a,0x60,
-0x32,0x63,0xbd,0xd1,0x17,0x60,0xdc,0x62,0xa2,0xd3,0xff,0xff,0xa0,0x84,0xa2,0xdb,
-0x60,0x45,0xbd,0xd3,0x7f,0xfb,0xff,0xff,0xbd,0xd3,0x86,0xfb,0x65,0x44,0x00,0x65,
-0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,0x04,0x26,0x02,0x65,0x60,0x40,0x08,0x26,
-0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,0xa2,0xdb,0x16,0x60,0x88,0x63,0xa3,0xd1,
-0x17,0x60,0x06,0x63,0xa3,0xd9,0x86,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x02,0x02,
-0xa3,0xdb,0x05,0x00,0xfd,0xa0,0xff,0xff,0x02,0x05,0x03,0x64,0x86,0xfb,0xff,0x60,
-0xff,0x64,0x62,0xfb,0x76,0xf5,0x66,0x60,0x58,0x4e,0x3a,0x78,0xff,0xff,0x17,0x60,
-0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0x02,0x00,0x00,0x64,0x09,0x00,
-0x02,0x2a,0x02,0x00,0x01,0x64,0x05,0x00,0x04,0x2a,0x02,0x00,0x02,0x64,0x01,0x00,
-0x03,0x64,0x13,0xfa,0x77,0xf5,0x13,0xfa,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,
-0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,0xff,0xff,0x81,0xf1,0x31,0xf8,0x82,0xf1,
-0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x66,0x60,0xa6,0x63,0x03,0x64,0xcb,0xfb,
-0xa3,0xdb,0x00,0x64,0x7f,0xf1,0x85,0xfb,0x44,0x4a,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,
-0xa2,0xdb,0x47,0x60,0x62,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x3c,0x60,
-0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,
-0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x47,0x60,0x88,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xbe,0xfe,
-0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,
-0x01,0x64,0x8c,0xfb,0x02,0x64,0x90,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,0x70,0x64,
-0xa2,0xdb,0x20,0x44,0x10,0x26,0x05,0x00,0x01,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,
-0xff,0xff,0x20,0x44,0xef,0xb4,0x40,0x40,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,
-0x02,0x64,0xa2,0xdb,0x47,0x60,0xbd,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3d,0x60,0x1c,0x62,0xa2,0xd3,0x10,0x61,
-0x00,0x63,0x60,0x45,0xe0,0x84,0xa2,0xdb,0x65,0x44,0x01,0x26,0xdf,0x83,0xcd,0x81,
-0xe8,0x84,0xfb,0x02,0x63,0x44,0xfc,0xa0,0xff,0xff,0x0d,0x04,0x3d,0x60,0x1e,0x62,
-0x32,0x64,0xa2,0xdb,0x62,0xf3,0xff,0xff,0x60,0x40,0x00,0x36,0x13,0x00,0x17,0x60,
-0x06,0x62,0x00,0x64,0xa2,0xdb,0x3d,0x60,0x1e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,
-0xcc,0x84,0x08,0x03,0xa2,0xdb,0x06,0x02,0x3d,0x60,0x20,0x62,0xa2,0xd1,0x17,0x60,
-0x06,0x62,0xa2,0xd9,0x65,0xf1,0x00,0x64,0x6a,0xfb,0x6b,0xfb,0xff,0xff,0x6d,0xfb,
-0x6f,0xfb,0x64,0x40,0x02,0x26,0x03,0x00,0x4a,0x60,0xfc,0x78,0xff,0xff,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x66,0x60,0xb2,0x65,0x86,0xf3,0xa4,0xf1,0xa5,0xdb,0xff,0xff,0xff,0x22,0x06,0x00,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc0,0x84,0x6c,0xfb,0x5e,0xf3,0xff,0xff,
-0x04,0xb0,0xff,0xff,0x15,0x03,0x02,0x64,0x8c,0xfb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,0x80,0x60,0x00,0x64,
-0xa2,0xdb,0x48,0x60,0x30,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,
-0x49,0x60,0xcc,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,
-0xa0,0x84,0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xef,0x02,0x1e,0x60,
-0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0x6e,0xf3,0x60,0x40,0x02,0x26,0x32,0x00,0x76,0xf5,0x80,0x64,0x29,0xfa,
-0x00,0x63,0x38,0xf2,0x22,0xfc,0x17,0xfa,0x1c,0x64,0x21,0xfa,0x15,0xfc,0x16,0xfc,
-0x01,0x64,0x14,0xfa,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x48,0x60,
-0x72,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x86,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x2f,0x03,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x25,0x03,0x40,0x48,0x00,0x64,0x40,0x4c,0x3c,0x60,0x2a,0x62,
-0xa2,0xd3,0x28,0x45,0xd4,0x80,0x60,0x46,0x23,0x03,0x2b,0xf2,0xff,0xff,0xff,0xff,
-0x01,0x2a,0x14,0x00,0x09,0xf2,0xff,0xff,0x40,0x4d,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0x01,0x64,0x40,0x4c,0x2d,0x44,0x00,0xbc,0x60,0x46,0xe0,0x03,0x0a,0xf2,0xe1,0x00,
-0x49,0x60,0x99,0x78,0xff,0xff,0x01,0x64,0x6d,0xfb,0x49,0x60,0xc2,0x78,0xff,0xff,
-0x2c,0x44,0x01,0x2a,0x11,0x00,0x28,0x46,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,
-0x0b,0x00,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0e,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,0x31,0x00,0x4b,0x60,
-0x58,0x4e,0x03,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,
-0x48,0x60,0xf3,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x77,0xf5,0x22,0xf2,0xff,0xff,0xff,0xff,0x0f,0x26,0x02,0x00,0x01,0x64,0x6d,0xfb,
-0x01,0x64,0x6f,0xfb,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,
-0x40,0x48,0x00,0xbc,0x60,0x46,0x03,0x02,0x49,0x60,0x99,0x78,0xff,0xff,0x6c,0xf3,
-0xa4,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x7f,0x0e,0x2b,0xf2,0xff,0xff,0x01,0xb0,
-0x19,0xf2,0x77,0x02,0x00,0xbc,0x60,0x43,0x6b,0x03,0xa3,0xd3,0xff,0xff,0xff,0xff,
-0x02,0x2a,0x63,0x00,0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x6a,0x03,0x69,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x03,0x66,0x44,0x69,0xfb,0x4b,0x60,0x58,0x4e,
-0x0a,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x49,0x60,
-0x52,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x77,0xf5,
-0x22,0xf2,0x69,0xf5,0x0f,0xb0,0x46,0x48,0x07,0x02,0x69,0x60,0x58,0x4e,0xb9,0x78,
-0xff,0xff,0x00,0x64,0x15,0xfa,0x21,0x00,0x69,0x60,0x58,0x4e,0xcc,0x78,0xff,0xff,
-0x15,0xf2,0xff,0xff,0x01,0xa4,0xe7,0xa0,0x15,0xfa,0x23,0x04,0x66,0x60,0xb0,0x62,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x09,0xf2,0xff,0xff,0x40,0x48,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x4c,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xce,0xfe,0x28,0x44,0x83,0x00,0x01,0x64,0x6f,0xfb,0x09,0x00,
-0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,0xb9,0x78,
-0xff,0xff,0x28,0x46,0x09,0xf2,0xf0,0x00,0x6c,0xf3,0xa4,0xf1,0xff,0xff,0xd0,0x84,
-0xff,0xff,0x23,0x0e,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x60,0x40,0xff,0x22,
-0x01,0x64,0x60,0x43,0x28,0x60,0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x04,0x64,0xa2,0xdb,0x49,0x60,0xbe,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x6d,0xf3,0xff,0xff,0x02,0xbc,
-0x6d,0xfb,0x00,0x64,0x6a,0xfb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x66,0x60,
-0x9a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x3c,0x03,0x10,0xb4,0x20,0x45,
-0xb4,0x84,0x40,0x40,0x66,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x8c,0xf3,0xff,0xff,0x00,0xa0,0x02,0x64,0x2a,0x03,0x8c,0xfb,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,0x80,0x60,
-0x00,0x64,0xa2,0xdb,0x49,0x60,0xf6,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,
-0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x45,0x60,
-0x93,0x78,0xff,0xff,0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x40,0x03,0x22,
-0x3d,0x00,0x01,0x2a,0x0c,0x00,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,0xf0,0x65,
-0xad,0xf3,0xa5,0xd3,0x60,0x45,0xc4,0x84,0xd0,0x80,0xff,0xff,0x26,0x0d,0x67,0x60,
-0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x45,0xfd,0xb4,0xa2,0xdb,0x20,0x44,0xb4,0x84,
-0x40,0x40,0x59,0x60,0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,
-0x50,0x62,0x56,0x60,0x53,0x64,0xa2,0xdb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,
-0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x4a,0x60,
-0x4a,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,0x71,0xf3,0x70,0xf3,
-0x9c,0xa0,0xff,0xff,0x7f,0x04,0x64,0x64,0x71,0xfb,0x66,0x60,0xa8,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x76,0x02,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,
-0x44,0x02,0x8c,0xf3,0xff,0xff,0xff,0xa0,0x02,0x64,0x2a,0x02,0x8c,0xfb,0x1e,0x60,
-0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,
-0x80,0x60,0x00,0x64,0xa2,0xdb,0x4a,0x60,0x7f,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,
-0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x68,0x60,0xcc,0x62,0x90,0x60,0x71,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x64,0xa0,0xd3,
-0xff,0xff,0xfd,0xa0,0xff,0xff,0x09,0x02,0x02,0x64,0xa2,0xdb,0x00,0x64,0x70,0xfb,
-0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x66,0x60,0x96,0x65,0xa5,0xd1,
-0x01,0x60,0xf4,0x64,0x64,0x43,0xdf,0x83,0xd0,0x80,0xa5,0xdd,0x48,0x05,0x66,0x60,
-0x98,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0xa5,0xdb,0x40,0x03,0xd5,0xf3,
-0x03,0x7c,0x00,0xa0,0xff,0xff,0x13,0x03,0xd5,0xf9,0x0a,0x65,0x3d,0x60,0x58,0x4e,
-0x32,0x78,0xff,0xff,0x0c,0x60,0x50,0x62,0x56,0x60,0x53,0x64,0xa2,0xdb,0x1f,0x60,
-0x1c,0x63,0x0e,0x64,0x00,0x7c,0xbd,0xd9,0xcc,0x84,0xff,0xff,0xfc,0x02,0x45,0x60,
-0x93,0x78,0xff,0xff,0xfd,0xa0,0xff,0xff,0x22,0x04,0x66,0x60,0x96,0x62,0x00,0x64,
-0xa2,0xdb,0x70,0xfb,0x66,0x60,0x98,0x62,0x01,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x64,
-0xa0,0xd3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x12,0x03,0x03,0x64,0xa2,0xdb,0x03,0x64,
-0xcb,0xfb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x70,0x64,0xa2,0xdb,0x01,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x69,0x60,
-0x58,0x4e,0x58,0x78,0xff,0xff,0x47,0x60,0xb2,0x78,0xff,0xff,0x77,0xf5,0xff,0x60,
-0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0x0b,0x00,0x77,0xf1,0x2b,0xf2,0x2c,0xf2,
-0x60,0x43,0x64,0x46,0x2c,0xfa,0x2b,0xfc,0x28,0x46,0x2d,0xf2,0x64,0x46,0x2d,0xfa,
-0x90,0x64,0x29,0xfa,0x47,0xf3,0xff,0xff,0xe8,0x84,0xe8,0x84,0x1c,0xfa,0x00,0x63,
-0x38,0xf2,0x22,0xfc,0xff,0xff,0x19,0xfc,0x16,0xfc,0x17,0xfa,0x1c,0x64,0x21,0xfa,
-0x0e,0x64,0x15,0xfa,0x01,0x64,0x14,0xfa,0x2e,0x58,0xff,0xff,0x3a,0x60,0x02,0x62,
-0xa2,0xd3,0x43,0xf1,0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x90,0x84,
-0x01,0xb4,0xc5,0xf1,0x0d,0x02,0x61,0x44,0x90,0x84,0x20,0xb4,0xff,0xff,0x08,0x03,
-0x64,0x44,0x20,0x2a,0x04,0x00,0x00,0x64,0x47,0xfb,0x00,0xbc,0x01,0x00,0x01,0xbc,
-0x2e,0x58,0xff,0xff,0x4c,0x60,0x3f,0x78,0xff,0xff,0x78,0xf5,0x51,0x60,0x58,0x4e,
-0x28,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0xb2,0x78,0xff,0xff,0x85,0xf1,0x10,0x67,
-0xa0,0x84,0xb0,0xbc,0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0x06,0x63,
-0x38,0xfc,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x66,0x45,
-0x00,0xf4,0x04,0x61,0x7d,0xf1,0x00,0x64,0x64,0x40,0x02,0x26,0x01,0x64,0xa1,0xda,
-0x01,0x63,0x59,0xdc,0x00,0x64,0x59,0xda,0xa7,0xf1,0x28,0x60,0x7e,0x62,0xa2,0xd9,
-0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0xf4,0x96,0x7e,0x00,0x00,0x10,
-0x65,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x01,0x64,
-0x5d,0xfb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,0x4b,0x60,
-0x97,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0x2e,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,
-0x38,0xf2,0x00,0xf4,0x04,0xf2,0x60,0x41,0x00,0xa8,0xff,0xff,0x03,0x03,0x4c,0x60,
-0x30,0x78,0xff,0xff,0x7d,0xf3,0xff,0xff,0x02,0xb4,0x03,0xf2,0x03,0x02,0x4c,0x60,
-0x02,0x78,0xff,0xff,0x02,0xa8,0x88,0x65,0x38,0x02,0xd5,0x80,0xff,0xff,0x04,0x05,
-0x4c,0x60,0x30,0x78,0xff,0xff,0x43,0x00,0x26,0x46,0x51,0x60,0x58,0x4e,0x28,0x78,
-0xff,0xff,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x08,0x64,0x28,0xfa,0x85,0xf1,0x10,0x67,
-0xa0,0x84,0xb0,0xbd,0x40,0x67,0xb4,0x84,0x29,0xfa,0x2b,0xf0,0x2e,0xf2,0xff,0xff,
-0x2e,0xf8,0x2b,0xfa,0x2c,0xf0,0xff,0xff,0x2f,0xf2,0x2f,0xf8,0x2c,0xfa,0xff,0xff,
-0x2d,0xf0,0x30,0xf2,0x30,0xf8,0xff,0xff,0x2d,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,
-0x58,0x4e,0x14,0x78,0xff,0xff,0x66,0x45,0x00,0xf4,0x04,0x61,0x01,0x64,0xa1,0xda,
-0x03,0x63,0x59,0xdc,0x4b,0x60,0x71,0x78,0xff,0xff,0x04,0xa8,0xff,0xff,0x2e,0x02,
-0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,
-0x03,0x03,0x4c,0x60,0xde,0x78,0xff,0xff,0x4f,0x60,0xbb,0x78,0xff,0xff,0x00,0x60,
-0x04,0x64,0xa0,0x80,0x9c,0x84,0x17,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x64,0x5d,0xfb,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x51,0x60,0x58,0x4e,0xc1,0x78,
-0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,0x03,0x03,0x4c,0x60,0xde,0x78,0xff,0xff,
-0x4f,0x60,0xbb,0x78,0xff,0xff,0x4b,0x60,0x95,0x78,0xff,0xff,0x26,0x46,0x3e,0x60,
-0x58,0x4e,0x91,0x78,0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,0x03,0x03,0x4c,0x60,
-0x59,0x78,0xff,0xff,0x4e,0x60,0xac,0x78,0xff,0xff,0xa7,0xf1,0x28,0x60,0x7e,0x62,
-0xa2,0xd9,0x79,0xf5,0x51,0x60,0x58,0x4e,0xb2,0x78,0xff,0xff,0x00,0x60,0xb8,0x63,
-0x27,0x60,0xde,0x64,0xa3,0xdb,0x61,0x60,0xbc,0x63,0xbd,0xd3,0xbd,0xd1,0xff,0xff,
-0xb0,0x84,0xa3,0xd1,0xff,0xff,0xb0,0x83,0x61,0x60,0xba,0x62,0xa2,0xdd,0x00,0x60,
-0xb8,0x63,0x01,0x60,0x10,0x65,0xa3,0xd3,0xa5,0xd1,0x04,0xa4,0xa3,0xdb,0xd0,0x80,
-0xa0,0xd3,0x07,0x07,0x40,0x47,0x52,0x60,0x58,0x4e,0x11,0x78,0xff,0xff,0xef,0x02,
-0x0c,0x00,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x66,0x60,0xc2,0x65,
-0x00,0x64,0xa5,0xdb,0x44,0x60,0x83,0x78,0xff,0xff,0x27,0x43,0x3c,0xa3,0xa3,0xd1,
-0x67,0x60,0x48,0x63,0xc9,0xf3,0xa3,0xd9,0xa0,0x84,0xd0,0x80,0xff,0xff,0xd7,0x02,
-0x27,0x43,0x40,0xa3,0xa3,0xd3,0xff,0xff,0x01,0xa0,0x60,0x41,0x05,0x02,0x3e,0x60,
-0x58,0x4e,0xae,0x78,0xff,0xff,0x0d,0x00,0x17,0x60,0x46,0x62,0xa2,0xd3,0xff,0xff,
-0x60,0x45,0xd5,0x84,0x60,0x45,0x01,0x0d,0x00,0x65,0x3e,0x60,0x58,0x4e,0xd6,0x78,
-0xff,0xff,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,0x00,0xa0,0x05,0x03,0x04,0x03,0x67,0x60,
-0x80,0x62,0x01,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,
-0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x4c,0x60,
-0x77,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x27,0xd1,0x3c,0x60,0xa2,0x62,
-0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,
-0x20,0x60,0x00,0x64,0xa2,0xdb,0x4c,0x60,0xcb,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x20,0x44,0x08,0xbc,
-0x40,0x40,0x4b,0x60,0x4a,0x78,0xff,0xff,0x79,0xf5,0x51,0x60,0x58,0x4e,0x28,0x78,
-0xff,0xff,0x00,0x64,0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0xff,0xff,
-0x47,0xf1,0x00,0xf4,0x04,0x62,0x00,0x60,0x01,0x64,0xb0,0x84,0xa2,0xda,0x0f,0x63,
-0x04,0x61,0x59,0xdc,0x50,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x79,0xf5,0x2d,0x44,
-0x08,0xa4,0x38,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,
-0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x06,0x64,0x5d,0xfb,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xc4,0x62,0x00,0x60,0x1c,0x64,0xa2,0xdb,0x4d,0x60,0x24,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xa0,0x80,
-0x9c,0x84,0x21,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x63,0x5d,0xfd,0x1e,0x60,
-0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,0x00,0xf4,0x03,0xf2,0xff,0xff,
-0x00,0xb8,0xff,0xff,0x3d,0x03,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,
-0x4c,0x60,0x59,0x78,0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0x1a,0x03,
-0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,
-0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x4c,0x60,
-0xd8,0x78,0xff,0xff,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x10,0x03,0xa0,0x84,
-0xa2,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,
-0x51,0x60,0x58,0x4e,0xc1,0x78,0xff,0xff,0x4c,0x60,0x59,0x78,0xff,0xff,0xa0,0x00,
-0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x51,0x60,0x58,0x4e,0x3d,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0x54,0x78,
-0xff,0xff,0x5a,0x60,0x58,0x4e,0xdb,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x48,0x78,
-0xff,0xff,0x51,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0x27,0x43,0x11,0x61,0x10,0x65,
-0xc7,0x83,0x59,0x60,0x1e,0x64,0xbd,0xd1,0xcd,0x81,0x58,0xd9,0xfc,0x02,0x26,0x46,
-0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,
-0x01,0x64,0x90,0xfb,0x01,0x67,0x85,0xfb,0x04,0x64,0xcb,0xfb,0x01,0x65,0x3d,0x60,
-0x58,0x4e,0x32,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x74,0x78,0xff,0xff,0x1e,0x60,
-0xb0,0x62,0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x0c,0x64,0x5d,0xfb,0x1e,0x60,0xc4,0x62,0x00,0x60,0xf0,0x64,0xa2,0xdb,
-0x4d,0x60,0xe0,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x00,0x60,0x80,0x64,0xa0,0x80,0x9c,0x84,0x07,0x03,0xa0,0x84,0xa2,0xdb,
-0x61,0x60,0xc8,0x62,0x0e,0x64,0xa2,0xdb,0x17,0x00,0x00,0x60,0x40,0x64,0xa0,0x80,
-0x9c,0x84,0x07,0x03,0xa0,0x84,0xa2,0xdb,0x61,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,
-0x0b,0x00,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0x49,0x03,0xa0,0x84,0xa2,0xdb,
-0x61,0x60,0xc8,0x62,0x0a,0x64,0xa2,0xdb,0x5e,0xf3,0xff,0xff,0x01,0xb0,0xff,0xff,
-0x14,0x03,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,0x08,0x60,0x00,0x64,0xa2,0xdb,0x4e,0x60,
-0x1f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0x92,0x62,0xa2,0xd1,0x61,0x60,
-0xd2,0x62,0xa2,0xd9,0x1e,0x60,0x94,0x62,0xa2,0xd1,0x61,0x60,0xd4,0x62,0xa2,0xd9,
-0x4e,0x60,0x58,0x4e,0x5d,0x78,0xff,0xff,0x02,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,
-0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x66,0x60,0xc2,0x65,0x64,0x60,
-0x58,0x62,0x00,0x64,0xa2,0xdb,0xa5,0xdb,0x44,0x60,0x83,0x78,0xff,0xff,0x00,0x60,
-0x20,0x64,0xa0,0x80,0x9c,0x84,0x0a,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x63,0x5d,0xfd,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x1b,0x00,0x4d,0x60,0xde,0x78,
-0xff,0xff,0x2f,0x58,0xff,0xff,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,
-0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x2e,0x58,
-0xff,0xff,0x02,0x64,0x8c,0xfb,0x47,0xf3,0x46,0xfb,0x00,0x60,0xfe,0x65,0x00,0x60,
-0xfc,0x63,0xa5,0xd3,0xa3,0xdb,0xa7,0xf1,0x28,0x60,0x7e,0x62,0xa2,0xd9,0x8c,0xf3,
-0x00,0x65,0xd4,0x80,0xff,0xff,0x0c,0x03,0x1e,0x60,0xc4,0x62,0x80,0x60,0x00,0x64,
-0xa2,0xdb,0x4e,0x60,0x81,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x00,0x60,0xb8,0x63,0x27,0x60,
-0xde,0x64,0xa3,0xdb,0x64,0x60,0x5a,0x62,0x00,0x64,0xa2,0xdb,0x61,0x60,0xbc,0x63,
-0xbd,0xd3,0xbd,0xd1,0xff,0xff,0xb0,0x84,0xa3,0xd1,0xff,0xff,0xb0,0x83,0x61,0x60,
-0xba,0x62,0xa2,0xdd,0x00,0x60,0xb8,0x63,0x01,0x60,0x10,0x65,0xa3,0xd3,0xff,0xff,
-0x02,0xa4,0xa0,0xd1,0xff,0xff,0x64,0x41,0xa5,0xd1,0x02,0xa4,0xa3,0xdb,0xd0,0x80,
-0xa0,0xd3,0x49,0x07,0x40,0x47,0xc4,0xf1,0x58,0xf3,0xff,0xff,0xc0,0x85,0xd5,0x80,
-0x60,0x45,0x0e,0x05,0x61,0x60,0xc8,0x62,0xa2,0xd3,0x1f,0xf1,0xfc,0xa0,0x65,0x44,
-0x3a,0x02,0x64,0x40,0x10,0x2a,0x37,0x00,0x03,0xa5,0xd5,0x80,0x34,0x04,0x07,0x00,
-0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0xff,0xff,0x13,0x02,0x66,0x60,
-0xbe,0x62,0xa2,0xd1,0x27,0x44,0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x02,0xa4,
-0xd0,0x80,0xff,0xff,0xc7,0x07,0xe1,0x85,0xc5,0x85,0x64,0x44,0xe0,0x84,0xd4,0x80,
-0xff,0xff,0xc0,0x04,0x52,0x60,0x58,0x4e,0x11,0x78,0xff,0xff,0xbb,0x02,0x27,0x44,
-0x06,0xa4,0x60,0x41,0xa1,0xd1,0x81,0xf3,0xff,0xff,0xd0,0x80,0x82,0xf1,0x59,0xd3,
-0x68,0x02,0xd0,0x80,0x83,0xf3,0x59,0xd1,0x64,0x02,0xd0,0x80,0xff,0xff,0x61,0x02,
-0x4e,0x60,0xac,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,
-0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,
-0x05,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x46,0xf3,0x47,0xfb,0x00,0x60,
-0xfc,0x63,0xa3,0xd1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,
-0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,
-0x2f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,
-0xa2,0xdb,0xcf,0xfe,0x64,0x60,0x5a,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x10,0x02,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x09,0x02,
-0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x01,0x63,0x8c,0xfd,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x4d,0x60,0xd3,0x78,
-0xff,0xff,0x27,0x43,0x3c,0xa3,0xa3,0xd1,0xc9,0xf3,0xff,0xff,0xa0,0x84,0xd0,0x80,
-0xff,0xff,0x03,0x03,0x4e,0x60,0xac,0x78,0xff,0xff,0x27,0x43,0x40,0xa3,0xa3,0xd3,
-0xff,0xff,0x01,0xa0,0x60,0x41,0x05,0x02,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,
-0x0d,0x00,0x17,0x60,0x46,0x62,0xa2,0xd3,0xff,0xff,0x60,0x45,0xd5,0x84,0x60,0x45,
-0x01,0x0d,0x00,0x65,0x3e,0x60,0x58,0x4e,0xd6,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x4f,0x60,0x6f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x27,0xd1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,0xae,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,
-0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x79,0xf5,0x51,0x60,0x58,0x4e,0x28,0x78,0xff,0xff,0x00,0x60,0x20,0x64,
-0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0xb2,0x78,
-0xff,0xff,0x00,0xf4,0x04,0x61,0x47,0xf1,0x01,0x64,0xb0,0x84,0xa1,0xda,0x0f,0x64,
-0x59,0xda,0x81,0xf1,0xff,0xff,0x59,0xd8,0x82,0xf1,0x59,0xd8,0xff,0xff,0x83,0xf1,
-0x59,0xd8,0x50,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x79,0xf5,0x2d,0x44,0x0e,0xa4,
-0x38,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x14,0x64,0x5d,0xfb,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x1c,0x64,0xa2,0xdb,0x50,0x60,0x0b,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,
-0x14,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x51,0x60,0x58,0x4e,0xc1,0x78,0xff,0xff,0x64,0x60,0x5a,0x62,
-0x01,0x64,0xa2,0xdb,0x4e,0x60,0xac,0x78,0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,
-0x9c,0x84,0x15,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,0x5d,0xfb,0x1e,0x60,
-0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x50,0x60,0xad,0x78,0xff,0xff,0x00,0x60,
-0x08,0x64,0xa0,0x80,0x9c,0x84,0x22,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,
-0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,0x00,0xf4,
-0xff,0xff,0x03,0xf2,0xff,0xff,0x00,0xb8,0xff,0xff,0x09,0x03,0x26,0x46,0x3e,0x60,
-0x58,0x4e,0x91,0x78,0xff,0xff,0x4e,0x60,0xac,0x78,0xff,0xff,0xa0,0x00,0x1e,0x60,
-0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x51,0x60,0x58,0x4e,0x3d,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0x54,0x78,0xff,0xff,
-0x5a,0x60,0x58,0x4e,0xdb,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x48,0x78,0xff,0xff,
-0x51,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,
-0xff,0xff,0x03,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,
-0x74,0x78,0xff,0xff,0x01,0x63,0x8c,0xfd,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xb0,0x62,
-0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x4d,0x60,0xd3,0x78,0xff,0xff,0x20,0x44,0xf7,0xb4,0x40,0x40,0x4b,0x60,0x4a,0x78,
-0xff,0xff,0x5d,0xf1,0x29,0xf2,0x64,0x41,0x60,0x40,0xa0,0x3a,0x0d,0x00,0x08,0xb1,
-0xff,0xff,0x31,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x27,0x00,0xc0,0x3a,0x0d,0x00,0x04,0xb1,0xff,0xff,
-0x22,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x18,0x00,0xb0,0x3a,0x02,0x00,0x01,0x65,0x07,0x00,0x10,0x3a,
-0x02,0x00,0x02,0x65,0x03,0x00,0x30,0x3a,0x0e,0x00,0x10,0x65,0xa5,0x80,0xff,0xff,
-0x0a,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x00,0x66,0x2f,0x58,0xff,0xff,0x27,0x43,0x12,0xa3,0xbf,0xd1,
-0xff,0xff,0x64,0x47,0x59,0xda,0x64,0x41,0xdd,0x81,0xe9,0x81,0x62,0x44,0x04,0x03,
-0xbd,0xd1,0xcd,0x81,0x58,0xd8,0xfc,0x02,0x58,0x8d,0x17,0x60,0xd2,0x63,0xa3,0xd1,
-0x2d,0x44,0xc8,0x84,0x64,0x45,0x64,0x41,0x03,0xa1,0xe9,0x81,0x41,0x4c,0xbd,0xd1,
-0xcd,0x81,0x58,0xd8,0xfc,0x02,0x2d,0xd2,0x2d,0x43,0x60,0x47,0x01,0x7e,0xa3,0xda,
-0x27,0x44,0x10,0xa4,0xa0,0xd3,0xcb,0x83,0x44,0x8d,0xf8,0x84,0x2c,0x41,0x0c,0x04,
-0xbe,0xd2,0xff,0xff,0x60,0x47,0xbe,0xda,0x00,0x7e,0xa3,0xd2,0x60,0x45,0x00,0x7f,
-0xb4,0x84,0xcd,0x81,0xbd,0xda,0xf4,0x02,0x2e,0x58,0xff,0xff,0x67,0x60,0x48,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0x02,0x00,0x00,0x64,0x09,0x00,0x02,0x2a,
-0x02,0x00,0x01,0x64,0x05,0x00,0x04,0x2a,0x02,0x00,0x02,0x64,0x01,0x00,0x03,0x64,
-0x13,0xfa,0x2e,0x58,0xff,0xff,0xff,0x60,0xff,0x65,0x66,0x60,0xbe,0x63,0x27,0x42,
-0x3e,0xa2,0xa2,0xd3,0x7f,0x7c,0xa3,0xdb,0xd4,0x80,0x01,0x61,0x02,0x02,0x00,0x61,
-0x65,0x5c,0x66,0x60,0xc0,0x62,0x61,0x44,0xa2,0xdb,0x66,0x60,0xbc,0x62,0xa2,0xd9,
-0x2e,0x58,0xff,0xff,0x27,0x42,0x32,0xa2,0x00,0x61,0x00,0x63,0xa2,0xd3,0xff,0xff,
-0x00,0xbc,0xe0,0x84,0x24,0x03,0x04,0x3a,0x02,0x00,0x01,0xb9,0x1e,0x00,0x08,0x3a,
-0x0a,0x00,0x02,0xb9,0x60,0x40,0x01,0x2b,0x18,0x00,0x01,0x65,0xd7,0x80,0xff,0xff,
-0x14,0x05,0x01,0x63,0x12,0x00,0x16,0x3a,0x0a,0x00,0x04,0xb9,0x60,0x40,0x01,0x2b,
-0x0c,0x00,0x02,0x65,0xd7,0x80,0xff,0xff,0x08,0x05,0x02,0x63,0x06,0x00,0x2c,0x3a,
-0x04,0x00,0x08,0xb9,0x60,0x40,0x01,0x27,0x03,0x63,0x02,0xa2,0xd7,0x00,0x59,0x60,
-0x68,0x62,0xa2,0xdd,0xc9,0xf1,0x59,0x60,0x6a,0x63,0xa1,0x84,0xa3,0xdb,0x60,0x40,
-0x08,0x2a,0x03,0x00,0x03,0x63,0x08,0x64,0x0c,0x00,0x04,0x2a,0x03,0x00,0x02,0x63,
-0x04,0x64,0x07,0x00,0x02,0x2a,0x03,0x00,0x01,0x63,0x02,0x64,0x02,0x00,0x00,0x63,
-0x01,0x64,0x50,0xfb,0x51,0xf1,0x61,0x60,0xd0,0x62,0xa2,0xd9,0x51,0xfd,0x2e,0x58,
-0xff,0xff,0x27,0x43,0x06,0xa3,0xbd,0xd1,0x2b,0xf8,0x31,0xf8,0xff,0xff,0xbd,0xd1,
-0x2c,0xf8,0x32,0xf8,0xff,0xff,0xa3,0xd1,0x2d,0xf8,0x33,0xf8,0x2e,0x58,0xff,0xff,
-0xbd,0xf1,0xff,0xff,0x2e,0xf8,0xbe,0xf1,0x2f,0xf8,0xff,0xff,0xbf,0xf1,0x30,0xf8,
-0xf0,0x60,0x20,0x64,0x0e,0xfa,0x08,0x64,0x28,0xfa,0x2e,0x58,0xff,0xff,0x67,0x60,
-0x66,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xff,0xff,0xa2,0xdb,0x28,0x60,0x60,0x62,
-0x01,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x28,0x60,0x60,0x62,0x00,0x64,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x27,0x43,0x02,0x65,0xc7,0x85,0xa5,0xd3,0xff,0xff,0x60,0x47,
-0x5a,0xfb,0x04,0x65,0xc7,0x85,0xa5,0xd3,0xff,0xff,0x60,0x47,0x59,0xfb,0x0c,0x65,
-0xc7,0x85,0xa5,0xd3,0x80,0xfb,0xf1,0xa4,0xab,0xfb,0xab,0xf1,0x28,0x60,0xd2,0x62,
-0xa2,0xd9,0x3c,0x60,0xd2,0x65,0x04,0xf0,0x3f,0x60,0xff,0x64,0x84,0xf9,0xa0,0x84,
-0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xa5,0xdb,0x3c,0x60,0xd0,0x65,0x01,0x64,
-0x07,0xb1,0x03,0x00,0xe0,0x84,0xcd,0x81,0xff,0xff,0xfc,0x02,0xa5,0xdb,0x61,0x60,
-0xca,0x63,0x26,0x46,0x31,0xf0,0x81,0xf9,0xbd,0xd9,0xff,0xff,0x32,0xf0,0x82,0xf9,
-0xbd,0xd9,0xff,0xff,0x33,0xf0,0x83,0xf9,0xa3,0xd9,0x2e,0x58,0xff,0xff,0x27,0x44,
-0x0e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x43,0xf3,
-0xe8,0x85,0x94,0x84,0x01,0x26,0x26,0x00,0xc5,0xf1,0x1f,0xf3,0x91,0x80,0x20,0x2a,
-0x05,0x00,0x60,0x40,0x10,0x2a,0x1e,0x00,0x20,0xb1,0x61,0x5c,0x47,0xf9,0x61,0x60,
-0xba,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x12,0x03,0x50,0xfe,0x27,0x41,
-0x06,0xa1,0x61,0x60,0xbc,0x63,0xa1,0xd3,0xbd,0xd1,0x59,0xd3,0xd0,0x80,0xbd,0xd1,
-0x59,0xd3,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x01,0x02,0x00,
-0x00,0x64,0x01,0x00,0x01,0x64,0x00,0xbc,0x2e,0x58,0xff,0xff,0x66,0x60,0xfa,0x63,
-0x00,0x64,0xbd,0xdb,0x01,0x64,0xbd,0xdb,0xa3,0xdb,0x67,0x60,0x00,0x63,0x00,0x64,
-0xbd,0xdb,0x01,0x64,0xbd,0xdb,0xa3,0xdb,0x27,0x44,0x02,0xa4,0xa0,0xd1,0x27,0x44,
-0x04,0xa4,0xa0,0xd3,0xff,0xff,0xd0,0x81,0xff,0xff,0x01,0x05,0x00,0x61,0x66,0x60,
-0xf2,0x63,0x61,0x44,0xbd,0xdb,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd1,0xff,0xff,
-0xd1,0x81,0xff,0xff,0x01,0x05,0x00,0x61,0x66,0x60,0xf8,0x62,0x61,0x44,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x59,0x60,0x9c,0x62,0x66,0x60,0x2a,0x63,0x00,0x64,0xa2,0xdb,
-0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x67,0x60,0x84,0x62,0x00,0x64,
-0xa2,0xdb,0x2e,0x58,0xff,0xff,0x00,0x64,0x5d,0xfb,0x3c,0x60,0xb0,0x63,0x21,0x44,
-0xbd,0xdb,0xff,0xff,0x1d,0xff,0x01,0x64,0xcb,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x00,0x60,0x2a,0x63,0x0c,0x60,0x40,0x61,0x0e,0x60,0x7e,0x64,
-0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x7a,0xf5,0xbd,0xf1,0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,
-0x30,0xf8,0x1e,0x60,0x92,0x62,0x67,0x60,0x12,0x63,0xa2,0xd3,0xa3,0xdb,0x7f,0xf3,
-0x00,0x65,0x7e,0xfb,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,0x00,0xa0,0x05,0x03,0x04,0x03,
-0x67,0x60,0x80,0x62,0x01,0x64,0xa2,0xdb,0xcb,0xf3,0x14,0x7c,0xfc,0xa0,0x0f,0x64,
-0x0a,0x03,0xd5,0xf3,0x28,0x7c,0xfd,0xa0,0x60,0x45,0x2d,0x64,0x04,0x04,0x01,0x60,
-0x86,0x64,0x01,0x03,0x78,0x64,0xaf,0xf9,0xb1,0xfb,0x1e,0x60,0xbc,0x62,0x00,0x64,
-0xa2,0xdb,0x20,0x44,0x03,0x26,0x07,0x00,0x00,0x64,0x8a,0xfb,0x27,0x60,0xe0,0x64,
-0x88,0xfb,0x89,0xfb,0x0a,0x00,0x40,0x2a,0x04,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,
-0xb1,0xfb,0x3d,0x60,0x4c,0x62,0x00,0x64,0xa2,0xdb,0x65,0x44,0xfd,0xa0,0xff,0xff,
-0x71,0x05,0x20,0x40,0x40,0x26,0x6e,0x00,0x10,0x60,0x00,0x65,0x85,0xf3,0x7a,0xf5,
-0xa4,0x84,0x40,0x7e,0x29,0xfa,0x17,0x60,0xde,0x64,0xa0,0xd1,0x13,0xf8,0xff,0xff,
-0x5b,0xf3,0x00,0xf4,0x60,0x43,0xbd,0xd1,0x04,0x65,0x20,0x40,0x80,0x26,0x00,0x7c,
-0x64,0x47,0xa5,0xda,0x64,0x41,0xdd,0x81,0xe9,0x81,0x62,0x44,0x04,0x03,0xbd,0xd1,
-0xcd,0x81,0x58,0xd8,0xfc,0x02,0x58,0x8d,0x17,0x60,0xd2,0x63,0xa3,0xd1,0x2d,0x44,
-0xc8,0x84,0x64,0x45,0x64,0x41,0x03,0xa1,0xe9,0x81,0x41,0x4c,0xbd,0xd1,0xcd,0x81,
-0x58,0xd8,0xfc,0x02,0x2d,0xd2,0x2d,0x43,0x60,0x47,0x01,0x7e,0x5b,0xf1,0xa3,0xda,
-0xa4,0xd3,0xcb,0x83,0x20,0x40,0x80,0x26,0x00,0x64,0x44,0x8d,0xf8,0x84,0x2c,0x41,
-0x0c,0x04,0xbe,0xd2,0xff,0xff,0x60,0x47,0xbe,0xda,0x00,0x7e,0xa3,0xd2,0x60,0x45,
-0x00,0x7f,0xb4,0x84,0xcd,0x81,0xbd,0xda,0xf4,0x02,0x7a,0xf5,0x2d,0x44,0x04,0xa4,
-0x38,0xfa,0x66,0x60,0xc0,0x62,0xa2,0xd1,0x66,0x60,0xb8,0x62,0xa2,0xd9,0x1f,0x60,
-0x5a,0x63,0xbf,0xf3,0x71,0x5c,0x60,0x47,0xc0,0x84,0x1f,0xb5,0x01,0xb4,0xa3,0xdb,
-0x1f,0x60,0x56,0x62,0xa2,0xd3,0x65,0x41,0x60,0x45,0x61,0x44,0xd4,0x80,0xff,0xff,
-0x02,0x04,0xd4,0x84,0xfb,0x00,0x60,0x45,0x1f,0x60,0x54,0x62,0xa2,0xd3,0xff,0xff,
-0xc4,0x84,0x40,0x4a,0x1f,0x60,0x58,0x62,0x00,0x64,0xa2,0xdb,0x13,0x00,0xd5,0xf3,
-0xff,0xff,0xfd,0xa0,0xfc,0xa0,0x09,0x03,0x05,0x02,0x01,0x60,0x86,0x64,0xb1,0xfb,
-0x03,0x64,0xd5,0xfb,0x54,0x60,0xfb,0x78,0xff,0xff,0x04,0x64,0xd5,0xfb,0x52,0x60,
-0xf4,0xa6,0x7e,0x00,0x00,0x10,0xb3,0x78,0xff,0xff,0xd5,0xf1,0x1f,0x60,0x58,0x62,
-0xa2,0xd3,0xff,0xff,0xf2,0xa0,0xff,0xff,0xe5,0x03,0x01,0xa4,0xa2,0xdb,0x1f,0x60,
-0x5a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x07,0x03,0x2a,0x44,0xdc,0x84,
-0xf2,0xa0,0xff,0xff,0x08,0x06,0x01,0x64,0x06,0x00,0x2a,0x44,0xcc,0x84,0xff,0xa0,
-0xff,0xff,0x01,0x05,0x0e,0x64,0x40,0x4a,0x1f,0x60,0x1a,0x63,0x20,0x40,0x40,0x26,
-0x0e,0x00,0x64,0x44,0x04,0x36,0x0b,0x00,0x03,0x3a,0x02,0x00,0x1f,0x60,0x36,0x63,
-0x2a,0x44,0xe0,0x85,0x47,0xd3,0xff,0xff,0x01,0xb0,0xff,0xff,0xce,0x03,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xbe,0x62,
-0x40,0x60,0x00,0x64,0xa2,0xdb,0x53,0x60,0xae,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,0xa2,0xdb,
-0xff,0xff,0x2d,0xff,0x1e,0x60,0xbe,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x53,0x60,
-0xd4,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x02,0x0a,0x00,0x64,
-0x57,0xfb,0x20,0x44,0x40,0x2a,0x09,0x00,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x20,0x44,0x40,0x22,0x03,0x00,
-0x54,0x60,0x72,0x78,0xff,0xff,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0x7a,0xf5,0x7b,0x05,
-0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xaf,0xf1,0x28,0x60,0x66,0x62,
-0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0xc1,0xfe,0x1e,0x60,0xbe,0x62,0x00,0x60,0x05,0x64,0xa2,0xdb,
-0x54,0x60,0x25,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,
-0xa2,0xd1,0x00,0x60,0x01,0x64,0xa0,0x80,0x9c,0x84,0x0c,0x03,0xa0,0x84,0xa2,0xdb,
-0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,
-0x1d,0xff,0x11,0x00,0x67,0x60,0x10,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,
-0x28,0x60,0x60,0x62,0x01,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,0xa2,0xdb,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xb1,0xf1,
-0x28,0x60,0x66,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xbe,0x62,0x00,0x60,0x04,0x64,
-0xa2,0xdb,0x54,0x60,0x66,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x57,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x2e,0x02,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xb1,0xf1,
-0x28,0x60,0x66,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xbe,0x62,0x00,0x60,0x04,0x64,
-0xa2,0xdb,0x54,0x60,0x8e,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0xd5,0xf3,0xff,0xff,0xff,0xa0,0xff,0xff,0x03,0x03,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x52,0x60,0xb3,0x78,0xff,0xff,0xb1,0xf1,0x28,0x60,
-0x66,0x62,0xa2,0xd9,0xb2,0xf1,0x28,0x60,0x72,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,
-0x28,0x60,0x62,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,
-0xbe,0x62,0x28,0x60,0x6e,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xbe,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,0x54,0x60,0xc2,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,
-0xa0,0x80,0x9c,0x84,0x0e,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xbe,0x62,0x28,0x60,
-0x6e,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x53,0x60,0x7c,0x78,
-0xff,0xff,0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0xe3,0x03,0xa0,0x84,0xa2,0xdb,
-0x57,0xf3,0x10,0x0a,0x00,0xa0,0x00,0x64,0x0c,0x02,0x3c,0x60,0xba,0x62,0x28,0x60,
-0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x53,0x60,0x7c,0x78,
-0xff,0xff,0x57,0xfb,0x3c,0x60,0xbe,0x62,0x28,0x60,0x6e,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0xc5,0x00,0x1e,0x60,0x92,0x62,0x67,0x60,0x12,0x63,
-0xa2,0xd3,0xa3,0xd1,0xff,0xff,0xd0,0x85,0x67,0x60,0x0e,0x62,0xa2,0xd3,0xff,0xff,
-0xd4,0x80,0xff,0xff,0x02,0x05,0x65,0x44,0xa2,0xdb,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,
-0x00,0xa0,0x05,0x03,0x04,0x03,0x67,0x60,0x80,0x62,0x41,0x64,0xa2,0xdb,0x1e,0x60,
-0x94,0x62,0x66,0x60,0xb6,0x63,0xa2,0xd3,0xff,0xff,0x0c,0xa4,0xa3,0xdb,0x59,0x60,
-0xb2,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xbc,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xbe,0x62,0x40,0x60,0x00,0x64,
-0xa2,0xdb,0x55,0x60,0x24,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x7e,0xf1,
-0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,
-0x1e,0x60,0xbe,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x55,0x60,0x4a,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,
-0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x20,0x44,0x03,0x22,0x08,0x00,0x04,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x56,0x60,0x1b,0x78,0xff,0xff,0x8a,0xf1,
-0x1f,0x60,0x60,0x63,0xc3,0x85,0x45,0x4d,0x27,0x60,0xe0,0x65,0x89,0xf3,0x45,0x4c,
-0x40,0x48,0x59,0x60,0x8a,0x62,0x28,0x44,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xa2,0xdb,
-0x2d,0x45,0xd7,0x80,0x02,0x65,0x18,0x05,0x47,0xd1,0x02,0x65,0x47,0xd3,0x0a,0x65,
-0xd0,0x81,0x47,0xd3,0x01,0x05,0x00,0x61,0xf2,0xa3,0x01,0xb0,0x61,0x44,0x06,0x03,
-0x2c,0x42,0xa2,0xdb,0x5a,0xdd,0x5a,0x8c,0x44,0xa3,0xea,0x00,0x28,0x42,0x4a,0xdd,
-0x4a,0xdb,0x42,0x48,0x44,0xa3,0xe4,0x00,0x28,0x44,0x88,0xfb,0x88,0xf1,0x27,0x60,
-0xe0,0x63,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0x65,0xff,0xa0,0xff,0xff,0x01,0x03,
-0x00,0x65,0x45,0x4c,0x44,0x48,0x28,0x45,0xd7,0x80,0xa3,0xd1,0x36,0x05,0x5a,0xd3,
-0xff,0xff,0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,
-0xa4,0x85,0x64,0x47,0xb4,0x9c,0x63,0x42,0x04,0x65,0x46,0xd3,0x28,0x45,0xd6,0x80,
-0xff,0xff,0x02,0x04,0x04,0xa3,0xe7,0x00,0x62,0x46,0x60,0x41,0x5a,0xd3,0xff,0xff,
-0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,
-0x61,0x47,0xb4,0x84,0xd0,0x80,0x66,0x42,0xe7,0x06,0x60,0x41,0xa2,0xd3,0x5a,0xd1,
-0xa3,0xd1,0x64,0x45,0xbd,0xdb,0xa3,0xd3,0x66,0x42,0xa2,0xd9,0x5a,0xdb,0x65,0x44,
-0xa3,0xdb,0x61,0x5c,0xfe,0xa3,0x66,0x42,0xd7,0x00,0x88,0xf3,0x89,0xf1,0x60,0x43,
-0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0x65,0xff,0xa0,0xff,0xff,0x01,0x03,0x00,0x65,
-0x45,0x4c,0x44,0x48,0x28,0x45,0xd7,0x80,0xa3,0xd1,0x36,0x05,0x5a,0xd3,0xff,0xff,
-0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,
-0x64,0x47,0xb4,0x9c,0x63,0x42,0x04,0x65,0x46,0xd3,0x28,0x45,0xd6,0x80,0xff,0xff,
-0x02,0x04,0x04,0xa3,0xe7,0x00,0x62,0x46,0x60,0x41,0x5a,0xd3,0xff,0xff,0x3e,0xa4,
-0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,0x61,0x47,
-0xb4,0x84,0xd0,0x80,0x66,0x42,0xe7,0x06,0x60,0x41,0xa2,0xd3,0x5a,0xd1,0xa3,0xd1,
-0x64,0x45,0xbd,0xdb,0xa3,0xd3,0x66,0x42,0xa2,0xd9,0x5a,0xdb,0x65,0x44,0xa3,0xdb,
-0x61,0x5c,0xfe,0xa3,0x66,0x42,0xd7,0x00,0x20,0x44,0x3c,0xb4,0x40,0x40,0x1e,0x60,
-0xa8,0x62,0xa2,0xd1,0x10,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,
-0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xbc,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x20,0x40,0x03,0x26,0x06,0x00,0x1f,0x60,
-0x60,0x63,0x8a,0xf1,0x08,0x60,0x80,0x64,0x23,0x00,0x3d,0x60,0x4c,0x63,0xbd,0xd3,
-0xff,0xff,0x00,0xa0,0x60,0x45,0x15,0x03,0xc7,0x85,0x63,0x5c,0x04,0x64,0xc0,0x81,
-0x31,0xf2,0x50,0xfe,0x59,0xd1,0x32,0xf2,0xd0,0x80,0x59,0xd1,0x33,0xf2,0xd0,0x80,
-0x59,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x76,0x03,0x44,0xa3,0xd7,0x80,0xff,0xff,
-0xec,0x02,0x3d,0x60,0x4e,0x63,0x3d,0x60,0x4c,0x62,0xa2,0xd1,0x1a,0x60,0x90,0x64,
-0xd0,0x80,0xff,0xff,0x68,0x06,0xc3,0x83,0x87,0xfd,0xff,0xff,0x7f,0xf3,0x25,0xf0,
-0xbd,0xdb,0x64,0x44,0x00,0x7f,0xbd,0xdb,0x64,0x47,0x00,0x7f,0xbd,0xdb,0x31,0xf0,
-0xbd,0xd9,0xff,0xff,0x32,0xf0,0xbd,0xd9,0x33,0xf0,0xff,0xff,0xbd,0xd9,0xff,0x60,
-0xff,0x7c,0x38,0xf2,0x00,0xf4,0x05,0xa4,0xa0,0xd8,0x06,0xf0,0xff,0xff,0xbd,0xd9,
-0x07,0xf0,0xbd,0xd9,0x20,0x44,0x03,0xb4,0xff,0xff,0x0a,0x02,0x16,0x60,0x42,0x62,
-0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,0x64,0x40,0x01,0x2a,0x3b,0x00,
-0x57,0x60,0x58,0x4e,0x59,0x78,0xff,0xff,0xff,0x60,0xfe,0x64,0xd0,0x80,0xff,0xff,
-0x32,0x03,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x2d,0x05,0x00,0x36,0x12,0x00,
-0x66,0x60,0xc4,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0x60,0x43,0x0b,0x03,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x17,0x60,0x82,0x64,0xa0,0xd1,0x65,0x44,0xd0,0x80,
-0xff,0xff,0x60,0x02,0x87,0xf3,0xff,0xff,0x3e,0xa4,0x60,0x43,0xbd,0xd9,0x61,0x44,
-0xbd,0xdb,0xff,0x60,0xff,0x64,0xd0,0x80,0xff,0xff,0x04,0x02,0x66,0x60,0xb8,0x62,
-0x00,0x64,0xa2,0xdb,0x87,0xf3,0x10,0x65,0xc4,0x83,0x00,0x64,0x08,0xf0,0xa3,0xdb,
-0x64,0x47,0x60,0x45,0x00,0x3b,0x46,0x00,0xbd,0xdb,0xdc,0x84,0xe8,0x81,0x10,0x64,
-0x58,0xd0,0xcd,0x81,0xbd,0xd9,0xfc,0x02,0xd8,0x83,0x04,0x64,0x40,0x4d,0x07,0x61,
-0x65,0x40,0x01,0x2a,0xbd,0xd0,0xff,0xff,0x64,0x44,0x00,0x7f,0x2d,0xda,0x5a,0x8d,
-0x64,0x47,0x00,0x7f,0x2d,0xda,0xcd,0x81,0x5a,0x8d,0xf4,0x02,0x87,0xf1,0x32,0x63,
-0xc3,0x83,0x04,0x61,0x65,0x40,0x01,0x26,0x02,0xa1,0xa1,0xd2,0xff,0xff,0x01,0xa8,
-0x59,0xd2,0x20,0x02,0x59,0xd0,0xcc,0x84,0xbd,0xd9,0xfc,0x02,0x00,0x64,0xbd,0xdb,
-0x59,0xd2,0x59,0xd0,0x03,0xa8,0x7f,0xf3,0x15,0x02,0x59,0xd0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x10,0x02,0x87,0xf3,0x32,0x65,0xc4,0x83,0x00,0x61,0xa3,0xd3,0xff,0xff,
-0x60,0x40,0xff,0x22,0x14,0x00,0x80,0x2a,0x10,0x00,0x60,0x40,0x82,0x3a,0x03,0x00,
-0x01,0xb9,0x0b,0x00,0x24,0x00,0x84,0x3a,0x02,0x00,0x02,0xb9,0x06,0x00,0x8b,0x3a,
-0x02,0x00,0x04,0xb9,0x02,0x00,0x96,0x36,0x08,0xb9,0x02,0xa3,0xe7,0x00,0x87,0xf3,
-0x3c,0x65,0xc4,0x82,0x61,0x43,0xa2,0xdd,0x20,0x40,0x03,0x26,0x09,0x00,0x8a,0xf3,
-0xff,0xff,0x44,0xa4,0x8a,0xfb,0x89,0xf3,0xff,0xff,0x04,0xa4,0xa2,0xdb,0x07,0x00,
-0x3d,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x44,0xa4,0xa2,0xdb,0xff,0xff,0x26,0x46,
-0x2f,0x58,0xff,0xff,0x66,0x60,0xca,0x62,0x2e,0x44,0xa2,0xdb,0x66,0x60,0xce,0x65,
-0x66,0x60,0xc4,0x62,0x00,0x64,0xa2,0xdb,0xa5,0xdb,0x01,0xf2,0x10,0x63,0x00,0x7f,
-0xf4,0xa4,0x60,0x41,0x10,0x63,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,
-0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x5c,0x63,0x44,
-0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,
-0x01,0xa3,0x00,0x7f,0x60,0x45,0x64,0x44,0xad,0xa8,0x07,0xa8,0x0c,0x03,0x15,0x03,
-0xc9,0x81,0xd5,0x81,0x61,0x44,0x80,0x27,0x03,0x00,0xff,0xa0,0xc7,0x83,0xdb,0x07,
-0x58,0x60,0x61,0x78,0xff,0xff,0x66,0x60,0xcc,0x62,0x5a,0xdd,0x65,0x44,0x5a,0xdb,
-0x61,0x44,0x5a,0xdb,0x58,0x60,0x1e,0x78,0xff,0xff,0x66,0x60,0xc4,0x62,0xa2,0xdd,
-0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0xe3,0x04,0x17,0x60,0x82,0x61,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0xa1,0xdb,0x08,0xa1,0xa1,0xdb,0x02,0xa1,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xa1,0xdb,0x1f,0x60,0x54,0x61,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0xa1,0xdb,0x1f,0x60,0x56,0x61,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xf2,0xa0,
-0xff,0xff,0x01,0x06,0x0e,0x64,0xa1,0xdb,0x17,0x60,0x80,0x61,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0xa1,0xdb,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,0x1f,0x60,0x56,0x61,
-0xa1,0xd1,0x1f,0x60,0x1c,0x63,0x1f,0x60,0x54,0x62,0xa2,0xd3,0x01,0x61,0x00,0x65,
-0xff,0xa0,0xff,0xff,0x04,0x03,0xe1,0x81,0xcc,0x84,0x02,0xa3,0xf9,0x00,0x64,0x44,
-0x05,0x7c,0xb5,0x85,0xbd,0xd9,0xcc,0x84,0x00,0xa0,0xe1,0x81,0xfa,0x02,0x65,0x44,
-0xa5,0xfb,0x16,0x60,0x42,0x62,0xa2,0xd3,0x01,0x7c,0x04,0xa8,0xd5,0xf9,0x05,0x02,
-0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,0x0b,0x65,0x3d,0x60,0x58,0x4e,
-0x32,0x78,0xff,0xff,0xff,0x60,0xfe,0x7c,0x58,0x60,0x65,0x78,0xff,0xff,0x45,0x4d,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0xa0,0x60,0x00,0x64,0xd4,0x80,0xff,0xff,
-0x39,0x02,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xf8,0x65,0xd4,0x80,0xff,0xff,0x2a,0x02,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x5c,0x58,0x60,0x58,0x4e,0xb5,0x78,
-0xff,0xff,0x67,0x41,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x2d,0x44,0xf1,0xa0,
-0xff,0xff,0x1c,0x04,0x45,0x4d,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x41,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x66,0x60,0xc6,0x62,0x65,0x44,0xa2,0xdb,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x66,0x60,0xc8,0x62,0x65,0x44,0xa2,0xdb,
-0x2d,0x45,0x04,0x00,0xff,0x60,0xff,0x7c,0x64,0x41,0x00,0x65,0x66,0x60,0xca,0x62,
-0xa2,0xd3,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x66,0x60,0xca,0x62,0x2e,0x44,
-0xa2,0xdb,0x66,0x60,0xcc,0x62,0x5a,0xd3,0x5a,0xd1,0x00,0xa0,0x60,0x43,0x36,0x03,
-0x5a,0xd3,0x64,0x45,0x60,0x41,0x1c,0x00,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x5c,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x45,0x64,0x44,0xae,0xa8,0xff,0xff,0x07,0x03,
-0xc9,0x81,0xd5,0x81,0x61,0x44,0xff,0xa0,0xc7,0x83,0xde,0x07,0x0f,0x00,0xdf,0x83,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0xf8,0x60,0xa0,0x64,0xd4,0x80,0xff,0xff,
-0x05,0x02,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x01,0x00,0x00,0x65,0x66,0x60,
-0xca,0x62,0xa2,0xd3,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0x60,0x45,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,
-0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x47,0xb4,0x85,0x2e,0x58,
-0xff,0xff,0x2f,0x58,0xff,0xff,0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,
-0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbb,0xf1,0x28,0x60,
-0xae,0x62,0xa2,0xd9,0x3c,0x60,0xc2,0x62,0x28,0x60,0xaa,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xdc,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,
-0x58,0x60,0xf5,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xda,0x62,
-0xa2,0xd1,0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0x0c,0x03,0xa0,0x84,0xa2,0xdb,
-0x0f,0x47,0x67,0x60,0x42,0x62,0x6f,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x29,0x00,
-0x07,0x4f,0xd4,0x00,0x1e,0x60,0xda,0x62,0xa2,0xd1,0xff,0x60,0xfb,0x61,0xa1,0x84,
-0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0xc5,0x02,0x17,0x60,0x7a,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x02,0x2a,0xbe,0x00,
-0x0f,0x47,0x67,0x60,0x42,0x62,0x66,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x09,0x00,
-0x67,0x60,0x42,0x62,0x69,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x02,0x00,0x07,0x4f,
-0xad,0x00,0x48,0xf1,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x03,0x02,0x5a,0x60,0xd9,0x78,0xff,0xff,
-0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0x60,0x47,0xff,0xff,0x66,0x3a,0x59,0x00,
-0x50,0xf1,0x5b,0x60,0x38,0x64,0xa0,0xd9,0x7f,0xf1,0x5b,0x60,0x3a,0x64,0xa0,0xd9,
-0x84,0xf1,0x5b,0x60,0x42,0x64,0xa0,0xd9,0xcb,0xf1,0x5b,0x60,0x44,0x64,0xa0,0xd9,
-0xbd,0xf1,0x5b,0x60,0x46,0x64,0xa0,0xd9,0xc7,0xf1,0x5b,0x60,0x4c,0x64,0xa0,0xd9,
-0xc8,0xf1,0x5b,0x60,0x4e,0x64,0xa0,0xd9,0x5b,0x60,0x3c,0x63,0x81,0xf1,0xbd,0xd9,
-0x81,0xf1,0xff,0xff,0xbd,0xd9,0x81,0xf1,0xa3,0xd9,0x59,0x60,0xa6,0x65,0x5b,0x60,
-0x50,0x64,0x65,0x41,0xd4,0x85,0xfe,0xa1,0x65,0x43,0x0a,0xa3,0x38,0xfc,0x46,0x48,
-0x00,0xf4,0x04,0x63,0x0a,0xa3,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,
-0xa0,0xd3,0xff,0xff,0x03,0xfa,0x65,0x47,0x05,0xfa,0x60,0x47,0x8e,0xa0,0xff,0xff,
-0x09,0x04,0x8e,0xa5,0x72,0x64,0x07,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,
-0x7c,0x64,0x01,0x00,0x00,0x65,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,0xfb,0x07,
-0x65,0x44,0x00,0xa0,0x00,0xf4,0x02,0x03,0x04,0x63,0xee,0x00,0x5a,0x60,0x8b,0x78,
-0xff,0xff,0x6e,0x3a,0x00,0x00,0x67,0x3a,0x00,0x00,0x68,0x3a,0x00,0x00,0x69,0x36,
-0x03,0x00,0x5a,0x60,0x4a,0x78,0xff,0xff,0x61,0x60,0xc2,0x63,0x61,0x60,0xc6,0x62,
-0xa2,0xd3,0xa3,0xd1,0x00,0xa0,0xff,0xff,0x56,0x03,0x02,0x60,0x80,0x63,0x64,0x41,
-0x61,0x60,0xd8,0x65,0xc5,0x81,0xfe,0xa1,0xd3,0x85,0x0a,0xa3,0x38,0xfc,0x46,0x48,
-0x00,0xf4,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,
-0x03,0xfa,0x63,0x47,0x05,0xfa,0x04,0x63,0x0a,0xa3,0x65,0x44,0x8e,0xa0,0xff,0xff,
-0x09,0x04,0x8e,0xa5,0x72,0x64,0x08,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,
-0x7c,0x64,0x02,0x00,0x00,0x65,0x40,0x49,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,
-0xfb,0x07,0x65,0x44,0x00,0xa0,0xff,0xff,0x03,0x03,0x00,0xf4,0x04,0x63,0xec,0x00,
-0x61,0x60,0xc2,0x62,0x61,0x60,0xd8,0x61,0xfe,0xa1,0xa2,0xd3,0x29,0x45,0x00,0xa0,
-0x7c,0x62,0x4e,0x03,0xd6,0x85,0xd4,0x80,0x60,0x42,0x09,0x06,0x65,0x44,0xd6,0x85,
-0x07,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,0x7c,0x64,0x01,0x00,0x00,0x65,
-0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,0xfb,0x07,0x65,0x44,0x00,0xa0,0x00,0xf4,
-0x37,0x03,0x04,0x63,0xee,0x00,0x00,0x64,0xd0,0x80,0xff,0xff,0x03,0x02,0x5a,0x60,
-0xc5,0x78,0xff,0xff,0x64,0x45,0x64,0x44,0x0a,0xa4,0x38,0xfa,0x61,0x60,0xd8,0x61,
-0xfe,0xa1,0x46,0x48,0x00,0xf4,0x04,0x63,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,
-0x42,0x64,0xa0,0xd3,0xff,0xff,0x03,0xfa,0x65,0x47,0x05,0xfa,0x0a,0xa3,0x65,0x44,
-0x8e,0xa0,0xff,0xff,0x09,0x04,0x8e,0xa5,0x72,0x64,0x07,0x00,0x84,0xa0,0xff,0xff,
-0x03,0x04,0x84,0xa5,0x7c,0x64,0x01,0x00,0x00,0x65,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,
-0xff,0xff,0xfb,0x07,0x65,0x44,0x00,0xa0,0x00,0xf4,0x02,0x03,0x04,0x63,0xee,0x00,
-0x5a,0x60,0x8b,0x78,0xff,0xff,0x6a,0x3a,0x00,0x00,0x64,0x3a,0x0f,0x00,0x0a,0x63,
-0x38,0xfc,0x46,0x48,0x00,0xf4,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,
-0xa0,0xd3,0xff,0xff,0x03,0xfa,0x63,0x47,0x05,0xfa,0x2e,0x00,0x6f,0x3a,0x66,0x00,
-0x12,0x63,0x38,0xfc,0xa0,0x60,0x01,0x64,0x31,0xfa,0xf0,0x60,0xf8,0x64,0x32,0xfa,
-0x04,0x60,0xf0,0x64,0x33,0xfa,0xff,0xff,0x81,0xf1,0x2b,0xf8,0x82,0xf1,0xff,0xff,
-0x2c,0xf8,0x83,0xf1,0x2d,0xf8,0x46,0x48,0x00,0xf4,0xaa,0x60,0xaa,0x64,0x02,0xfa,
-0x00,0x60,0x03,0x64,0x03,0xfa,0x00,0x60,0x00,0x64,0x04,0xfa,0x81,0x60,0x87,0x64,
-0x05,0xfa,0x00,0x64,0x0a,0xfa,0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0x06,0xfa,
-0x63,0x47,0x08,0xfa,0x28,0x46,0x0d,0x00,0x28,0x46,0x81,0xf1,0x2b,0xf8,0x31,0xf8,
-0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,0x2d,0xf8,0x33,0xf8,
-0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,
-0xff,0xff,0x85,0xf3,0xff,0xff,0x08,0xbc,0x43,0xf1,0xff,0xff,0x64,0x40,0x01,0x2a,
-0x03,0x00,0x60,0x47,0x40,0xbc,0x60,0x47,0x29,0xfa,0x00,0x63,0x28,0xfc,0x22,0xfc,
-0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0xff,0x64,0x23,0xfa,0xff,0x7f,0x00,0x7e,
-0x0e,0xfa,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x14,0x00,0x0f,0x4e,0x46,0x45,
-0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,
-0xd1,0xfe,0x0e,0x4f,0x2f,0x58,0xff,0xff,0x61,0x60,0xc2,0x62,0xa2,0xd1,0x61,0x60,
-0xd8,0x64,0xc0,0x83,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xbd,0xdb,0xf6,0xa0,0x00,0xa0,
-0x01,0x03,0x34,0x02,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x2d,0x03,0x1e,0x60,0x92,0x62,0x61,0x60,0xd2,0x61,0xa2,0xd3,0xa1,0xd1,0xff,0xff,
-0xd0,0x85,0x1e,0x60,0x94,0x62,0x61,0x60,0xd4,0x61,0xa2,0xd3,0xa1,0xd1,0x01,0x05,
-0xff,0xa4,0xd0,0x84,0x65,0x44,0x02,0x02,0xfc,0x23,0x14,0x00,0x61,0x60,0xd2,0x62,
-0xa2,0xd3,0xbd,0xdb,0x61,0x60,0xd4,0x62,0xa2,0xd3,0xbd,0xdb,0x00,0x64,0xbd,0xdb,
-0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,
-0xbd,0xdb,0x2f,0x00,0x61,0x60,0xd6,0x62,0x02,0x64,0xa2,0xdb,0x1e,0x60,0x92,0x62,
-0xa2,0xd3,0xbd,0xdb,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xbd,0xdb,0x27,0x60,0xe0,0x65,
-0x88,0xf3,0xff,0xff,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xbd,0xdb,0x64,0x60,0x58,0x62,
-0xa2,0xd1,0x00,0x64,0xa2,0xdb,0xbd,0xd9,0x27,0x41,0x06,0xa1,0xa1,0xd1,0xbd,0xd9,
-0x59,0xd1,0xff,0xff,0xbd,0xd9,0x59,0xd1,0xbd,0xd9,0x27,0x41,0x04,0xa1,0xa1,0xd3,
-0xbd,0xdb,0x27,0x41,0x02,0xa1,0xa1,0xd1,0xff,0xff,0xd0,0x84,0xbd,0xdb,0x51,0xf3,
-0xbd,0xdb,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x05,0x02,
-0x61,0x60,0xc4,0x62,0xa2,0xd3,0xbd,0xdb,0x09,0x00,0x61,0x60,0xc4,0x62,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0xff,0xa0,0xbd,0xdb,0x09,0x02,0x00,0x64,0xbd,0xdb,
-0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x20,0x00,0x61,0x60,
-0xca,0x62,0xa2,0xd1,0xbd,0xd9,0x61,0x60,0xcc,0x62,0xa2,0xd1,0xbd,0xd9,0x61,0x60,
-0xce,0x62,0xa2,0xd1,0xbd,0xd9,0xff,0xff,0x59,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,
-0xbd,0xdb,0x58,0xf3,0xbd,0xdb,0xf4,0xb6,0x7e,0x00,0x00,0x10,0x61,0x60,0xd0,0x62,
-0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9c,0x62,0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9e,0x62,
-0xa2,0xd3,0xbd,0xdb,0x61,0x60,0xc2,0x63,0x02,0x60,0x80,0x65,0xa3,0xd3,0xff,0xff,
-0x28,0xa4,0xd4,0x80,0xff,0xff,0x05,0x04,0x61,0x60,0xc6,0x62,0x01,0x64,0xa2,0xdb,
-0x00,0x64,0xa3,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xf6,0xa0,0x00,0xa0,
-0x01,0x03,0x0f,0x02,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xfe,0xa0,
-0x06,0x03,0x05,0x03,0x01,0x64,0xa2,0xdb,0x5a,0x60,0xdb,0x78,0xff,0xff,0x00,0x64,
-0xa2,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd1,0xff,0xff,0x64,0x44,0xf4,0xa0,0xff,0xff,
-0x07,0x03,0x59,0x60,0xc0,0x64,0xc0,0x83,0xa3,0xd3,0xff,0xff,0xdc,0x84,0xa3,0xdb,
-0x59,0x60,0xa6,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x64,0x60,0x5c,0x62,0xa2,0xd1,0x64,0x60,0x62,0x64,0xc0,0x83,0x64,0x60,0x5e,0x62,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0xbd,0xdb,0x1e,0x60,0x92,0x62,0xa2,0xd3,
-0xbd,0xdb,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xbd,0xdb,0x66,0x60,0x24,0x62,0xa2,0xd3,
-0xbd,0xdb,0x66,0x60,0x26,0x62,0xa2,0xd3,0xbd,0xdb,0x66,0x60,0x28,0x62,0xa2,0xd3,
-0xbd,0xdb,0x66,0x60,0x22,0x62,0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9c,0x62,0xa2,0xd3,
-0xbd,0xdb,0xff,0xff,0x58,0xf3,0xbd,0xdb,0x59,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,
-0xbd,0xdb,0x5a,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,0xbd,0xdb,0xff,0xff,0x81,0xf3,
-0xbd,0xdb,0x82,0xf3,0xff,0xff,0xbd,0xdb,0x83,0xf3,0xa3,0xdb,0x64,0x60,0x5c,0x63,
-0x01,0x60,0xc0,0x65,0xa3,0xd3,0xff,0xff,0x1c,0xa4,0xd4,0x80,0xff,0xff,0x05,0x04,
-0x64,0x60,0x60,0x62,0x01,0x64,0xa2,0xdb,0x00,0x64,0xa3,0xdb,0x2e,0x58,0xff,0xff,
-0x1e,0x60,0xce,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0xba,0xf1,0x28,0x60,0x96,0x62,0xa2,0xd9,0x3c,0x60,
-0xc6,0x62,0x28,0x60,0x92,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xd0,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x5c,0x60,0x37,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xce,0x62,0x00,0x64,0xa2,0xdb,0xcb,0xf3,
-0xff,0xff,0xfc,0xa0,0xff,0xff,0xdf,0x02,0x66,0x60,0x28,0x62,0x0f,0x64,0xa2,0xdb,
-0x17,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0x60,0x41,0x02,0xa4,0xff,0xff,0x9c,0xa0,
-0xff,0xff,0x01,0x04,0x00,0x64,0x60,0x45,0x18,0x60,0x56,0x62,0xc6,0x82,0xa2,0xd1,
-0x00,0x63,0xa2,0xdd,0x18,0x60,0x54,0x63,0xa3,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,
-0x17,0x60,0xee,0x62,0xc6,0x82,0xa2,0xd1,0x00,0x63,0xa2,0xdd,0x17,0x60,0xec,0x63,
-0xa3,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,0x17,0x60,0xea,0x63,0x65,0x44,0xa3,0xdb,
-0x61,0x45,0x18,0x60,0xba,0x63,0xa3,0xd1,0x18,0x60,0x56,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0x60,0x41,0xc0,0x84,0xa3,0xdb,0x18,0x60,0x54,0x63,0xa3,0xd1,0xff,0xff,
-0xc1,0x84,0xa3,0xdb,0x18,0x60,0x52,0x63,0xa3,0xd1,0x17,0x60,0xee,0x64,0xc4,0x84,
-0xa0,0xd3,0xff,0xff,0x60,0x41,0xc0,0x84,0xa3,0xdb,0x17,0x60,0xec,0x63,0xa3,0xd1,
-0xff,0xff,0xc1,0x84,0xa3,0xdb,0xe2,0xa0,0x00,0x64,0x03,0x05,0x05,0x7c,0xb8,0xf9,
-0x3f,0x00,0x00,0x63,0x18,0x60,0x52,0x64,0xa0,0xdd,0x18,0x60,0xba,0x64,0xa0,0xdd,
-0x18,0x60,0xc6,0x64,0x64,0x63,0xa0,0xdd,0x18,0x60,0x54,0x63,0xa3,0xd3,0xff,0xff,
-0x00,0xbc,0x60,0x41,0x2d,0x03,0x02,0x60,0x8f,0x65,0x17,0x60,0xec,0x62,0xa2,0xd3,
-0xd5,0x80,0xff,0xff,0x03,0x06,0xe9,0x81,0xe8,0x84,0xfa,0x00,0x60,0x5c,0x61,0x44,
-0xe0,0x84,0xe0,0x84,0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x45,0xe0,0x84,
-0xc4,0x85,0xc5,0x85,0x00,0x62,0x65,0x44,0x64,0x45,0x11,0x61,0xe0,0x84,0xcd,0x81,
-0xfd,0x04,0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,
-0xd6,0x82,0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,
-0x60,0x43,0x59,0x60,0x9e,0x62,0xa2,0xdd,0x66,0x60,0x22,0x62,0xa2,0xdd,0x63,0x44,
-0xd3,0xa0,0x01,0x65,0x0e,0x04,0x1f,0xf3,0x59,0xf3,0x60,0x40,0x10,0x2a,0x67,0x00,
-0xec,0xa0,0x67,0x60,0x6c,0x64,0x63,0x04,0xa0,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x5e,0x03,0x18,0x60,0xbc,0x65,0x51,0xf3,0xff,0xff,0xe0,0x84,0xc4,0x83,0xa3,0xd3,
-0xff,0xff,0xf8,0xa0,0x03,0x65,0x53,0x05,0x18,0x60,0x52,0x64,0xa0,0xd3,0xff,0xff,
-0xe2,0xa0,0x18,0x60,0xba,0x63,0x33,0x04,0xa3,0xd3,0xff,0xff,0x00,0xbc,0x60,0x41,
-0x2f,0x03,0x02,0x60,0x8f,0x65,0x18,0x60,0x52,0x62,0xa2,0xd3,0xd5,0x80,0xff,0xff,
-0x03,0x06,0xe9,0x81,0xe8,0x84,0xfa,0x00,0x60,0x5c,0x61,0x44,0xe0,0x84,0xe0,0x84,
-0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x45,0xe0,0x84,0xc4,0x85,0xc5,0x85,
-0x00,0x62,0x65,0x44,0x64,0x45,0x11,0x61,0xe0,0x84,0xcd,0x81,0xfd,0x04,0x01,0x00,
-0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,0xd6,0x82,0xe2,0x80,
-0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,0x01,0x00,0x69,0x00,
-0x60,0x43,0x18,0x60,0xc6,0x62,0xa2,0xdd,0x63,0x44,0xd3,0xa0,0x00,0x63,0x18,0x60,
-0x52,0x62,0xa2,0xdd,0x18,0x60,0xba,0x62,0xa2,0xdd,0x5b,0x04,0x18,0x60,0xc6,0x63,
-0xa3,0xd1,0x66,0x60,0x22,0x62,0xa2,0xd9,0x64,0x64,0xa3,0xdb,0x02,0x65,0x05,0x64,
-0xb8,0xfb,0x66,0x60,0x24,0x63,0x65,0x44,0xbd,0xdb,0x51,0xf3,0xa3,0xdb,0x51,0xf3,
-0x50,0xf3,0x60,0x41,0xff,0xa0,0xe8,0x85,0x41,0x03,0xff,0xa1,0x59,0x60,0x6a,0x64,
-0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x50,0xfb,0x61,0x43,0x66,0x60,
-0x28,0x62,0xa2,0xdd,0x51,0xfd,0x61,0x45,0x00,0x63,0x17,0x60,0xe6,0x62,0xa2,0xdd,
-0x17,0x60,0xe4,0x62,0xa2,0xdd,0x17,0x60,0xec,0x62,0xa2,0xdd,0x18,0x60,0x54,0x62,
-0xa2,0xdd,0x17,0x60,0xe8,0x62,0xa2,0xdd,0x67,0x60,0x6c,0x62,0xa2,0xdd,0x64,0x61,
-0x17,0x60,0xee,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x64,0x61,0x18,0x60,
-0x56,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x65,0x41,0xe1,0x85,0x18,0x60,
-0xbc,0x63,0xc7,0x83,0xa3,0xd3,0xff,0xff,0xf8,0xa0,0xff,0xff,0xc0,0x05,0x04,0x61,
-0x18,0x60,0xbc,0x63,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x5e,0x60,0x32,0x78,
-0xff,0xff,0x17,0x60,0xec,0x63,0xa3,0xd3,0xff,0xff,0xe2,0xa0,0x59,0x60,0x9e,0x63,
-0xa3,0xd3,0x1d,0x04,0xdd,0xa0,0xff,0xff,0x23,0x05,0x50,0xf3,0x04,0x65,0xf8,0xa0,
-0xff,0xff,0x32,0x02,0xb8,0xf3,0xff,0xff,0xfd,0xa0,0x03,0x64,0x2d,0x03,0xb8,0xfb,
-0x67,0x60,0x6e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x25,0x00,0x17,0x60,
-0xe8,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x1e,0x02,0x6f,0x00,0x18,0x60,
-0xc6,0x63,0xa3,0xd3,0xff,0xff,0xdd,0xa0,0x64,0x64,0xa3,0xdb,0x05,0x65,0xef,0x04,
-0x05,0x64,0xb8,0xfb,0x51,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x5f,0x03,0x03,0x60,
-0xe8,0x65,0x17,0x60,0xe4,0x64,0xa0,0xd3,0x59,0xf3,0xd4,0x80,0x06,0x65,0x56,0x04,
-0xf1,0xa0,0xff,0xff,0xdc,0x05,0x52,0x00,0x66,0x60,0x24,0x63,0x65,0x44,0xbd,0xdb,
-0xfa,0xa0,0x51,0xf3,0x15,0x02,0xbd,0xdb,0x60,0x5c,0x08,0x65,0x03,0x61,0x67,0x60,
-0x6c,0x62,0x01,0x64,0xa2,0xdb,0x04,0x00,0xd0,0x80,0xe8,0x85,0x3f,0x03,0xff,0xa1,
-0x59,0x60,0x6a,0x64,0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x0e,0x00,
-0xbd,0xdb,0x50,0xf3,0x60,0x41,0xf8,0xa0,0xe0,0x85,0x30,0x03,0x01,0xa1,0x59,0x60,
-0x6a,0x64,0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x50,0xfb,0x61,0x43,
-0x51,0xfd,0x66,0x60,0x28,0x62,0xa2,0xdd,0x00,0x63,0x17,0x60,0xe6,0x62,0xa2,0xdd,
-0x17,0x60,0xe4,0x62,0xa2,0xdd,0x17,0x60,0xec,0x62,0xa2,0xdd,0x18,0x60,0x54,0x62,
-0xa2,0xdd,0x64,0x61,0x17,0x60,0xee,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,
-0x64,0x61,0x18,0x60,0x56,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x04,0x61,
-0x18,0x60,0xbc,0x63,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x60,0x64,0x65,
-0x17,0x60,0xe6,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xd4,0x80,0xa2,0xdb,0x09,0x04,
-0x17,0x60,0xe4,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x17,0x60,0xe8,0x62,0x01,0x64,
-0xa2,0xdb,0x66,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x04,0x07,
-0x5b,0x60,0x58,0x4e,0xc4,0x78,0xff,0xff,0x5c,0x60,0x1f,0x78,0xff,0xff,0x61,0xf1,
-0x28,0x60,0xa2,0x62,0xa2,0xd9,0x3c,0x60,0xca,0x62,0x28,0x60,0x9e,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xd6,0x62,0x00,0x60,0xfe,0x64,
-0xa2,0xdb,0x5e,0x60,0x6b,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x60,0xf3,
-0x65,0xf3,0x60,0x45,0x60,0x47,0xb4,0x84,0x60,0x45,0x68,0x60,0xcc,0x62,0x10,0x60,
-0x00,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x00,0x60,0x7a,0x66,
-0x00,0x64,0x61,0xfb,0x01,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x1e,0x03,0x1e,0x60,
-0x92,0x64,0xa0,0xd3,0x03,0xf0,0xff,0xff,0xd0,0x84,0x00,0xfa,0x01,0xf2,0x60,0x45,
-0xd4,0x80,0xff,0xff,0x05,0x06,0xd4,0x84,0x01,0xfa,0xfe,0xa0,0xff,0xff,0x0d,0x05,
-0x00,0x64,0x01,0xfa,0x02,0xfa,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x80,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x62,0xf1,0x17,0x60,0x06,0x64,
-0xa0,0xd3,0xff,0xff,0xd0,0x80,0x60,0x45,0x69,0x03,0x16,0x60,0x42,0x64,0xa0,0xd3,
-0xff,0xff,0xff,0xa0,0xfc,0xa0,0x06,0x03,0x62,0x02,0x86,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x5d,0x03,0x65,0x44,0x60,0x45,0x68,0x60,0xcc,0x62,0x60,0x60,0x00,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x19,0xf2,0xff,0xff,0xdc,0x84,
-0x19,0xfa,0x65,0x44,0xff,0x22,0x4b,0x00,0x12,0xfa,0xcc,0x84,0xfc,0xa0,0x60,0x41,
-0x01,0x06,0x04,0x64,0xe0,0x84,0x60,0x45,0xe0,0x84,0xe0,0x84,0xc4,0x85,0x18,0x60,
-0xf8,0x64,0xc4,0x83,0xbd,0xd3,0xff,0xff,0x14,0xfa,0xbd,0xd3,0xff,0xff,0x15,0xfa,
-0xbd,0xd3,0xff,0xff,0x16,0xfa,0xbd,0xd3,0xff,0xff,0x17,0xfa,0xbd,0xd3,0xff,0xff,
-0x18,0xfa,0x16,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0xfc,0xa0,0x61,0x44,0x05,0x02,
-0xfd,0xa0,0x14,0xf2,0x02,0x05,0xfd,0xa4,0x14,0xfa,0xcb,0xf3,0xff,0xff,0xfc,0xa0,
-0x62,0xf3,0x08,0x02,0xff,0xff,0xff,0x26,0x05,0x00,0x01,0x64,0x63,0x60,0x58,0x4e,
-0x03,0x78,0xff,0xff,0x62,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x2f,0x00,0x15,0xf2,
-0x01,0xfa,0x61,0xfb,0x02,0x64,0x02,0xfa,0x08,0x64,0x60,0xfb,0x01,0x64,0x13,0xfa,
-0x05,0xfa,0x04,0xfa,0x10,0x60,0x00,0x64,0x66,0xfb,0x20,0x00,0x24,0x00,0x65,0xf3,
-0xff,0xff,0x02,0xb0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,0x05,0x02,0x00,0x64,0x63,0x60,0x58,0x4e,
-0x03,0x78,0xff,0xff,0x80,0x64,0x60,0xfb,0x32,0x64,0x61,0xfb,0x17,0x60,0x06,0x63,
-0x00,0x64,0x66,0xfb,0x01,0xfa,0x02,0xfa,0xff,0xff,0xa3,0xdb,0x17,0x60,0x06,0x64,
-0xa0,0xd3,0x62,0xfb,0x05,0x00,0x00,0xa0,0xff,0xff,0x02,0x02,0x32,0x64,0x61,0xfb,
-0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x01,0x64,0xa0,0x84,0xa2,0xdb,
-0x09,0xf8,0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x62,0xf3,0x03,0x03,
-0x60,0x60,0xa2,0x78,0xff,0xff,0x00,0xa0,0xff,0xff,0x6c,0x03,0x64,0x44,0x80,0x2a,
-0x1d,0x00,0x20,0xf2,0xff,0xff,0xdc,0x84,0x20,0xfa,0x01,0x60,0x5e,0x64,0x20,0x40,
-0x40,0x2a,0x08,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x01,0xfa,0x01,0x64,0x02,0xfa,0x65,0xf3,0xff,0xff,0xff,0xff,
-0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,0x6e,0xf3,0x6f,0xf1,
-0xff,0xff,0x01,0xb4,0xb0,0x84,0xff,0xff,0x19,0x03,0x60,0xf3,0xff,0xff,0x08,0xbc,
-0x60,0xfb,0x62,0x60,0x58,0x4e,0xdc,0x78,0xff,0xff,0x00,0xfa,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x21,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x01,0xf0,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x09,0xf2,0x06,0xf2,
-0x60,0x40,0x08,0x2a,0x28,0x00,0x60,0x40,0xff,0x22,0x02,0x00,0xcc,0x84,0x06,0xfa,
-0x00,0xa0,0xff,0xff,0x13,0x02,0x05,0xf2,0x14,0xf0,0x03,0xa4,0xd0,0x80,0xff,0xff,
-0x01,0x06,0x64,0x44,0x05,0xfa,0x07,0xf2,0xff,0xff,0x60,0x40,0xff,0x22,0x02,0x00,
-0xcc,0x84,0x07,0xfa,0x00,0xa0,0x01,0x64,0x01,0x02,0x05,0xfa,0x86,0xf3,0x01,0xf0,
-0x00,0xfa,0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x60,0xf3,0xff,0xff,0x08,0xbc,
-0x60,0xfb,0x01,0x00,0x56,0x00,0x09,0xf2,0x60,0xf1,0x60,0x40,0x04,0x2a,0x2c,0x00,
-0x64,0x40,0x20,0x2a,0x19,0x00,0x01,0x64,0x05,0xfa,0x04,0xfa,0x62,0x60,0x58,0x4e,
-0xdc,0x78,0xff,0xff,0x00,0xfa,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x22,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0xf0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x0d,0x06,0x01,0xfa,0x0b,0x00,0x64,0x40,0x10,0x2a,0x08,0x00,0x14,0x64,
-0x00,0xfa,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x60,0xf3,
-0xff,0xff,0xcf,0xb4,0x08,0xbc,0x60,0xfb,0x09,0xf2,0x60,0xf1,0x60,0x40,0x10,0x2a,
-0x0b,0x00,0x64,0x44,0xbf,0xb4,0x08,0xbc,0x60,0xfb,0x14,0x64,0x61,0xf1,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,0x18,0xf0,0x60,0x40,0x20,0x2a,
-0x04,0x00,0x01,0x63,0x05,0xfc,0x04,0xfc,0x06,0xf8,0x60,0x40,0x40,0x2a,0x01,0x00,
-0x06,0xf8,0x61,0xf3,0x65,0xf1,0x00,0xa0,0x05,0x64,0x03,0x02,0x64,0x40,0x80,0x26,
-0x61,0xfb,0x01,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,
-0x09,0xf0,0x61,0xf3,0x64,0x40,0x02,0x26,0x03,0x00,0x00,0xa0,0xff,0xff,0x7b,0x02,
-0x62,0xf3,0xff,0xff,0x00,0xa0,0x60,0xf3,0x76,0x03,0x00,0xa0,0xcb,0xf3,0x5d,0x02,
-0xfd,0xa0,0x04,0xf2,0x48,0x02,0x60,0x40,0xff,0x22,0x02,0x00,0xcc,0x84,0x04,0xfa,
-0x60,0x40,0x00,0x36,0x0b,0x00,0x71,0xf3,0xff,0xff,0xfb,0xa0,0x3c,0x60,0x28,0x62,
-0x05,0x05,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x12,0x03,0x05,0xf2,0x65,0xf1,
-0x04,0xfa,0x64,0x40,0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0x60,0xf3,0x86,0xf1,0x08,0xbc,0x60,0xfb,0x05,0x64,0xc0,0x84,0x00,0xfa,0x44,0x00,
-0xa4,0xf3,0xff,0xff,0x60,0x41,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x85,0x73,0x44,0xc4,0x84,0x61,0x45,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0x60,0x45,0x01,0x60,0xf4,0x64,0xd4,0x80,0x65,0x44,0x05,0x05,0x23,0xf2,
-0xff,0xff,0xdc,0x84,0x23,0xfa,0x32,0x64,0xfa,0xa0,0xff,0xff,0x01,0x05,0x06,0x64,
-0xfb,0xa4,0x00,0xfa,0x21,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,0x65,0xf3,0x1c,0x02,
-0xfd,0xa0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0x32,0x64,
-0x00,0xfa,0x01,0x64,0x05,0xfa,0x04,0xfa,0x0f,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,
-0x00,0x64,0x0a,0x02,0x60,0xfb,0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0x62,0x60,
-0x58,0x4e,0xe8,0x78,0xff,0xff,0x00,0xfa,0x00,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x61,0xfb,0x62,0x60,0xc8,0x78,0xff,0xff,0x62,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x03,0x02,0x61,0x60,0xab,0x78,0xff,0xff,0x64,0x44,0x80,0x2a,
-0x1d,0x00,0x20,0xf2,0xff,0xff,0xdc,0x84,0x20,0xfa,0x01,0x60,0x5e,0x64,0x20,0x40,
-0x40,0x2a,0x08,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x01,0xfa,0x01,0x64,0x02,0xfa,0x65,0xf3,0xff,0xff,0xff,0xff,
-0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,0x09,0xf2,0xff,0xff,
-0xff,0xff,0x04,0x2a,0x62,0x00,0x1c,0xf2,0xff,0xff,0xdc,0x84,0x1c,0xfa,0x00,0x64,
-0x00,0xfa,0x01,0x64,0x08,0xfa,0x06,0xf2,0xff,0xff,0xff,0xff,0xff,0x22,0x03,0x00,
-0xcc,0x84,0x06,0xfa,0x0b,0x02,0x00,0xa0,0xff,0xff,0x07,0x02,0x05,0xf2,0x14,0xf0,
-0x03,0xa4,0xd0,0x80,0xff,0xff,0x01,0x06,0x64,0x44,0x05,0xfa,0x60,0xf3,0xff,0xff,
-0x60,0x40,0x20,0x2a,0x22,0x00,0x01,0x60,0x2c,0x64,0x00,0xfa,0x5b,0x60,0x28,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x00,0x64,0x63,0x60,
-0x58,0x4e,0x03,0x78,0xff,0xff,0x00,0xf2,0x01,0xf0,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x18,0x06,0x01,0xfa,0x04,0x64,0x02,0xfa,0x14,0x00,0x60,0x40,0x10,0x2a,0x11,0x00,
-0x5b,0x60,0x2c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x32,0x64,0x00,0xfa,0xff,0xff,0x63,0xf3,
-0x64,0xfb,0x60,0xf3,0x61,0xf1,0xcf,0xb4,0x60,0xfb,0x00,0xf2,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x61,0xfb,0x00,0x64,0x0a,0xfa,0x09,0xf2,0xff,0xff,0xff,0xff,
-0x10,0x2a,0x12,0x00,0x1d,0xf2,0xff,0xff,0xdc,0x84,0x1d,0xfa,0x60,0xf3,0x32,0x65,
-0x60,0x40,0x40,0x2a,0x05,0x65,0xbf,0xb4,0x60,0xfb,0x65,0x44,0x61,0xf1,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,0xff,0xff,0xff,0xff,0x20,0x2a,
-0x38,0x00,0x1a,0xf2,0xff,0xff,0xdc,0x84,0x1a,0xfa,0x5d,0xf3,0xa7,0xf1,0x60,0x40,
-0xff,0x22,0x14,0x7c,0x64,0x44,0x60,0xf1,0x00,0xfa,0x64,0x40,0x01,0x2a,0x09,0x00,
-0x17,0xf2,0x13,0xf0,0x00,0xfa,0xff,0xff,0x05,0xf8,0x04,0xf8,0x18,0xf0,0xff,0xff,
-0x06,0xf8,0x60,0xf1,0xff,0xff,0x64,0x40,0x04,0x2a,0x09,0x00,0x17,0xf2,0x64,0x40,
-0x01,0x2a,0x05,0x64,0x00,0xfa,0x60,0xf3,0xff,0xff,0xfe,0xb4,0x60,0xfb,0x60,0xf3,
-0xff,0xff,0x08,0xbc,0xf9,0xb4,0x60,0xfb,0x00,0xf2,0x01,0xf0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x01,0xfa,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,
-0x61,0xfb,0x09,0xf2,0xff,0xff,0xff,0xff,0x40,0x22,0x16,0x00,0x1b,0xf2,0xff,0xff,
-0xdc,0x84,0x1b,0xfa,0x18,0xf2,0xff,0xff,0x06,0xfa,0x02,0xf2,0xff,0xff,0xfc,0xa0,
-0x00,0x64,0x03,0x02,0x01,0xfa,0x02,0xfa,0xff,0xff,0x16,0xf2,0x01,0xf0,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x61,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x05,0x02,0x65,0xf1,0x05,0x64,0x64,0x40,0x80,0x26,0x61,0xfb,0x04,0x00,0x60,0xf3,
-0xff,0xff,0x80,0xb4,0x60,0xfb,0x60,0xf3,0x65,0xf3,0x60,0x45,0x60,0x47,0xb4,0x84,
-0x01,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,
-0x61,0xf1,0x02,0xb0,0x00,0x64,0x06,0x02,0xd0,0x80,0xff,0xff,0x03,0x03,0x62,0x60,
-0xc8,0x78,0xff,0xff,0x62,0xf3,0x60,0xf3,0x00,0xa0,0xff,0xff,0x03,0x02,0x62,0x60,
-0xc6,0x78,0xff,0xff,0x60,0x40,0xff,0x26,0x6d,0x00,0xcb,0xf3,0xff,0xff,0xfc,0xa0,
-0x64,0xf3,0x67,0x02,0x00,0xb8,0xcc,0x84,0x16,0x03,0x64,0xfb,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x80,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x67,0x60,0x84,0x64,0xa0,0xd3,0x07,0x02,0x00,0xa0,0xff,0xff,0x04,0x03,0x67,0x60,
-0x8a,0x62,0x01,0x64,0xa2,0xdb,0x04,0xf2,0xff,0xff,0x00,0xb8,0xcc,0x84,0x01,0x03,
-0x04,0xfa,0x00,0x65,0x64,0xf3,0x63,0xf3,0x00,0xa0,0xff,0xff,0x0d,0x02,0x64,0xfb,
-0x60,0x45,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x05,0x64,0xa2,0xdb,0x68,0x60,
-0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0x65,0x04,0xf2,0x05,0xf2,0x00,0xa0,0xff,0xff,
-0x02,0x02,0x04,0xfa,0x01,0x65,0x65,0x40,0x00,0x36,0x2d,0x00,0x60,0xf3,0xff,0xff,
-0x08,0xbc,0x60,0xfb,0x00,0x64,0x08,0xfa,0x0a,0xf2,0x15,0xf0,0x60,0x40,0x01,0x26,
-0x0d,0x00,0x66,0x60,0x2a,0x62,0xa2,0xd3,0x37,0x7c,0xfe,0xa0,0xff,0xff,0x06,0x05,
-0x0c,0xf2,0x25,0x7c,0xfe,0xa0,0xff,0xff,0x01,0x05,0x15,0x7c,0x00,0xf8,0x65,0xf3,
-0xff,0xff,0xff,0xff,0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0x18,0x60,0xc8,0x62,0xa2,0xd3,0x10,0x7c,0x0b,0xfa,0x0d,0xf8,0x62,0x60,0xbe,0x78,
-0xff,0xff,0x19,0x00,0x2e,0x00,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x73,0x45,0xc4,0x84,0x60,0x53,0x62,0x60,0x58,0x4e,0xe8,0x78,0xff,0xff,
-0x00,0xfa,0xe1,0xf1,0x67,0x60,0x7e,0x65,0x05,0x64,0x64,0x40,0x00,0x3a,0xa5,0xdb,
-0x62,0x60,0xbe,0x78,0xff,0xff,0x61,0xf3,0xff,0xff,0x00,0xa0,0x32,0x64,0x0a,0x02,
-0x61,0xfb,0x65,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,
-0xd5,0x78,0xff,0xff,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x01,0x64,0x0a,0xfa,
-0x51,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,0x00,0x64,0x4c,0x02,0x60,0xfb,0x66,0xf3,
-0xff,0xff,0x00,0xa0,0x01,0x64,0x04,0x02,0x63,0x60,0x58,0x4e,0x03,0x78,0xff,0xff,
-0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0x26,0x02,0x73,0x44,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x8c,
-0xa4,0xf3,0xff,0xff,0x60,0x41,0x73,0x44,0x61,0x45,0xd4,0x80,0xff,0xff,0x03,0x0d,
-0x2c,0x45,0xc4,0x84,0xf9,0x00,0x60,0x53,0x08,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x08,0x02,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x24,0xf2,0xff,0xff,0xdc,0x84,
-0x24,0xfa,0x62,0x60,0x58,0x4e,0xe8,0x78,0xff,0xff,0x00,0xfa,0x08,0x00,0x32,0x64,
-0x00,0xfa,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x01,0x64,0x0a,0xfa,0x65,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x06,0x03,0x00,0xf2,0x14,0x65,0xd4,0x80,0x02,0x06,
-0x65,0x44,0x00,0xfa,0x00,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x04,0x06,
-0x61,0xfb,0x02,0x00,0x32,0x64,0x61,0xfb,0x1e,0x60,0x92,0x64,0xa0,0xd3,0x03,0xfa,
-0xff,0xff,0x61,0xf3,0x60,0x45,0x60,0x45,0x68,0x60,0xcc,0x62,0x50,0x60,0x00,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x5e,0x60,0x53,0x78,0xff,0xff,
-0xa4,0xf1,0x73,0x44,0x64,0x45,0x86,0xf1,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xc0,0x84,0x2e,0x58,0xff,0xff,0xa4,0xf3,0xff,0xff,0x60,0x45,0x73,0x44,
-0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x60,0x45,0x01,0x60,0xf4,0x64,
-0xd4,0x80,0x65,0x44,0x05,0x05,0x23,0xf2,0xff,0xff,0xdc,0x84,0x23,0xfa,0x32,0x64,
-0xfa,0xa0,0xff,0xff,0x01,0x05,0x06,0x64,0xfb,0xa4,0x2e,0x58,0xff,0xff,0x60,0x45,
-0x68,0x60,0xcc,0x62,0x20,0x60,0x00,0x64,0xb4,0x84,0xa2,0xdb,0x65,0x44,0x2e,0x43,
-0x11,0xfc,0x10,0x60,0x00,0x65,0x60,0x40,0xff,0x22,0x00,0x65,0x65,0x44,0x66,0xfb,
-0x21,0xf2,0xff,0xff,0xdc,0x84,0x21,0xfa,0x7b,0xf5,0xff,0xff,0x81,0xf1,0x2b,0xf8,
-0x31,0xf8,0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,0x2d,0xf8,
-0x33,0xf8,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,
-0x30,0xf8,0xff,0xff,0x01,0x60,0x48,0x64,0xb4,0x84,0x29,0xfa,0x00,0x63,0x22,0xfc,
-0x2a,0x60,0x20,0x64,0x0e,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xd6,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,
-0x63,0x60,0x52,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,
-0xa2,0xd1,0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x7b,0xf5,0x22,0xf0,0x00,0x60,0x7a,0x66,0x64,0x44,0x0f,0x22,0x04,0x00,0x22,0xf2,
-0xff,0xff,0xdc,0x84,0x22,0xfa,0x5a,0x60,0x3a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0xf0,
-0x68,0x60,0xcc,0x62,0x20,0x60,0x02,0x64,0xa2,0xdb,0xff,0xff,0xf4,0xc6,0x7e,0x00,
-0x00,0x10,0x44,0x4e,0x2e,0x58,0xff,0xff,0x1e,0xf2,0xff,0xff,0xdc,0x84,0x1e,0xfa,
-0x68,0x60,0xcc,0x62,0x30,0x60,0x00,0x64,0xa2,0xdb,0x2e,0x43,0x11,0xfc,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0xbf,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0xff,0xff,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xd6,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x63,0x60,0xa1,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x08,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xd6,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x63,0x60,0xb5,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x9f,0x60,
-0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xff,0xff,0xbe,0xfe,
-0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,
-0x00,0x60,0x7a,0x66,0x68,0x60,0xcc,0x62,0x30,0x60,0x01,0x64,0xa2,0xdb,0x11,0xf2,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x1f,0xf2,0xff,0xff,0xdc,0x84,0x1f,0xfa,
-0x68,0x60,0xcc,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x2e,0x43,0x11,0xfc,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0xbf,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0xff,0xff,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xd6,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x63,0x60,0xf9,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x65,0xf3,0xff,0xff,0xff,0xff,0x80,0x26,0x14,0x00,0x3c,0x60,0xa2,0x62,0x2a,0x44,
-0xa2,0xdb,0xca,0x82,0x02,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xd6,0x62,
-0x20,0x60,0x00,0x64,0xa2,0xdb,0x64,0x60,0x13,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x9f,0x60,0xff,0x61,0xa1,0x84,
-0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x5b,0x60,0x24,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x00,0x60,0x7a,0x66,0x68,0x60,0xcc,0x62,0x40,0x60,0x01,0x64,
-0xa2,0xdb,0x11,0xf2,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,
-0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x3c,0x60,0xb2,0x62,0x28,0x60,0x9e,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x1e,0x60,0xd4,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,
-0xff,0xff,0x62,0xf3,0x26,0x46,0x60,0x40,0xff,0x22,0x37,0x00,0x60,0xf3,0x0f,0xf2,
-0x60,0x43,0x29,0xf0,0x60,0x40,0x10,0x2a,0x15,0x00,0x63,0x44,0x60,0x43,0x68,0x60,
-0xcc,0x62,0x00,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x63,0x44,0xa2,0xdb,
-0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x40,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x1b,0x00,0x64,0x40,0x20,0x2b,0x02,0x00,0x40,0xbb,0x01,0x00,0xbf,0xb3,
-0x60,0xfd,0x63,0x44,0x60,0x43,0x68,0x60,0xcc,0x62,0x00,0x60,0x10,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x63,0x44,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x10,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x26,0x46,0x2f,0x58,0xff,0xff,
-0x22,0x02,0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,
-0x65,0x60,0x8f,0x78,0xff,0xff,0x65,0x60,0x81,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,
-0xfc,0xa0,0xfd,0xa0,0xd5,0x02,0x00,0x64,0x40,0x48,0x26,0x46,0x38,0xf2,0x00,0xf4,
-0x10,0x63,0xf4,0xa4,0x60,0x41,0x00,0x65,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x40,0x4c,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x45,0x2c,0x44,0x04,0xa8,0x05,0xa8,0x06,0x03,
-0xc9,0x81,0x2e,0x03,0xd5,0x81,0xc7,0x83,0xce,0x06,0xde,0x00,0x41,0x4c,0x67,0x60,
-0x80,0x61,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x59,0xdb,0x63,0x44,0x01,0x22,0x05,0x00,
-0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,
-0x59,0xdb,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0x59,0xdb,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0x59,0xdb,0x2c,0x41,0xf8,0xa1,0xb4,0x00,
-0x65,0x41,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x40,0x4c,0x64,0xfb,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0x63,0xfb,0x62,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x49,0x03,0x63,0x44,
-0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,
-0x01,0xa3,0x00,0x7f,0x40,0x4d,0x2c,0x44,0x00,0xb8,0xff,0xff,0x05,0x02,0x2d,0x44,
-0x01,0x2a,0x02,0x00,0x10,0x65,0x45,0x48,0x66,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x2f,0x03,0x2d,0x44,0xfe,0xb5,0x3c,0x60,0xd2,0x64,0xa0,0xd3,0xff,0xff,0x00,0x7f,
-0xd4,0x84,0x04,0xa4,0x19,0x04,0x60,0x45,0xd5,0x80,0xfc,0xa5,0x15,0x04,0x3c,0x60,
-0xd0,0x64,0xa0,0xd1,0xc7,0x83,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,
-0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xa0,0x80,0xff,0xff,
-0x03,0x03,0x28,0x44,0x20,0xbc,0x40,0x48,0x28,0x44,0xff,0x22,0x09,0x00,0x60,0xf1,
-0xff,0xff,0xb0,0x84,0x60,0xfb,0x60,0x45,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x68,0x60,0xcc,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x26,0x46,0x2f,0x58,
-0xff,0xff,0x0e,0x48,0x65,0x60,0x58,0x4e,0xfc,0x78,0xff,0xff,0x10,0x03,0x29,0xf2,
-0xa3,0xd1,0x60,0x40,0x10,0x2b,0x03,0x00,0x64,0x44,0x02,0xbc,0x02,0x00,0x64,0x44,
-0xfd,0xb4,0xa3,0xdb,0x0a,0xa3,0x3c,0x64,0xa3,0xdb,0xf6,0xa3,0x50,0x00,0x2c,0x43,
-0xa3,0xd3,0xff,0xff,0x60,0x40,0x01,0x26,0x37,0x00,0x43,0x4c,0x29,0xf0,0x01,0x64,
-0x64,0x40,0x10,0x27,0x03,0x64,0xbd,0xdb,0x2e,0xf2,0xff,0xff,0xbd,0xdb,0x2f,0xf2,
-0xbd,0xdb,0xff,0xff,0x30,0xf2,0xbd,0xdb,0x01,0x60,0x92,0x64,0xa0,0xd3,0xff,0xff,
-0x60,0x40,0x08,0x2a,0x02,0x00,0x08,0x7f,0x0a,0x00,0x04,0x2a,0x02,0x00,0x04,0x7f,
-0x06,0x00,0x02,0x2a,0x02,0x00,0x02,0x7f,0x02,0x00,0x01,0x7f,0x01,0x7e,0x60,0x47,
-0xbd,0xdb,0x3c,0x64,0xbd,0xdb,0x29,0xf0,0x34,0xf2,0x64,0x40,0x08,0x27,0xcc,0x84,
-0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x2c,0x43,0x1a,0x00,0x61,0x44,0xdc,0x84,0xd0,0x80,
-0xff,0xff,0xcb,0x06,0x72,0xfb,0xc9,0x00,0x72,0xf1,0x38,0x60,0xe2,0x63,0x00,0x61,
-0xa3,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0xf0,0x00,0x10,0xa3,0x61,0x44,0xf1,0xa0,
-0xdd,0x81,0xf6,0x04,0x00,0x63,0x3d,0x60,0x4a,0x62,0x01,0x64,0xa2,0xdb,0x08,0x4e,
-0x00,0xbb,0x2e,0x58,0xff,0xff,0x28,0x60,0xe2,0x65,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x44,0xd3,0x62,0x43,0x43,0x4c,0x60,0x40,0x01,0x2a,0x10,0x00,
-0x02,0xa3,0x2e,0xf2,0x50,0xfe,0xbd,0xd1,0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,
-0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x02,0x02,0xf8,0xa3,0x1e,0x00,
-0x72,0xf1,0x38,0x60,0xe2,0x63,0x64,0x41,0xff,0x22,0x17,0x00,0xbd,0xd1,0x2e,0xf2,
-0x50,0xfe,0x64,0x40,0x01,0x26,0x04,0x00,0xcd,0x81,0x0e,0xa3,0xf7,0x02,0x0d,0x00,
-0xbd,0xd1,0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,
-0xd0,0x80,0xcd,0x81,0xe3,0x01,0x08,0xa3,0xe9,0x02,0x00,0x63,0x00,0xbb,0x2e,0x58,
-0xff,0xff,0xff,0x60,0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0xff,0xff,0x47,0xf3,
-0xff,0xff,0xe8,0x84,0xe8,0x84,0x01,0x00,0x00,0x64,0x1c,0xfa,0x46,0x4d,0xbd,0xf1,
-0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,0xff,0xff,0x81,0xf1,
-0x31,0xf8,0x82,0xf1,0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x3b,0x60,0x28,0x63,
-0x80,0xf1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,0x00,0x7f,0xbd,0xdb,0x43,0xf3,
-0x47,0xf1,0x01,0xb4,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xb0,0x84,0x02,0xbc,
-0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x59,0x60,0x20,0x62,0xa2,0xd3,
-0xda,0x85,0xbd,0xdb,0x60,0x41,0x06,0xa4,0x8f,0xfb,0xa5,0xd1,0xda,0x85,0x64,0x44,
-0x00,0x7f,0xcd,0x81,0xbd,0xdb,0x05,0x03,0x64,0x47,0x00,0x7f,0xcd,0x81,0xbd,0xdb,
-0xf4,0x02,0x01,0x64,0xbd,0xdb,0x8f,0xf1,0x17,0x60,0xd2,0x62,0xa2,0xd3,0xda,0x85,
-0xbd,0xdb,0x43,0x48,0x60,0x41,0x41,0x4c,0xc0,0x84,0x02,0xa4,0x8f,0xfb,0xa5,0xd1,
-0xda,0x85,0x64,0x44,0x00,0x7f,0xcd,0x81,0xbd,0xdb,0x05,0x03,0x64,0x47,0x00,0x7f,
-0xcd,0x81,0xbd,0xdb,0xf4,0x02,0x03,0x64,0xbd,0xdb,0x01,0x64,0xbd,0xdb,0x7f,0xf3,
-0xbd,0xdb,0xff,0xff,0x8f,0xf3,0xff,0xff,0x03,0xa4,0x8f,0xfb,0x06,0x64,0xbd,0xdb,
-0x02,0x64,0xbd,0xdb,0x86,0xf1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,0x00,0x7f,
-0xbd,0xdb,0x8f,0xf3,0xff,0xff,0x04,0xa4,0x8f,0xfb,0x07,0x64,0xbd,0xdb,0x06,0x64,
-0xbd,0xdb,0x17,0x60,0x82,0x62,0xa2,0xd1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,
-0x00,0x7f,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x1f,0x60,0x54,0x62,0xa2,0xd3,0xbd,0xdb,
-0x1f,0x60,0x56,0x62,0xa2,0xd3,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x8f,0xf3,0xff,0xff,
-0x08,0xa4,0x8f,0xfb,0x00,0x64,0xa3,0xdb,0x17,0x60,0xdc,0x62,0xa2,0xd1,0x28,0x43,
-0x2c,0x41,0xa3,0xd3,0xff,0xff,0x60,0x40,0x02,0x3a,0x04,0x00,0x64,0x40,0x01,0x2a,
-0x14,0x00,0x11,0x00,0x04,0x3a,0x04,0x00,0x64,0x40,0x02,0x2a,0x0e,0x00,0x0b,0x00,
-0x0b,0x3a,0x04,0x00,0x64,0x40,0x04,0x2a,0x08,0x00,0x05,0x00,0x16,0x3a,0x05,0x00,
-0x64,0x40,0x08,0x2a,0x02,0x00,0x80,0xbc,0xa3,0xdb,0xcd,0x81,0xdb,0x83,0xe1,0x02,
-0x8f,0xf3,0xff,0xff,0x60,0x41,0x08,0xa4,0x38,0xfa,0x00,0xf4,0x3b,0x60,0x28,0x63,
-0x01,0xf2,0xff,0xff,0x7c,0x7e,0x01,0xfa,0x0c,0x65,0xbd,0xd3,0xbd,0xd1,0x60,0x47,
-0xb0,0x87,0xa5,0xda,0xda,0x85,0xcd,0x81,0xcd,0x81,0x01,0x03,0xf6,0x02,0x2d,0x46,
-0x2e,0x58,0xff,0xff,0x00,0xf4,0x07,0xf0,0x73,0xf3,0x64,0x40,0x02,0x2a,0x49,0x00,
-0x02,0xbc,0x73,0xfb,0x60,0x45,0x26,0x46,0x2e,0xf2,0xff,0xff,0x60,0x47,0xbd,0xf3,
-0x60,0x5c,0x60,0x47,0x2f,0xf2,0xd0,0x80,0x60,0x47,0xbe,0xf3,0x60,0x5c,0x60,0x47,
-0x10,0x07,0x0b,0x04,0x30,0xf2,0xd0,0x80,0x60,0x47,0x60,0x5c,0xbf,0xf3,0x09,0x07,
-0x04,0x04,0x60,0x47,0xd0,0x80,0xff,0xff,0x04,0x07,0xfe,0x64,0xa4,0x84,0x73,0xfb,
-0xff,0xff,0x31,0xf2,0x32,0xf0,0x33,0xf0,0xb0,0x84,0xb0,0x84,0xff,0xff,0x21,0x03,
-0x3b,0x60,0x28,0x63,0x31,0xf0,0xbd,0xd9,0x32,0xf0,0xff,0xff,0xbd,0xd9,0x33,0xf0,
-0xbd,0xd9,0xff,0xff,0x00,0xf4,0x02,0xf0,0xbd,0xd9,0xff,0xff,0x03,0xf0,0xbd,0xd9,
-0x04,0xf0,0xff,0xff,0xbd,0xd9,0x05,0xf0,0xbd,0xd9,0xff,0xff,0x06,0xf0,0xbd,0xd9,
-0x07,0xf0,0xff,0xff,0xbd,0xd9,0x00,0x64,0x08,0xf0,0xa3,0xdb,0x64,0x47,0x60,0x45,
-0x00,0x3b,0x62,0x00,0xbd,0xdb,0xdc,0x84,0xe8,0x81,0x10,0x64,0x58,0xd0,0xcd,0x81,
-0xbd,0xd9,0xfc,0x02,0xd8,0x83,0x04,0x64,0x40,0x4d,0x09,0x61,0x65,0x40,0x01,0x2a,
-0xbd,0xd0,0xff,0xff,0x64,0x44,0x00,0x7f,0x2d,0xda,0x5a,0x8d,0x64,0x47,0x00,0x7f,
-0x2d,0xda,0xcd,0x81,0x5a,0x8d,0xf4,0x02,0x3b,0x60,0x5c,0x63,0x04,0x61,0x65,0x40,
-0x01,0x26,0x02,0xa1,0xa1,0xd2,0xff,0xff,0x01,0xa8,0x59,0xd2,0x4b,0x02,0xfc,0xa0,
-0xff,0xff,0x48,0x07,0xbd,0xdb,0x59,0xd0,0xcc,0x84,0xbd,0xd9,0xfc,0x02,0x00,0x64,
-0xbd,0xdb,0x3b,0x60,0x6a,0x63,0x59,0xd2,0x59,0xd0,0x03,0xa8,0x7f,0xf3,0x3a,0x02,
-0x59,0xd0,0xff,0xff,0xd0,0x80,0xbd,0xd9,0x35,0x02,0x59,0xd2,0x59,0xd0,0x06,0xa8,
-0x59,0xd0,0x30,0x02,0x59,0xd2,0xff,0xff,0x60,0x47,0xb0,0x84,0xbd,0xdb,0x3b,0x60,
-0x5e,0x63,0x00,0x61,0xa3,0xd3,0xff,0xff,0x60,0x40,0xff,0x22,0x16,0x00,0x80,0x2a,
-0x11,0x00,0x7f,0xb4,0xa3,0xdb,0x60,0x40,0x02,0x3a,0x02,0x00,0x01,0xb9,0x0a,0x00,
-0x04,0x3a,0x02,0x00,0x02,0xb9,0x06,0x00,0x0b,0x3a,0x02,0x00,0x04,0xb9,0x02,0x00,
-0x16,0x36,0x08,0xb9,0x02,0xa3,0xe6,0x00,0x0d,0x00,0x3b,0x60,0x68,0x63,0x61,0x44,
-0xa3,0xdb,0x3b,0x60,0x28,0x63,0x39,0x60,0xf0,0x64,0x23,0x61,0xbd,0xd1,0xcd,0x81,
-0x58,0xd9,0xfc,0x02,0x26,0x46,0x2f,0x58,0xff,0xff,0xcb,0xf3,0x0f,0xf0,0xfd,0xa0,
-0xff,0xff,0x07,0x02,0x64,0x40,0x60,0x26,0x04,0x00,0x68,0x60,0x58,0x4e,0xba,0x78,
-0xff,0xff,0x2f,0x58,0xff,0xff,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x4d,0x05,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0x6e,0xf1,0xfc,0xa0,0xff,0xff,0x46,0x02,0x64,0x40,
-0x01,0x2a,0x43,0x00,0x68,0x60,0x58,0x4e,0x86,0x78,0xff,0xff,0x26,0x46,0x3d,0x02,
-0x2e,0xf0,0x2b,0xf8,0x2f,0xf0,0xff,0xff,0x2c,0xf8,0x30,0xf0,0x2d,0xf8,0x66,0x60,
-0x58,0x4e,0x45,0x78,0xff,0xff,0x26,0x46,0x00,0xf0,0x04,0x64,0x03,0xfa,0x04,0xf8,
-0x00,0x64,0x0b,0xfa,0x0c,0xfa,0x0f,0xfa,0xff,0xff,0x85,0xf3,0x38,0xf0,0x50,0xbc,
-0x29,0xfa,0x17,0xf8,0x0c,0x64,0x15,0xfa,0x20,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,
-0x13,0xfa,0x1c,0x64,0x21,0xfa,0x08,0x64,0x28,0xfa,0x00,0x63,0x22,0xfc,0x16,0xfc,
-0x07,0xfc,0x01,0x64,0x19,0xfc,0x1c,0xfc,0x14,0xfa,0xff,0x67,0x0e,0xfa,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x00,0x66,0x46,0x46,0x2f,0x58,0xff,0xff,0xcb,0xf3,
-0x0f,0xf0,0xfd,0xa0,0x20,0x64,0x47,0x02,0x64,0x40,0x60,0x26,0x10,0x64,0x60,0xf1,
-0xff,0xff,0xb0,0x84,0x60,0xfb,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x50,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2e,0xf2,0xff,0xff,
-0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,
-0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,
-0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,0x60,0x40,
-0x10,0x2b,0x09,0x00,0x69,0x60,0x58,0x4e,0xdf,0x78,0xff,0xff,0x04,0x03,0x69,0x60,
-0x58,0x4e,0xf9,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,0x00,0xf4,0x04,0x63,0x06,0x00,
-0x00,0xf4,0x07,0xf0,0x10,0x63,0x64,0x40,0x02,0x2a,0x27,0x00,0xbd,0xd2,0xff,0xff,
-0x60,0x47,0x00,0x3a,0x22,0x00,0x60,0x41,0x00,0x36,0x1d,0x00,0x59,0x60,0x20,0x62,
-0xa2,0xd3,0x61,0x45,0xd4,0x80,0xff,0xff,0x18,0x02,0xda,0x82,0x61,0x40,0xfe,0x22,
-0x08,0x00,0x62,0x45,0xbd,0xd2,0xa5,0xd1,0xda,0x82,0xd0,0x80,0xc9,0x81,0xf6,0x03,
-0x0c,0x00,0x61,0x40,0x00,0x36,0x07,0x00,0x62,0x45,0xa3,0xd2,0xa5,0xd1,0xff,0xff,
-0x90,0x80,0xff,0x26,0x02,0x00,0x00,0x64,0x01,0x00,0x01,0x64,0x01,0xb4,0x2e,0x58,
-0xff,0xff,0x2e,0xf0,0x2b,0xf8,0x2f,0xf0,0xff,0xff,0x2c,0xf8,0x30,0xf0,0x2d,0xf8,
-0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,
-0x30,0xf8,0x81,0xf1,0x31,0xf8,0xff,0xff,0x82,0xf1,0x32,0xf8,0x83,0xf1,0xff,0xff,
-0x33,0xf8,0x00,0xf0,0x04,0x64,0x03,0xfa,0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,
-0x0f,0xfa,0xff,0xff,0x85,0xf3,0xff,0xff,0xb0,0xbc,0x29,0xfa,0x0c,0x64,0x15,0xfa,
-0x20,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0x13,0xfa,0x1c,0x64,0x21,0xfa,0x08,0x64,
-0x28,0xfa,0x00,0x63,0x22,0xfc,0x16,0xfc,0x07,0xfc,0x01,0x64,0x19,0xfc,0x1c,0xfc,
-0x14,0xfa,0xff,0x67,0x0e,0xfa,0x38,0xf0,0x06,0x64,0x38,0xfa,0x17,0xfa,0x44,0x48,
-0x00,0xf4,0x01,0xf2,0xff,0xff,0x7c,0x7e,0x01,0xfa,0x02,0xf2,0x00,0x63,0x00,0xa0,
-0x04,0xfc,0x07,0x02,0x03,0xf2,0x00,0x63,0xff,0xa0,0xff,0xff,0x3b,0x03,0x0e,0x63,
-0x39,0x00,0xff,0xa0,0x0d,0x63,0x36,0x02,0x43,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,
-0x31,0x00,0x03,0xf2,0x0e,0x63,0xff,0xa0,0xff,0xff,0x1c,0x02,0x0a,0x63,0x80,0x60,
-0x10,0x64,0xbd,0xda,0x00,0x60,0x3a,0x61,0x01,0x60,0x02,0x65,0x55,0x60,0xaa,0x64,
-0xcd,0x81,0xbd,0xda,0xc4,0x84,0xfc,0x02,0x00,0xf4,0x04,0x63,0x06,0x61,0xcd,0x81,
-0xbd,0xda,0xc4,0x84,0xfc,0x02,0x26,0x46,0x88,0x64,0x38,0xfa,0x17,0xfa,0x00,0xf4,
-0x00,0x63,0x10,0x00,0xfd,0xa0,0xff,0xff,0x0d,0x02,0x0f,0x63,0x55,0x60,0xaa,0x65,
-0x05,0xf2,0xff,0xff,0xd4,0x80,0x88,0x64,0x05,0x02,0x28,0x45,0xd4,0x80,0xff,0xff,
-0x01,0x02,0x00,0x63,0x03,0xf2,0x04,0xfc,0xdc,0x84,0x03,0xfa,0x26,0x46,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x00,0x66,0x46,0x46,0x2e,0x58,0xff,0xff,0x1e,0x60,
-0x92,0x62,0xa2,0xd1,0x59,0x60,0x76,0x64,0xa0,0xd3,0xfa,0x65,0xd0,0x80,0xff,0xff,
-0x37,0x0d,0xc4,0x84,0xa2,0xdb,0x59,0x60,0x74,0x64,0xa0,0xd1,0x39,0x60,0xe2,0x64,
-0x64,0x43,0xd0,0x80,0x28,0x60,0xe2,0x64,0x01,0x07,0x60,0x43,0x10,0x61,0xa3,0xd3,
-0xff,0xff,0xff,0xff,0x01,0x2a,0x1e,0x00,0x0a,0x65,0x46,0xd3,0xff,0xff,0xcc,0x84,
-0xa2,0xdb,0x03,0x02,0x00,0x64,0xa3,0xdb,0x15,0x00,0xfe,0xa2,0xa2,0xd3,0xff,0xff,
-0x60,0x47,0x60,0x40,0x08,0x2a,0x02,0x00,0x08,0x7f,0x0a,0x00,0x04,0x2a,0x02,0x00,
-0x04,0x7f,0x06,0x00,0x02,0x2a,0x02,0x00,0x02,0x7f,0x02,0x00,0x01,0x7f,0x01,0x7e,
-0x60,0x47,0xa2,0xdb,0xcd,0x81,0x10,0xa3,0xda,0x02,0x59,0x60,0x74,0x64,0xa0,0xdd,
-0x2e,0x58,0xff,0xff,0x3a,0x60,0xb0,0x63,0x6b,0xf3,0xff,0xff,0x00,0xbc,0x60,0x41,
-0x10,0x03,0x2b,0xf2,0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,0x2d,0xf2,
-0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x04,0x03,0xfa,0xa1,0xff,0xff,
-0xf0,0x02,0x01,0xbc,0x2e,0x58,0xff,0xff,0x3a,0x60,0x38,0x63,0x6a,0xf3,0xe6,0x00,
-0x3a,0x60,0xb0,0x65,0x6b,0xf3,0xff,0xff,0xc4,0x83,0x88,0xa0,0x06,0xa4,0x09,0x05,
-0x6b,0xfb,0xff,0xff,0x2b,0xf2,0xbd,0xdb,0x2c,0xf2,0xff,0xff,0xbd,0xdb,0x2d,0xf2,
-0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3a,0x60,0x38,0x65,0x6a,0xf3,0xff,0xff,0xc4,0x83,
-0x88,0xa0,0x06,0xa4,0x09,0x05,0x6a,0xfb,0xff,0xff,0x2b,0xf2,0xbd,0xdb,0x2c,0xf2,
-0xff,0xff,0xbd,0xdb,0x2d,0xf2,0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3a,0x60,0xb0,0x63,
-0x6b,0xf3,0xff,0xff,0x00,0xbc,0x60,0x41,0x10,0x03,0x2e,0xf2,0x50,0xfe,0xbd,0xd1,
-0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x04,0x03,0xfa,0xa1,0xff,0xff,0xf0,0x02,0x01,0xbc,0x2e,0x58,0xff,0xff,
-0x3a,0x60,0xb0,0x65,0x6b,0xf3,0xff,0xff,0xc4,0x83,0x88,0xa0,0x06,0xa4,0x09,0x05,
-0x6b,0xfb,0xff,0xff,0x2e,0xf2,0xbd,0xdb,0x2f,0xf2,0xff,0xff,0xbd,0xdb,0x30,0xf2,
-0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3c,0x60,0x64,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,
-0x60,0x46,0x03,0x02,0x3d,0x60,0x2f,0x78,0xff,0xff,0x26,0x45,0xd4,0x80,0x0f,0xf0,
-0xf9,0x03,0x64,0x44,0x70,0xb0,0x70,0x2a,0x03,0x00,0x6f,0x60,0x7a,0x78,0xff,0xff,
-0x64,0x40,0x04,0x2a,0x13,0x00,0x67,0x60,0x60,0x62,0x00,0x64,0xa2,0xdb,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x40,0x2b,0x0f,0x00,0x64,0x40,0x80,0x2b,0x07,0x00,0x1b,0xf2,
-0x22,0xf0,0x60,0x47,0xc0,0xb4,0xb0,0x84,0x22,0xfa,0x05,0x00,0x00,0x64,0x40,0x46,
-0x3d,0x60,0x2f,0x78,0xff,0xff,0x32,0x40,0x01,0x2a,0x07,0x00,0x70,0x60,0xb6,0x78,
-0xff,0xff,0x03,0x03,0x6f,0x60,0x7a,0x78,0xff,0xff,0x46,0x46,0x0f,0xf0,0xff,0xff,
-0x64,0x44,0x80,0x26,0x0e,0x00,0x32,0x40,0x01,0x2a,0x08,0x00,0x22,0xf0,0x07,0x60,
-0x01,0x64,0xb0,0x84,0x22,0xfa,0x6f,0x60,0x93,0x78,0xff,0xff,0x6f,0x60,0x7a,0x78,
-0xff,0xff,0x08,0x26,0x2a,0x00,0x5b,0x60,0x22,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x03,0x27,0x06,0x00,0x67,0x60,0x78,0x62,0xa2,0xd3,0xff,0xff,
-0x01,0xa4,0xa2,0xdb,0x32,0x44,0x01,0x2a,0x05,0x00,0x22,0xf0,0x07,0x60,0x02,0x64,
-0xb0,0x84,0x04,0x00,0x02,0x2a,0x06,0x00,0x00,0x60,0x02,0x64,0x22,0xfa,0x6f,0x60,
-0x93,0x78,0xff,0xff,0x6f,0x60,0x7a,0x78,0xff,0xff,0x32,0x40,0x01,0x2a,0x08,0x00,
-0x22,0xf0,0x07,0x60,0x00,0x64,0xb0,0x84,0x22,0xfa,0x6f,0x60,0x93,0x78,0xff,0xff,
-0x29,0xf2,0x0f,0xf0,0x60,0x40,0xa4,0x36,0x08,0x00,0x0c,0xb4,0x04,0x36,0x02,0x00,
-0x0c,0x3a,0x06,0x00,0x6f,0x60,0x7a,0x78,0xff,0xff,0x6f,0x60,0x75,0x78,0xff,0xff,
-0x64,0x40,0x60,0x26,0x03,0x00,0x6b,0x60,0x84,0x78,0xff,0xff,0x95,0xf3,0x29,0xf2,
-0x00,0xbc,0xd3,0xf1,0x20,0x03,0x64,0x40,0x00,0x3a,0x4c,0x00,0x60,0x40,0x40,0x36,
-0x49,0x00,0x80,0x3a,0x15,0x00,0x5c,0x63,0x61,0x60,0xbc,0x61,0xbd,0xd2,0xa1,0xd1,
-0x02,0xa1,0xbd,0xd2,0xd0,0x80,0xa1,0xd1,0x02,0xa1,0x0a,0x02,0xd0,0x80,0xbd,0xd2,
-0xa1,0xd1,0x06,0x02,0xd0,0x80,0xff,0xff,0x03,0x02,0x6d,0x60,0xd3,0x78,0xff,0xff,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0x5c,0x63,0x60,0x40,0x02,0x2b,0x62,0x63,0xbd,0xd2,
-0x81,0xf1,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x07,0x02,0xd0,0x80,0xbd,0xd2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x1e,0x03,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0x29,0xf2,
-0x04,0x04,0x60,0x40,0x80,0x36,0x16,0x00,0x12,0x00,0x20,0x40,0x40,0x26,0xf9,0x00,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0x29,0xf2,0xfc,0xa0,0x2b,0xf0,0x08,0x02,0x60,0x40,
-0x80,0x36,0x02,0x00,0x40,0x3a,0x03,0x00,0x64,0x40,0x01,0x26,0x03,0x00,0x6f,0x60,
-0x7a,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,0x0c,0xb4,0x08,0x3a,0x3a,0x00,0x70,0x60,
-0x15,0x78,0xff,0xff,0x17,0x60,0x74,0x64,0xa0,0xd3,0xcb,0xf3,0x00,0xa0,0xfe,0xa0,
-0xee,0x02,0xed,0x03,0x5a,0x60,0x88,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,
-0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,
-0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x8c,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x0f,0xf2,0xff,0xff,0x60,0x40,0x40,0x26,0x42,0x00,0x32,0x44,0x02,0x26,
-0x3f,0x00,0x58,0x60,0x5a,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0x60,0x41,0x13,0x03,
-0x58,0x60,0x5e,0x63,0x2b,0xf2,0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,
-0x2d,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xfa,0xa1,0x04,0x01,0xf2,0x02,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x20,0x02,
-0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xf4,0xd6,0x7e,0x00,0x00,0x10,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,
-0x92,0x78,0xff,0xff,0x00,0x03,0x6f,0x60,0x64,0x78,0xff,0xff,0x32,0x40,0x02,0x26,
-0x2e,0x00,0x29,0xf0,0x43,0xf3,0x64,0x40,0x08,0x2a,0x29,0x00,0x64,0x40,0x40,0x27,
-0x0a,0x00,0x02,0x2a,0x08,0x00,0x38,0xf2,0xff,0xff,0x00,0xa8,0xff,0xff,0x03,0x03,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0x29,0xf0,0x03,0x67,0xa0,0x84,0xff,0xff,0x00,0x37,
-0x62,0x63,0x02,0x37,0x5c,0x63,0x01,0x37,0x56,0x63,0x03,0x37,0x10,0x00,0xbd,0xd2,
-0x81,0xf1,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x07,0x02,0xd0,0x80,0xbd,0xd2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x03,0x03,0x6f,0x60,0x7a,0x78,0xff,0xff,0x1e,0x60,
-0x9e,0x62,0xa2,0xd5,0x1e,0x60,0x92,0x62,0xa2,0xd3,0xff,0xff,0x40,0x48,0x09,0xf2,
-0x46,0x4b,0x00,0xbe,0x12,0xf2,0x19,0x03,0x60,0x45,0x28,0x44,0xd4,0x81,0x27,0x60,
-0x10,0x65,0xd5,0x80,0x46,0x45,0xf3,0x04,0x09,0xf2,0x2b,0x46,0x09,0xfa,0x5b,0x60,
-0x18,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0xa2,0xff,0x1a,0x60,0x58,0x4f,
-0x9e,0x78,0xff,0xff,0xa3,0xff,0x2b,0x46,0xe2,0x00,0x26,0x46,0x34,0xf2,0xff,0xff,
-0x0f,0xb4,0x29,0xf0,0x03,0x02,0x64,0x40,0x04,0x2b,0x67,0x00,0x60,0x40,0x0f,0x26,
-0x7d,0x00,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,
-0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,
-0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x1e,0x60,0x92,0x62,0xa2,0xd1,0xff,0xff,0x12,0xf8,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0x64,
-0x09,0xfa,0x05,0xf2,0x06,0xf2,0x60,0x43,0x05,0xfa,0x60,0x46,0x01,0xf0,0x7f,0x60,
-0xff,0x64,0xa0,0x84,0x01,0xfa,0x00,0x64,0x00,0xf0,0x00,0xfa,0xc0,0x80,0x44,0x45,
-0x08,0x03,0x25,0x46,0x05,0xfc,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0x26,0x46,0x70,0x60,0x58,0x4e,0x99,0x78,0xff,0xff,0x09,0x02,0x2b,0x46,
-0x26,0x44,0x09,0xfa,0x6f,0x60,0x8e,0x78,0xff,0xff,0x6d,0x60,0xb4,0x78,0xff,0xff,
-0x09,0x45,0x09,0xf0,0x26,0x46,0x09,0xf8,0x2b,0x46,0x26,0x44,0x09,0xfa,0xa2,0xff,
-0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0x5b,0x60,0x12,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x6f,0x60,0x8e,0x78,0xff,0xff,0x70,0x60,0x58,0x4e,
-0x99,0x78,0xff,0xff,0x39,0x02,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,
-0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x78,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,0xce,0x64,
-0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x5b,0x60,0x16,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,
-0x26,0x46,0x6f,0x60,0x7a,0x78,0xff,0xff,0x34,0xf2,0x26,0x46,0x34,0xf2,0x01,0xa5,
-0xd4,0x80,0x29,0x46,0x6a,0x03,0x01,0xa4,0xd4,0x80,0x26,0x46,0x23,0x02,0x5a,0x60,
-0xe6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,
-0xe0,0x84,0x60,0x45,0x5a,0x60,0xea,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,
-0x7a,0x78,0xff,0xff,0x29,0x46,0x05,0xf2,0x09,0xf0,0x2b,0x46,0x09,0xf8,0x26,0x46,
-0x05,0xf4,0x29,0x43,0x00,0xfc,0x26,0x46,0x05,0xfa,0x5a,0x60,0x74,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,
-0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5b,0x60,0x14,0x62,0xa2,0xd3,0xff,0xff,
-0x01,0xa4,0xa2,0xdb,0x6f,0x60,0x7a,0x78,0xff,0xff,0x34,0xfa,0x5a,0x60,0x74,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,
-0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x26,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x26,0x46,
-0x7f,0x60,0xff,0x65,0x00,0xf0,0x38,0xf2,0xff,0xff,0x06,0xf4,0x01,0xf2,0x60,0x41,
-0xa4,0x84,0x01,0xfa,0x00,0xf2,0x00,0x63,0x00,0xfc,0x66,0x45,0x26,0x46,0x00,0xfa,
-0x29,0x46,0x65,0x44,0x05,0xfa,0x64,0x45,0x06,0xf0,0x06,0xfa,0x64,0x46,0x65,0x44,
-0x00,0xfa,0x29,0x46,0x38,0xf0,0x61,0x44,0xc0,0x84,0x38,0xfa,0x26,0x46,0x29,0xf0,
-0x00,0xf2,0x06,0x45,0x00,0xa8,0x66,0x44,0x01,0x02,0x05,0xfa,0x64,0x40,0x04,0x2b,
-0x0e,0x00,0x1e,0x60,0x92,0x62,0xa2,0xd3,0x29,0x46,0x12,0xfa,0xa2,0xff,0x1a,0x60,
-0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0x6f,0x60,0x8e,0x78,0xff,0xff,0x29,0x46,
-0x38,0xf2,0x09,0xf0,0x60,0x47,0x3f,0xfa,0x2b,0x46,0x09,0xf8,0x26,0x46,0xff,0x60,
-0xf0,0x65,0x34,0xf2,0x29,0xf0,0xa4,0x84,0x29,0x46,0x34,0xfa,0xf7,0x60,0xff,0x64,
-0x0b,0xfa,0xa0,0x9c,0x29,0xf8,0x00,0x64,0x09,0xfa,0x06,0xf4,0x80,0x67,0x01,0xf2,
-0x60,0x45,0xb4,0x83,0x01,0xfc,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0x09,0x46,0x29,0x46,0x95,0xf3,0xff,0xff,0x60,0x40,0x01,0x26,0x03,0x00,
-0x6f,0x60,0x51,0x78,0xff,0xff,0x6f,0x60,0x64,0x78,0xff,0xff,0x95,0xf3,0xff,0xff,
-0x60,0x40,0x01,0x26,0x03,0x00,0x6e,0x60,0x7e,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,
-0xff,0xff,0x50,0x3a,0xf0,0x00,0x5c,0x63,0x61,0x60,0xbc,0x61,0xbd,0xd2,0xa1,0xd1,
-0x02,0xa1,0xbd,0xd2,0xd0,0x80,0xa1,0xd1,0x02,0xa1,0xe5,0x02,0xd0,0x80,0xbd,0xd2,
-0xa1,0xd1,0xe1,0x02,0xd0,0x80,0xff,0xff,0xde,0x02,0x26,0x46,0x28,0x60,0xda,0x63,
-0x00,0xf4,0x02,0xf2,0xbd,0xdb,0xff,0xff,0x03,0xf2,0xbd,0xdb,0x04,0xf2,0xff,0xff,
-0xbd,0xdb,0x05,0xf2,0xa3,0xdb,0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,
-0x23,0xf0,0x00,0x61,0xd0,0x84,0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x24,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,
-0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,
-0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,
-0x28,0xf0,0x01,0x03,0xcc,0x84,0xd0,0x84,0xa3,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,
-0x1a,0xff,0x67,0x60,0x6a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x68,0x03,
-0x26,0x46,0x00,0xf4,0x02,0xf2,0x5a,0xd2,0x40,0x47,0x40,0x48,0x5a,0xd2,0x5a,0xd2,
-0x40,0x49,0x60,0x41,0x5a,0xd0,0x80,0xf9,0x40,0x63,0xad,0x80,0xf0,0xa3,0x09,0x02,
-0x3c,0x03,0x29,0x41,0x28,0x44,0x40,0x49,0x27,0x44,0x40,0x48,0x00,0x64,0x40,0x47,
-0xf4,0x00,0xd1,0x80,0x01,0x02,0x31,0x04,0x10,0xa3,0x80,0x60,0x00,0x65,0xa5,0x80,
-0xcf,0x83,0x08,0x02,0x27,0x44,0x60,0x87,0x28,0x44,0x70,0x88,0x29,0x44,0x70,0x89,
-0xf1,0x81,0xf5,0x00,0xe7,0xa3,0x64,0x44,0x00,0xa0,0x00,0x62,0x02,0x02,0x00,0x61,
-0x1c,0x00,0xe0,0x84,0xde,0x82,0xfd,0x04,0x42,0xfe,0xf8,0x84,0x62,0x45,0xc7,0x83,
-0x60,0x45,0x02,0xfe,0xd5,0x84,0x02,0x05,0x01,0x05,0x61,0x44,0xcf,0x83,0x60,0x41,
-0x08,0x03,0x27,0x44,0x60,0x87,0x28,0x44,0x70,0x88,0x29,0x44,0x70,0x89,0xf1,0x81,
-0xf1,0x00,0xce,0x82,0xe9,0x81,0xfd,0x02,0xf1,0x81,0x02,0xf2,0xff,0xff,0x60,0x47,
-0xe8,0x84,0xe8,0x84,0x5a,0xd2,0x3f,0xb5,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0xb4,0x84,0x61,0x45,0xd4,0x84,0xc0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x93,0x67,0x60,0x6a,0x62,0x00,0x64,0xa2,0xdb,0x26,0x46,0x6d,0x00,
-0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x68,0x02,0x3d,0x60,0x1c,0x64,0xa0,0xd3,
-0xff,0xff,0x01,0xbc,0xa2,0xdb,0x29,0xf2,0xff,0xff,0x60,0x40,0x10,0x2b,0x09,0x00,
-0x69,0x60,0x58,0x4e,0xdf,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,0xf9,0x78,
-0xff,0xff,0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,
-0x34,0x03,0x29,0xf2,0x34,0xf0,0x60,0x40,0x08,0x3a,0x61,0x00,0x08,0x2b,0x2a,0x00,
-0x0c,0xa3,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x25,0x02,0x5a,0x60,0xe6,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x5a,0x60,0xea,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x7a,0x78,
-0xff,0xff,0x69,0x00,0x0c,0xa3,0xa3,0xd9,0x32,0x00,0x67,0x60,0x46,0x65,0x29,0xf2,
-0x34,0xf0,0x60,0x40,0xa4,0x36,0x72,0x00,0x08,0x2b,0x28,0x00,0xa5,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x23,0x02,0x5a,0x60,0xe6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,
-0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0xea,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x7a,0x78,0xff,0xff,0xa5,0xd9,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x0c,0x22,0x42,0x00,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,
-0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,
-0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x46,0x48,0x00,0xf4,0x80,0x60,0x87,0x65,0x66,0x44,
-0xac,0x80,0x05,0xf2,0xff,0xff,0xd4,0x80,0x08,0x03,0x07,0x02,0x6f,0x60,0xc4,0x78,
-0xff,0xff,0x03,0x02,0x6a,0x60,0x0c,0x78,0xff,0xff,0x28,0x46,0x38,0xf2,0x49,0xf1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x10,0x07,0x78,0x43,0x04,0xa3,0x56,0xfd,0x0c,0x60,
-0x46,0x64,0xa0,0xd7,0xff,0xff,0xff,0xff,0x6a,0x60,0x0c,0x78,0xff,0xff,0x95,0xf3,
-0xff,0xff,0x60,0x40,0x01,0x26,0x19,0x00,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0x00,0x64,0x40,0x46,0x6a,0x60,0x0c,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x6a,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xd2,0xfe,0x00,0x64,0x40,0x46,0x6a,0x60,0x0c,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,
-0x29,0xf2,0xff,0xff,0x60,0x47,0x56,0x63,0x01,0xb0,0x01,0x26,0x62,0x63,0xbd,0xd0,
-0x39,0xf8,0xbd,0xd0,0xff,0xff,0x3a,0xf8,0xbd,0xd0,0x3b,0xf8,0x02,0xb0,0x5c,0x63,
-0x04,0x03,0x62,0x63,0x03,0xb0,0x02,0x3a,0x6a,0x63,0xbd,0xd0,0x3c,0xf8,0xbd,0xd0,
-0xff,0xff,0x3d,0xf8,0xbd,0xd0,0x3e,0xf8,0x2f,0x58,0xff,0xff,0x06,0x67,0x06,0xf2,
-0x60,0x45,0xd4,0x80,0xff,0xff,0x48,0x02,0x17,0x60,0x7a,0x63,0xa3,0xd3,0x08,0xfe,
-0xff,0xff,0x04,0x26,0x41,0x00,0x07,0x67,0x06,0xfa,0x28,0x46,0x00,0xf0,0x04,0x64,
-0x03,0xfa,0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,0xff,0xff,0x0f,0xfa,0x2e,0xf2,
-0x2b,0xfa,0xff,0xff,0x2f,0xf2,0x2c,0xfa,0x30,0xf2,0xff,0xff,0x2d,0xfa,0xbd,0xf1,
-0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,0x30,0xf8,0x85,0xf3,
-0xff,0xff,0x08,0xbc,0x43,0xf1,0xff,0xff,0x64,0x40,0x01,0x2a,0x03,0x00,0x60,0x47,
-0x40,0xbc,0x60,0x47,0x29,0xfa,0x00,0x63,0x28,0xfc,0x22,0xfc,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xc1,0xfe,0x00,0x64,0x40,0x46,0x48,0xfe,0x6f,0x60,0x5f,0x78,0xff,0xff,0x2b,0xf0,
-0x67,0x44,0xd0,0x80,0x38,0xf2,0x7b,0x02,0xdc,0xa0,0x00,0xf4,0x78,0x04,0x06,0x60,
-0x08,0x65,0x05,0xf2,0x09,0xf0,0xd4,0x80,0x01,0x60,0x00,0x64,0x70,0x02,0xd0,0x80,
-0x67,0x60,0x06,0x63,0x6c,0x02,0x67,0x60,0x0a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x12,0xf0,0xbd,0xd3,0x13,0xf0,0xd0,0x80,0xa3,0xd3,0x60,0x02,0xd0,0x80,
-0x00,0xa0,0x5d,0x02,0x5c,0x03,0xbd,0xf3,0x0f,0xfa,0xbe,0xf3,0xff,0xff,0x10,0xfa,
-0xbf,0xf3,0x11,0xfa,0x02,0x60,0x00,0x64,0x09,0xfa,0x14,0x63,0x1e,0x61,0x26,0x65,
-0xa3,0xd2,0xa1,0xd0,0xa1,0xda,0x64,0x44,0xbd,0xda,0xd5,0x80,0xd9,0x81,0xf8,0x02,
-0x26,0x46,0x85,0xf3,0x43,0xf1,0x08,0xbc,0x60,0x47,0x64,0x40,0x01,0x26,0x40,0xbc,
-0x60,0x47,0x29,0xfa,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x28,0x64,0x38,0xfa,0x00,0x64,
-0x22,0xfa,0x28,0xfa,0xff,0xff,0x81,0xf3,0x2b,0xfa,0x82,0xf3,0xff,0xff,0x2c,0xfa,
-0x83,0xf3,0x2d,0xfa,0xff,0xff,0xbd,0xf3,0x2e,0xfa,0xbe,0xf3,0xff,0xff,0x2f,0xfa,
-0xbf,0xf3,0x30,0xfa,0xff,0xff,0x00,0xf4,0x0f,0xf2,0x10,0xf0,0x60,0x45,0x11,0xf2,
-0x26,0x46,0x33,0xfa,0x64,0x44,0x32,0xfa,0x65,0x44,0x31,0xfa,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x67,0x60,0x0c,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x8e,0x78,0xff,0xff,0x26,0x46,
-0x6b,0x60,0x03,0x78,0xff,0xff,0x1e,0x60,0x9e,0x64,0xa0,0xd5,0x66,0x45,0x09,0xf2,
-0x46,0x4b,0x00,0xbe,0x46,0x49,0x12,0x03,0x30,0xf0,0x65,0x46,0x30,0xf2,0x2f,0xf0,
-0xd0,0x80,0x29,0x46,0xf4,0x02,0x2f,0xf2,0x2e,0xf2,0xd0,0x80,0x65,0x46,0x03,0x02,
-0x2e,0xf0,0xff,0xff,0xd0,0x80,0x29,0x46,0xea,0x02,0x08,0xfe,0x2e,0x58,0xff,0xff,
-0x00,0x64,0x00,0xa0,0x68,0x60,0xd4,0x62,0xa2,0xd3,0x29,0xf0,0x60,0x40,0x00,0x36,
-0x34,0x00,0x01,0x3a,0x07,0x00,0x64,0x44,0x00,0x7f,0x80,0x65,0xd4,0x80,0xff,0xff,
-0x2f,0x03,0x2b,0x00,0x64,0x44,0x0c,0xb4,0xf8,0xa0,0xff,0xff,0x44,0x03,0x64,0x44,
-0x80,0x36,0x26,0x00,0xb4,0x36,0x27,0x00,0xc4,0x36,0x28,0x00,0xd4,0x36,0x29,0x00,
-0x40,0x36,0x2a,0x00,0xe4,0x36,0x2b,0x00,0x00,0x36,0x2c,0x00,0x10,0x36,0x2d,0x00,
-0x20,0x36,0x28,0x00,0x30,0x36,0x29,0x00,0x50,0x36,0x27,0x00,0xa0,0x36,0x28,0x00,
-0xa4,0x36,0x20,0x00,0xb0,0x36,0x24,0x00,0xc0,0x36,0x22,0x00,0x68,0x60,0xd6,0x62,
-0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x6a,0x60,0x42,0x78,0xff,0xff,
-0x72,0x60,0xb8,0x78,0xff,0xff,0x71,0x60,0x5a,0x78,0xff,0xff,0x71,0x60,0x98,0x78,
-0xff,0xff,0x71,0x60,0x8b,0x78,0xff,0xff,0x71,0x60,0xa5,0x78,0xff,0xff,0x71,0x60,
-0xfd,0x78,0xff,0xff,0x71,0x60,0x14,0x78,0xff,0xff,0x71,0x60,0x37,0x78,0xff,0xff,
-0x72,0x60,0x1b,0x78,0xff,0xff,0x72,0x60,0x52,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,
-0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,
-0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,
-0xff,0xff,0x04,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,0xa2,0xdb,0x70,0x60,0xf3,0x78,
-0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,
-0xff,0xff,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,
-0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x5c,0x65,
-0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,
-0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,
-0x2c,0xf2,0x82,0xf1,0x15,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,0x11,0x02,0xd0,0x80,
-0xff,0xff,0x14,0x03,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,
-0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,
-0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x02,0x68,0x60,0xd8,0x62,
-0x01,0x64,0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd8,0x65,0x68,0x60,
-0xd6,0x63,0x00,0x64,0x01,0x61,0xa3,0xd1,0xa5,0xdb,0xd1,0x80,0xa3,0xdb,0x70,0x60,
-0xf3,0x78,0xff,0xff,0x68,0x60,0xd8,0x65,0x68,0x60,0xd6,0x63,0x00,0x64,0x01,0x61,
-0xa5,0xd1,0xa3,0xdb,0xd1,0x80,0xa5,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x46,0x4a,0x2b,0xf2,
-0x81,0xf1,0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x0c,0x02,0xd0,0x80,0x2d,0xf2,
-0x83,0xf1,0x08,0x02,0xd0,0x80,0xff,0xff,0x05,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x0c,0x00,0x2b,0xf0,0xff,0x60,0xff,0x64,0xd0,0x80,0x2c,0xf0,0x33,0x02,
-0xd0,0x80,0x2d,0xf0,0x30,0x02,0xd0,0x80,0xff,0xff,0x2d,0x02,0x38,0xf2,0xff,0xff,
-0xfe,0xa0,0xff,0xff,0x28,0x04,0x00,0xf4,0x02,0xf0,0x16,0x60,0x44,0x62,0xa2,0xd1,
-0x64,0x47,0xd0,0x80,0xff,0xff,0x1f,0x02,0x60,0x41,0xe9,0x81,0x06,0x63,0x0c,0x03,
-0x16,0x60,0x46,0x64,0x60,0x45,0xbd,0xd0,0xa5,0xd3,0xff,0xff,0xd0,0x80,0x65,0x44,
-0x12,0x02,0xcd,0x81,0x02,0xa4,0xf6,0x02,0x02,0xf0,0xff,0xff,0x64,0x40,0x01,0x27,
-0x02,0x00,0x48,0xfe,0x08,0x00,0xa3,0xd0,0xa0,0xd1,0x64,0x44,0x00,0x7f,0x60,0x45,
-0x64,0x44,0x00,0x7f,0xd4,0x80,0x2a,0x46,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2e,0xf2,
-0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x30,0xf2,
-0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x05,0x03,0x5c,0x65,0x73,0x60,0x58,0x4f,
-0xc2,0x78,0xff,0xff,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,
-0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,0xff,0xff,
-0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,0x03,0x02,
-0xd0,0x80,0xff,0xff,0x1a,0x03,0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,
-0x14,0x03,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,
-0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x5c,0x65,
-0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x00,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,
-0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x29,0xf2,0xff,0xff,0xff,0xff,0x01,0x2b,0x3a,0x00,
-0x2b,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,
-0x2d,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x40,0x03,0x68,0x60,0xdc,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x42,0x02,0x68,0x60,0xde,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0x40,0x47,0x1e,0x03,0x56,0x65,0x68,0x60,0xe0,0x61,0x65,0x43,
-0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,
-0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xff,0xff,0x1f,0x01,0x27,0x44,
-0xcc,0x84,0x40,0x47,0xec,0x02,0x29,0xf2,0xff,0xff,0xff,0xff,0x01,0x27,0x02,0x00,
-0x08,0xfe,0x1d,0x00,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,
-0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,
-0x5c,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x09,0x02,0x2b,0xf2,0xff,0xff,
-0xff,0xff,0x01,0x26,0x04,0x00,0x68,0x60,0xd6,0x62,0x01,0x64,0xa2,0xdb,0x70,0x60,
-0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,
-0xa3,0xdb,0x38,0xf2,0x46,0x4a,0x60,0x41,0x68,0x60,0xd4,0x62,0xa2,0xd3,0xff,0xff,
-0xff,0xff,0x02,0x36,0x07,0x00,0x68,0x60,0xd2,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,
-0x04,0x36,0x07,0x00,0x61,0x44,0xf2,0xa0,0xff,0xff,0x06,0x05,0x73,0x60,0xbe,0x78,
-0xff,0xff,0x73,0x60,0x15,0x78,0xff,0xff,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,
-0x2f,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,
-0xff,0xff,0xef,0x03,0x68,0x60,0xd4,0x62,0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,
-0xe5,0x02,0x68,0x60,0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0xde,0x02,
-0x68,0x60,0xde,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x40,0x47,0x22,0x03,0x5c,0x65,
-0x68,0x60,0xe0,0x61,0x65,0x43,0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,
-0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,
-0xff,0xff,0xc4,0x01,0x27,0x44,0xcc,0x84,0x40,0x47,0xec,0x02,0x0a,0x00,0x68,0x60,
-0xd4,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x02,0x3a,0x03,0x00,0x73,0x60,0xbe,0x78,
-0xff,0xff,0x38,0xf2,0x00,0xf4,0x08,0xf0,0xf2,0xa0,0xff,0xff,0x03,0x05,0x73,0x60,
-0xbe,0x78,0xff,0xff,0x68,0x60,0xd2,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x04,0x36,
-0x1b,0x00,0x16,0x60,0x44,0x62,0x64,0x47,0x00,0xa0,0xff,0xff,0x3c,0x06,0xe0,0xa0,
-0xff,0xff,0x39,0x07,0xa2,0xdb,0x12,0x63,0x60,0x41,0x16,0x60,0x46,0x64,0xbd,0xd0,
-0xa0,0xd9,0xcd,0x81,0x02,0xa4,0xfb,0x02,0x08,0xf0,0xff,0xff,0x64,0x40,0x01,0x2b,
-0x2a,0x00,0xa3,0xd0,0xa0,0xd9,0x27,0x00,0x16,0x60,0x44,0x62,0x64,0x47,0xa2,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x6c,0x02,0x60,0x41,0xe9,0x81,0x12,0x63,0x0c,0x03,
-0x16,0x60,0x46,0x64,0x60,0x45,0xbd,0xd0,0xa5,0xd3,0xff,0xff,0xd0,0x80,0x65,0x44,
-0x5f,0x02,0xcd,0x81,0x02,0xa4,0xf6,0x02,0x08,0xf0,0xff,0xff,0x64,0x40,0x01,0x2b,
-0x0a,0x00,0xa3,0xd0,0xa0,0xd1,0x64,0x44,0x00,0x7f,0x60,0x45,0x64,0x44,0x00,0x7f,
-0xd4,0x80,0xff,0xff,0x4d,0x02,0x08,0xf2,0x12,0x65,0x00,0x7e,0x60,0x47,0xc4,0x81,
-0x61,0x45,0x01,0x26,0x04,0x00,0xa1,0xd2,0xf4,0xe6,0x7e,0x00,0xda,0x01,0xff,0xff,
-0x60,0x47,0x02,0x00,0x01,0xa1,0xa1,0xd2,0xff,0xff,0x00,0x7f,0x02,0xa4,0xc4,0x81,
-0x02,0xa1,0x01,0x26,0x02,0x00,0xa1,0xd2,0x04,0x00,0xff,0xa1,0xa1,0xd2,0xff,0xff,
-0x60,0x47,0x7f,0xf1,0x00,0x7f,0xd0,0x80,0xff,0xff,0x2d,0x02,0x68,0x60,0xd4,0x62,
-0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,0x19,0x02,0x68,0x60,0xde,0x62,0xa2,0xd3,
-0xff,0xff,0xf6,0xa0,0x60,0x41,0x12,0x05,0x01,0xa4,0xa2,0xdb,0xe1,0x81,0xe1,0x85,
-0xc5,0x85,0x68,0x60,0xe0,0x64,0xc4,0x81,0x2a,0x46,0x5c,0x63,0xbd,0xd2,0xa1,0xdb,
-0xbd,0xd2,0x02,0xa1,0xa1,0xdb,0xa3,0xd2,0x02,0xa1,0xa1,0xdb,0x68,0x60,0xd4,0x62,
-0x02,0x64,0xa2,0xdb,0x2a,0x46,0xff,0xff,0x2e,0xf0,0x81,0xf9,0x2f,0xf0,0xff,0xff,
-0x82,0xf9,0x30,0xf0,0x83,0xf9,0x2a,0x46,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x1d,0x02,0x68,0x60,0xde,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0x40,0x47,0x16,0x04,0x68,0x60,0xe0,0x61,0x65,0x43,
-0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,
-0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xff,0xff,0x06,0x01,0x27,0x44,
-0xcc,0x84,0x40,0x47,0xec,0x02,0x08,0xfe,0x01,0x00,0x48,0xfe,0x2f,0x58,0xff,0xff,
-0x99,0xff,0x08,0x60,0x2a,0x62,0x05,0x60,0xff,0x64,0xa2,0xdb,0x05,0x60,0xff,0xe5,
-0xff,0xff,0xff,0xff,0x98,0xff,0xe0,0x60,0x00,0x63,0xfe,0x60,0x00,0x66,0x0c,0x60,
-0x7b,0x64,0xa3,0xd0,0xcc,0x84,0xbd,0xd8,0xfc,0x02,0x99,0xff,0x08,0x60,0x2a,0x62,
-0x04,0x60,0xff,0x64,0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x98,0xff,
-0x0c,0x60,0x87,0x78,0xff,0xff,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x01,0x00,0x21,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x04,0x00,
-0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,
-0x53,0x65,0x63,0x6f,0x6e,0x64,0x61,0x72,0x79,0x20,0x46,0x27,0x73,0x00,0x0f,0x01,
-0x00,0x18,0x7e,0x00,0xce,0xd0,0x0c,0x01,0x38,0x81,0x7f,0x00,0x02,0x00,0x0e,0x01,
-0xfa,0x8d,0x7f,0x00,0x02,0x00,0x10,0x01,0x00,0x80,0x7f,0x00,0xda,0x14,0x0a,0x01,
-0xc2,0x8d,0x7f,0x00,0x02,0x00,0x0b,0x01,0x0c,0x8e,0x7f,0x00,0x24,0x00,0x08,0x01,
-0xe8,0x8d,0x7f,0x00,0x12,0x00,0x09,0x01,0xe6,0x8d,0x7f,0x00,0x02,0x00,0x04,0x01,
-0x0a,0x81,0x7f,0x00,0x02,0x00,0x05,0x01,0x46,0x8d,0x7f,0x00,0x02,0x00,0x05,0x01,
-0x40,0x81,0x7f,0x00,0x02,0x00,0x02,0x00,0x00,0x80,0x7f,0x00,0xfa,0x0f,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x70,0x09,
-0x34,0x09,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x1b,0x00,0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x27,
-0xe0,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x01,0x00,0x64,0x00,0x64,0x00,
-0xe8,0x03,0x10,0x27,0x14,0x00,0x88,0x13,0x88,0x13,0x2f,0x00,0x14,0x00,0x04,0x00,
-0x0f,0x00,0x02,0x00,0x02,0x00,0x14,0x00,0x0a,0x00,0x0f,0x00,0x0f,0x00,0x05,0x00,
-0x0a,0x00,0x64,0x00,0x88,0x13,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
-0x05,0x00,0x08,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x09,
-0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x10,0x60,0xa3,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,0x10,0x60,0xa4,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc4,0xe2,0x10,0x60,0xab,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x10,0x60,0x85,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x08,0xe1,0x10,0x60,
-0xc6,0x78,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xe2,0x10,0x60,0xc8,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x10,0x60,0xc9,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x10,0x60,0xca,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0xd8,0x78,0x4c,0x4e,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0x9f,0x78,0x4c,0xe2,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0xcf,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0xab,0x78,0xa1,0xf3,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0xbc,0x78,0x46,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0x8e,0x78,0x47,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x42,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x43,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x1a,0x60,0xd4,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0xf7,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x83,0x7c,0x08,0x60,0x12,0x64,0x80,0x29,
-0xa0,0xd9,0x47,0xff,0x19,0x60,0x0c,0x78,0xff,0xff,0x40,0xff,0x24,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,0x21,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc4,0xe2,0x22,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x08,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x22,0x60,0x76,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x21,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1b,0x60,0x08,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x0b,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x60,0x8c,0x78,0x40,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf6,0x60,0xa0,0x78,0x41,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x25,0x60,0x2d,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x24,0x60,0xb2,0x78,0x45,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x25,0x60,0x75,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x3d,0x60,0x25,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x60,0x47,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x79,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x24,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3d,0x60,0xa1,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x84,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x47,0xff,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0x60,0x37,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x31,0x60,0x19,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x28,0xe2,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x87,0x7c,0x08,0x60,0x12,0x64,0x80,0x29,
-0xa0,0xd9,0x47,0xff,0x30,0x60,0x7f,0x78,0x93,0x6f,0xa6,0x6f,0xcc,0x3f,0xb3,0x50,
-0xe4,0x67,0xbf,0x64,0x4e,0x2d,0x53,0x56,0x38,0x68,0xd0,0x58,0xa4,0x6f,0xa4,0x6f,
-0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,
-0xa4,0x6f,0xa4,0x6f,0x8e,0x11,0x00,0x0a,0x10,0x01,0x68,0xa4,0xb0,0x01,0x84,0x01,
-0x30,0x33,0x31,0x33,0x44,0x44,0x30,0x33,0x31,0x33,0x30,0x33,0x31,0x33,0x32,0x33,
-0x32,0x33,0x90,0x00,0x78,0x04,0xae,0xe4,0xcc,0x3d,0xb5,0x3d,0xb0,0x33,0x46,0x34,
-0x03,0xfc,0xe7,0x34,0xe1,0x36,0xc6,0x17,0x02,0x00,0x04,0xfc,0xe7,0x34,0x13,0x35,
-0xe4,0x16,0x22,0x00,0x07,0xfc,0xe7,0x34,0x13,0x35,0x92,0x00,0x02,0x00,0x0e,0xfc,
-0xe7,0x34,0xfd,0x36,0x10,0x17,0x22,0x00,0x00,0xfc,0xe7,0x34,0x13,0x35,0xc0,0x16,
-0x02,0x00,0x01,0xfc,0xe7,0x34,0x04,0x35,0xb6,0x16,0x06,0x00,0x02,0xfc,0xe7,0x34,
-0x04,0x35,0xc2,0x16,0x22,0x00,0x05,0xfc,0xe7,0x34,0x13,0x35,0xbe,0x16,0x02,0x00,
-0x09,0xfc,0xe7,0x34,0x7e,0x37,0x06,0x17,0x02,0x00,0x0a,0xfc,0xe7,0x34,0x13,0x35,
-0x08,0x17,0x02,0x00,0x0b,0xfc,0xe7,0x34,0x13,0x35,0x0a,0x17,0x02,0x00,0x0c,0xfc,
-0xe7,0x34,0x13,0x35,0x0c,0x17,0x02,0x00,0x0f,0xfc,0xe7,0x34,0x13,0x35,0x5e,0x17,
-0x02,0x00,0x19,0xfd,0xe7,0x34,0x13,0x35,0x70,0x17,0x02,0x00,0xb0,0xfc,0xe7,0x34,
-0x13,0x35,0xbe,0x01,0x02,0x00,0x1a,0xfd,0xe7,0x34,0x13,0x35,0x3a,0x17,0x02,0x00,
-0x8e,0xfc,0xe7,0x34,0x13,0x35,0x44,0x17,0x02,0x00,0xa8,0xfc,0xe7,0x34,0x13,0x35,
-0x46,0x17,0x02,0x00,0xb3,0xfc,0xe7,0x34,0x13,0x35,0xdc,0x17,0x02,0x00,0xb4,0xfc,
-0xe7,0x34,0x30,0x37,0x92,0x01,0x02,0x00,0xa9,0xfc,0xe7,0x34,0xcf,0x34,0xd4,0x66,
-0x02,0x00,0xad,0xfc,0xe7,0x34,0x13,0x35,0x6a,0x00,0x02,0x00,0xaa,0xfc,0xe7,0x34,
-0x13,0x35,0xc2,0x01,0x02,0x00,0xab,0xfc,0xe7,0x34,0x13,0x35,0x76,0x67,0x02,0x00,
-0xac,0xfc,0xe7,0x34,0x13,0x35,0xf6,0x18,0x02,0x00,0xaf,0xfc,0xcf,0x34,0x39,0x39,
-0x00,0x00,0x02,0x00,0x0d,0xfc,0xe7,0x34,0x13,0x35,0x0e,0x17,0x02,0x00,0x19,0xfc,
-0xe7,0x34,0x13,0x35,0xa6,0x01,0x02,0x00,0x20,0xfc,0xe7,0x34,0x13,0x35,0xb4,0x61,
-0x06,0x00,0x21,0xfc,0xe7,0x34,0x00,0x35,0xbc,0x61,0x06,0x00,0x24,0xfc,0x13,0x35,
-0xaf,0x37,0x00,0x00,0x0e,0x00,0x25,0xfc,0x13,0x35,0xb3,0x37,0x00,0x00,0x0e,0x00,
-0x26,0xfc,0x13,0x35,0xb7,0x37,0x00,0x00,0x0e,0x00,0x27,0xfc,0x13,0x35,0xbb,0x37,
-0x00,0x00,0x0e,0x00,0x23,0xfc,0xe7,0x34,0x13,0x35,0x84,0x00,0x02,0x00,0x28,0xfc,
-0xd7,0x37,0xe3,0x37,0x00,0x00,0x06,0x00,0x2a,0xfc,0xe7,0x34,0x13,0x35,0xfa,0x00,
-0x02,0x00,0x2b,0xfc,0xe7,0x34,0xe3,0x34,0x78,0x01,0x02,0x00,0x2c,0xfc,0xe7,0x34,
-0x11,0x38,0xd8,0x66,0x02,0x00,0x2d,0xfc,0xe7,0x34,0x02,0x38,0xaa,0x01,0x02,0x00,
-0x2e,0xfc,0xe7,0x34,0x13,0x35,0xa8,0x66,0x02,0x00,0x3a,0xfc,0xe7,0x34,0x13,0x35,
-0x06,0x67,0x04,0x00,0x80,0xfc,0x29,0x35,0x3c,0x35,0x5e,0x58,0xc0,0x00,0x81,0xfc,
-0xe7,0x34,0x13,0x35,0x8c,0x01,0x02,0x00,0x82,0xfc,0xe7,0x34,0x13,0x35,0x8e,0x01,
-0x02,0x00,0x83,0xfc,0xe7,0x34,0x13,0x35,0x90,0x01,0x02,0x00,0x84,0xfc,0xe7,0x34,
-0x30,0x37,0x92,0x01,0x02,0x00,0x85,0xfc,0xe7,0x34,0x1c,0x37,0x1e,0x59,0x02,0x00,
-0x87,0xfc,0xe7,0x34,0x13,0x35,0x7c,0x17,0x02,0x00,0x88,0xfc,0xe7,0x34,0x13,0x35,
-0x7a,0x17,0x02,0x00,0x89,0xfc,0xe7,0x34,0x13,0x35,0x34,0x17,0x02,0x00,0x8a,0xfc,
-0xe7,0x34,0x13,0x35,0xdc,0x17,0x02,0x00,0x8b,0xfc,0xe7,0x34,0x13,0x35,0x00,0x01,
-0x02,0x00,0x8c,0xfc,0xe7,0x34,0x2d,0x38,0x7e,0x17,0x02,0x00,0xa5,0xfc,0xe7,0x34,
-0x8a,0x37,0x74,0x17,0x02,0x00,0xa6,0xfc,0xcf,0x34,0x72,0x37,0x00,0x00,0x02,0x00,
-0x18,0xfc,0xe7,0x34,0x13,0x35,0xbc,0x16,0x02,0x00,0xae,0xfc,0xe7,0x34,0x13,0x35,
-0xbc,0x01,0x02,0x00,0x2f,0xfc,0xe7,0x34,0x13,0x35,0xac,0x01,0x02,0x00,0x30,0xfc,
-0xcf,0x34,0x4b,0x38,0x00,0x00,0x14,0x00,0x31,0xfc,0xcf,0x34,0xae,0x38,0x00,0x00,
-0x06,0x00,0x10,0xfd,0xe7,0x34,0xe3,0x34,0x4a,0x01,0x02,0x00,0x11,0xfd,0x81,0x36,
-0xe3,0x34,0x4c,0xe8,0x0c,0x00,0x12,0xfd,0x81,0x36,0xe3,0x34,0x5a,0xe8,0x02,0x00,
-0x13,0xfd,0x41,0x36,0xe3,0x34,0x00,0x04,0xe0,0x01,0x14,0xfd,0xe7,0x34,0xe3,0x34,
-0x38,0x17,0x02,0x00,0x15,0xfd,0xe7,0x34,0xe3,0x34,0x82,0x17,0x24,0x00,0x16,0xfd,
-0xe7,0x34,0xe3,0x34,0x60,0x17,0x10,0x00,0x17,0xfd,0xe7,0x34,0xe3,0x34,0x5c,0x17,
-0x02,0x00,0x18,0xfd,0xe7,0x34,0xe3,0x34,0x76,0x17,0x04,0x00,0x1b,0xfd,0xe7,0x34,
-0xe3,0x34,0x72,0x17,0x02,0x00,0x1c,0xfd,0xe7,0x34,0xe3,0x34,0x3e,0x00,0x02,0x00,
-0x24,0xfd,0xe7,0x34,0xe3,0x34,0xaa,0x17,0x0c,0x00,0x25,0xfd,0xe7,0x34,0xe3,0x34,
-0xb8,0x17,0x0c,0x00,0x26,0xfd,0x81,0x36,0xe3,0x34,0x74,0xe8,0x20,0x00,0x27,0xfd,
-0x81,0x36,0xe3,0x34,0x94,0xe8,0x38,0x00,0x45,0xfd,0xe7,0x34,0xe3,0x34,0x00,0x01,
-0x02,0x00,0x47,0xfd,0xe7,0x34,0xe3,0x34,0x4e,0x01,0x02,0x00,0x48,0xfd,0xa2,0x36,
-0xe3,0x34,0x6c,0x01,0x02,0x00,0x49,0xfd,0xa2,0x36,0xe3,0x34,0x6e,0x01,0x02,0x00,
-0x4a,0xfd,0xe7,0x34,0xe3,0x34,0xe0,0x17,0x02,0x00,0x4b,0xfd,0xe7,0x34,0xe3,0x34,
-0xe2,0x17,0x02,0x00,0x4d,0xfd,0x81,0x36,0xe3,0x34,0x5c,0xe8,0x04,0x00,0x50,0xfd,
-0xe7,0x34,0xe3,0x34,0xb2,0x59,0x12,0x00,0x4f,0xfd,0x81,0x36,0xe3,0x34,0x58,0xe8,
-0x02,0x00,0xc0,0xfd,0x81,0x36,0xe3,0x34,0x60,0xe8,0x02,0x00,0xc1,0xfd,0xe7,0x34,
-0xe3,0x34,0xfe,0x00,0x02,0x00,0xc2,0xfd,0xb0,0x36,0xe3,0x34,0x00,0x00,0x02,0x00,
-0xc3,0xfd,0x81,0x36,0xe3,0x34,0x62,0xe8,0x02,0x00,0xc6,0xfd,0xe7,0x34,0xe3,0x34,
-0xd2,0x17,0x0a,0x00,0x20,0xfd,0x68,0x36,0xe3,0x34,0x3a,0xe8,0x08,0x00,0x21,0xfd,
-0x68,0x36,0xe3,0x34,0x42,0xe8,0x0a,0x00,0x22,0xfd,0x68,0x36,0xe3,0x34,0x1c,0xe8,
-0x0a,0x00,0x23,0xfd,0x68,0x36,0xe3,0x34,0x30,0xe8,0x0a,0x00,0x40,0xfd,0xe7,0x34,
-0xe3,0x34,0x96,0x01,0x02,0x00,0x41,0xfd,0xe7,0x34,0xe3,0x34,0x20,0x59,0x22,0x00,
-0x42,0xfd,0xe7,0x34,0x0e,0x35,0x02,0x01,0x06,0x00,0x43,0xfd,0xcb,0x36,0xe3,0x34,
-0x00,0x00,0x06,0x00,0x44,0xfd,0xc0,0x36,0xe3,0x34,0xa0,0x00,0x02,0x00,0x46,0xfd,
-0xe7,0x34,0xe3,0x34,0x84,0x01,0x0c,0x00,0x4c,0xfd,0xe7,0x34,0xe3,0x34,0xca,0x18,
-0x02,0x00,0x8d,0xfc,0xe7,0x34,0xe3,0x34,0x8e,0x00,0x02,0x00,0x8f,0xfc,0xe7,0x34,
-0xe3,0x34,0x80,0x17,0x02,0x00,0xa7,0xfc,0xe7,0x34,0xe3,0x34,0xc6,0x66,0x04,0x00,
-0xfe,0xff,0x81,0x36,0xe3,0x34,0x64,0xe8,0x02,0x00,0xff,0xff,0x81,0x36,0xe3,0x34,
-0x66,0xe8,0x0e,0x00,0x00,0xf1,0x2a,0x00,0xc3,0x35,0x01,0xf1,0x80,0x08,0x92,0x35,
-0x02,0xf1,0x80,0x08,0xca,0x35,0x03,0xf1,0x96,0x00,0x03,0x39,0x04,0xf1,0x90,0x1a,
-0x05,0x36,0xd8,0x1a,0x91,0x19,0x96,0x19,0x5e,0x1a,0xb3,0x1a,0x0e,0x1a,0x6d,0x19,
-0x16,0x1a,0x1e,0x1a,0xdb,0x19,0x59,0x19,0xca,0x1a,0xca,0x1a,0x30,0x24,0x5c,0x24,
-0x59,0x24,0x7a,0x23,0x5c,0x24,0x5c,0x24,0x00,0x00,0x62,0x24,0x62,0x24,0x62,0x24,
-0x00,0x00,0x00,0x00,0xec,0x24,0x05,0x25,0x7b,0x23,0x00,0x00,0x77,0x24,0x00,0x00,
-0x00,0x00,0x9b,0x16,0x00,0x02,0x48,0x04,0x48,0x06,0x80,0x08,0x03,0x0a,0x04,0x0c,
-0x04,0x0e,0x00,0x10,0xe4,0x12,0xbb,0x14,0x1b,0x16,0x00,0x18,0x00,0x1a,0x00,0x1c,
-0x5c,0x1e,0xc1,0x20,0x20,0x22,0x74,0x24,0x07,0x26,0x0a,0x28,0x16,0x2a,0x00,0x2c,
-0x00,0x2e,0x1c,0x30,0x20,0x32,0x98,0x34,0x08,0x36,0x7a,0x38,0x0a,0x3a,0x24,0x3c,
-0xb2,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x0c,0x46,0x26,0x48,0x5b,0x4a,0x7f,0x4c,
-0x29,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,0x10,0x5a,0x10,0x5c,
-0x1e,0x5e,0x1a,0x60,0x18,0x62,0x00,0x2c,0x0c,0x2e,0x01,0x2c,0x10,0x2e,0x02,0x2c,
-0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,0x05,0x2c,0x20,0x2e,0x06,0x2c,
-0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,0x09,0x2c,0x34,0x2e,0x0a,0x2c,
-0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,0x0d,0x2c,0x43,0x2e,0x0e,0x2c,
-0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,0x11,0x2c,0x50,0x2e,0x12,0x2c,
-0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,0x15,0x2c,0x6d,0x2e,0x16,0x2c,
-0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,0x19,0x2c,0x7f,0x2e,0x1a,0x2c,
-0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,0x1d,0x2c,0x7f,0x2e,0x1e,0x2c,
-0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0x00,0x02,0x01,0x04,0x38,0x06,0x80,0x08,0x03,0x0a,
-0x04,0x0c,0x04,0x0e,0x00,0x10,0xa2,0x12,0xc8,0x14,0x1b,0x16,0x00,0x18,0x00,0x1a,
-0x00,0x1c,0x5c,0x1e,0xc1,0x20,0x1e,0x22,0x54,0x24,0x07,0x26,0x6a,0x28,0x12,0x2a,
-0x00,0x2c,0x00,0x2e,0x1c,0x30,0x20,0x32,0x82,0x34,0x08,0x36,0x7a,0x38,0xca,0x3a,
-0x24,0x3c,0xb6,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x7f,0x46,0x8b,0x48,0x0f,0x4a,
-0x06,0x4c,0x0a,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,0x20,0x5a,
-0xee,0x5c,0x1a,0x5e,0x26,0x60,0x5b,0x62,0x00,0x04,0x00,0x2c,0x0c,0x2e,0x01,0x2c,
-0x10,0x2e,0x02,0x2c,0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,0x05,0x2c,
-0x20,0x2e,0x06,0x2c,0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,0x09,0x2c,
-0x34,0x2e,0x0a,0x2c,0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,0x0d,0x2c,
-0x43,0x2e,0x0e,0x2c,0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,0x11,0x2c,
-0x50,0x2e,0x12,0x2c,0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,0x15,0x2c,
-0x6d,0x2e,0x16,0x2c,0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,0x19,0x2c,
-0x7f,0x2e,0x1a,0x2c,0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,0x1d,0x2c,
-0x7f,0x2e,0x1e,0x2c,0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0x00,0x00,0x04,0x00,0x65,0x00,
-0x00,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,0x5c,0x00,
-0x32,0x00,0x00,0x00,0x04,0x00,0x65,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x5a,0x00,0x00,0x00,0x7e,0x00,0x6e,0x00,
-0x00,0x00,0x80,0x00,0x02,0x00,0x00,0x00,0x80,0x00,0x16,0x00,0x00,0x00,0x80,0x00,
-0x2a,0x00,0x00,0x00,0x80,0x00,0x3e,0x00,0x00,0x00,0x80,0x00,0x52,0x00,0x00,0x00,
-0x80,0x00,0x66,0x00,0x00,0x00,0x80,0x00,0x7a,0x00,0x00,0x00,0x82,0x00,0x0e,0x00,
-0x00,0x00,0x82,0x00,0x22,0x00,0x00,0x00,0x82,0x00,0x36,0x00,0x00,0x00,0x82,0x00,
-0x4a,0x00,0x00,0x00,0x82,0x00,0x7a,0x00,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x1b,0x3b,0x10,0x3b,0x10,0x3b,0x30,0x3b,
-0x10,0x3b,0x10,0x3b,0x63,0x3b,0x71,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0xd5,0x3b,0x24,0x3c,0x49,0x3c,0x42,0x3c,
-0x2e,0x3c,0x38,0x3c,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x4b,0x3c,0x61,0x3c,0x68,0x3c,
-0x12,0x00,0x02,0x00,0x72,0x0e,0x04,0x00,0x8b,0x0e,0x06,0x00,0x39,0x0f,0x08,0x00,
-0x50,0x0f,0x0a,0x00,0x73,0x0f,0x0c,0x00,0x2e,0x10,0x12,0x00,0x3f,0x18,0x1a,0x00,
-0x75,0x25,0x00,0x09,0x16,0x0c,0x02,0x09,0x22,0x33,0x04,0x09,0x2f,0x34,0x06,0x09,
-0x72,0x34,0x14,0x09,0x34,0x31,0x16,0x09,0x50,0x31,0x42,0x09,0xb0,0x34,0x22,0x09,
-0x82,0x34,0x64,0x09,0x7a,0x34,0x70,0x09,0x02,0x3b,0x03,0x00,0x00,0x00,0x53,0x70,
-0x65,0x63,0x74,0x72,0x75,0x6d,0x32,0x34,0x2f,0x48,0x44,0x52,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x14,0x00,
-0x6e,0x6f,0x6e,0x2d,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x53,0x53,
-0x49,0x44,0x20,0x21,0x21,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x00,0x00,0x01,0x00,0x01,0x00,0x64,0x00,0x64,0x00,0x00,0x00,0x50,0x72,0x69,0x73,
-0x6d,0x20,0x20,0x49,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x01,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x53,0x70,0x65,0x63,
-0x74,0x72,0x75,0x6d,0x32,0x34,0x2f,0x48,0x44,0x52,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x14,0x00,0x6e,0x6f,
-0x6e,0x2d,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x53,0x53,0x49,0x44,
-0x20,0x21,0x21,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,
-0x01,0x00,0x01,0x00,0x64,0x00,0x64,0x00,0x00,0x00,0x50,0x72,0x69,0x73,0x6d,0x20,
-0x20,0x49,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x80,0x01,0x2c,0x00,
-0x01,0x00,0xb6,0x00,0xc0,0x00,0xd4,0x00,0xee,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,
-0x12,0x00,0x0f,0x00,0x0c,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x01,0x00,
-0x03,0x00,0x03,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x2a,0x1b,
-0x20,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x20,0x20,0x53,0x74,0x61,0x6e,
-0x64,0x61,0x72,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x00,0x00,0x00,0x00,0x00,0x00,
-0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x00,0x00,0x00,0x00,0x01,0x00,
-0x04,0x00,0x82,0x84,0x8b,0x96,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x04,0x0b,0x16,
-0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x60,0x1f,0xe0,0x27,0x3f,0x3f,0x49,0x6e,0x74,0x27,0x6c,0x2d,
-0x73,0x65,0x61,0x72,0x63,0x68,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x00,
-0x04,0x00,0xfa,0x00,0xc8,0x00,0xf4,0x01,0x05,0x00,0x07,0x00,0xfa,0x00,0xc8,0x00,
-0x2c,0x01,0x05,0x00,0x0a,0x00,0xfa,0x00,0x32,0x00,0x64,0x00,0x05,0x00,0x0a,0x00,
-0xfa,0x00,0x32,0x00,0x32,0x00,0x05,0x00,0x0a,0x00,0xfa,0x00,0x19,0x00,0x19,0x00,
-0x05,0x00,0x35,0x44,0x5d,0x65,0xf7,0x98,0xd9,0x6e,0x42,0xf7,0xb8,0x74,0xdc,0x15,
-0xfc,0xf8,0x82,0x05,0x79,0xf3,0x09,0x59,0x97,0x30,0xee,0x53,0x7b,0x83,0x15,0x46,
-0xbd,0x2c,0xd1,0x91,0x43,0x08,0x1e,0x7f,0xc9,0x60,0x53,0x68,0xf4,0xd2,0x88,0x06,
-0x22,0x14,0x39,0x09,0xf5,0x9f,0x05,0x27,0x74,0xfe,0xec,0x75,0x1f,0x42,0xf2,0x0c,
-0xae,0xb0,0x1b,0xfd,0xb5,0x76,0x03,0x39,0xfa,0x8f,0x7f,0x00,0xe0,0x04,0x35,0x2a,
-0x7f,0x42,0x2f,0x45,0xd9,0xd4,0x49,0xe6,0xf8,0xd9,0x86,0xee,0x78,0x42,0xca,0x54,
-0x39,0x10,0x17,0x89,0x15,0xfc,0xcd,0x61,0x9c,0x76,0xdc,0xbe,0x02,0x0a,0x46,0xb6,
-0xea,0xad,0x47,0xaa,0x89,0x11,0x97,0xf1,0x0b,0xec,0x22,0x6d,0xf3,0x33,0xd3,0xef,
-0x02,0x6f,0x58,0xb8,0x73,0x50,0x2a,0x8b,0xe8,0x3a,0x53,0xa8,0xe9,0x09,0xbf,0xbc,
-0x57,0x47,0x83,0xdb,0x5e,0xb1,0x62,0x82,0x5c,0xb2,0x71,0x5f,0x23,0x67,0xfd,0xcb,
-0xe0,0xd1,0x0d,0xe8,0xab,0x44,0x33,0x0f,0x4c,0x76,0x92,0x32,0x65,0xb1,0x7e,0xca,
-0xed,0x21,0x5e,0x45,0xba,0x92,0xa4,0x67,0x67,0x9e,0x23,0x33,0x8e,0x6a,0xa1,0xe9,
-0x94,0x3a,0x39,0xef,0x34,0xb3,0x65,0x96,0x3b,0x6e,0x46,0xbd,0xd0,0xc3,0x22,0x87,
-0x54,0xad,0xbc,0x89,0xd6,0x3a,0x3a,0x99,0xe0,0x89,0x0e,0xcf,0xfe,0xa3,0x0a,0x1a,
-0x68,0x6f,0xcb,0xd7,0xa6,0x25,0xd1,0x25,0x4d,0xc0,0x86,0xd8,0x9b,0xf7,0xe2,0xd5,
-0xf7,0xa3,0x0c,0x33,0xe3,0x83,0x87,0xff,0x4c,0x9d,0xd6,0xd6,0x89,0x9a,0x0c,0x38,
-0xa0,0xe8,0xef,0x52,0x1b,0x0e,0xbb,0x52,0xbc,0x9f,0xde,0xbf,0x1a,0xc8,0xf0,0xd0,
-0xd9,0x54,0xc2,0x6c,0x53,0xa8,0x1b,0xc4,0x17,0x42,0x1d,0x51,0x2e,0xc8,0x8e,0xe7,
-0x63,0x5c,0x00,0xd6,0xc5,0x0d,0xcd,0xeb,0xab,0x59,0x13,0xf3,0x02,0x82,0x0c,0x2c,
-0x36,0x3c,0xe7,0x21,0xb8,0xf9,0x57,0x67,0xdd,0xae,0x1d,0x6e,0x39,0x06,0xe2,0xd2,
-0x16,0x1f,0x8e,0x2c,0xec,0x5f,0x71,0xf1,0x01,0x8b,0x6b,0x52,0x72,0x04,0x49,0xa8,
-0x8b,0xa7,0x27,0xe3,0x3c,0xfa,0x55,0x45,0x94,0x54,0xcc,0x68,0xc9,0xf9,0x35,0xa8,
-0xa5,0x8c,0xa2,0x5c,0xb8,0x7f,0x24,0xe5,0x6e,0x15,0xe4,0xdd,0x97,0xf2,0xe8,0x84,
-0xf9,0xcf,0x92,0x98,0x70,0x25,0x24,0xf3,0x6a,0x39,0x15,0x11,0xa0,0x40,0xa2,0x99,
-0x58,0x7a,0xa2,0x9b,0x2a,0xb2,0x74,0xa4,0x7d,0x2d,0x7e,0x5e,0x0e,0xf5,0x59,0x37,
-0xf6,0x50,0x9f,0x06,0xf3,0x0d,0x03,0x87,0xcc,0x18,0xb5,0xca,0x44,0xf1,0x57,0x55,
-0xe2,0x5f,0x8c,0x82,0x6c,0x1e,0xb2,0x83,0x40,0x0f,0xef,0x6d,0x61,0x34,0x9d,0x8f,
-0x68,0x59,0xcb,0x22,0x9b,0x29,0x55,0x1a,0x87,0x3f,0x64,0x22,0x4a,0xbd,0x6c,0x7c,
-0x0e,0xc3,0x89,0x5d,0x2a,0x86,0xa2,0xb6,0xf5,0xa3,0xd8,0x36,0x90,0x08,0x95,0xee,
-0xca,0xb4,0x95,0xcc,0xa1,0x8b,0x98,0xc7,0x48,0xfd,0xa7,0xce,0xd6,0xc5,0x3b,0x7a,
-0x2e,0x3a,0x0e,0xf8,0x11,0x63,0xef,0xb5,0xd1,0x10,0xed,0x67,0xb7,0x25,0x26,0x23,
-0x2d,0xdb,0x8b,0xf8,0x50,0x8d,0xd1,0x2c,0x32,0x14,0xe5,0x92,0xd8,0x88,0x31,0x82,
-0x7c,0xa1,0x6e,0x59,0xf1,0x54,0xb7,0x6f,0xfa,0x00,0xe4,0x59,0xe8,0x59,0xec,0x59,
-0xf0,0x59,0xf4,0x59,0x00,0x5a,0x04,0x5a,0x08,0x5a,0x14,0x5a,0x18,0x5a,0x3e,0x5a,
-0x42,0x5a,0x46,0x5a,0x4a,0x5a,0x4e,0x5a,0x5a,0xda,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x5c,0xda,0x5e,0x5a,
-0x3e,0xe7,0x3e,0xe7,0x62,0x5a,0x66,0x5a,0x3e,0x67,0x70,0x5a,0x74,0x5a,0x78,0x5a,
-0x7c,0x5a,0x88,0x5a,0x8c,0x5a,0x90,0x5a,0x9c,0x5a,0xa0,0x5a,0xca,0x5a,0xce,0x5a,
-0xd2,0x5a,0xd6,0x5a,0xda,0x5a,0xe6,0x5a,0x3e,0x67,0x3e,0xe7,0xfa,0x5a,0xfe,0xda,
-0x00,0xdb,0x02,0xdb,0x04,0xdb,0x06,0x5b,0x3e,0xe7,0x0a,0x5b,0x0e,0xdb,0x10,0xdb,
-0x12,0xdb,0x14,0xdb,0x16,0xdb,0x18,0xdb,0x3e,0xe7,0x3e,0xe7,0x1a,0xdb,0x1c,0xdb,
-0x1e,0xdb,0x20,0xdb,0x24,0x5b,0x28,0x5b,0x2c,0x5b,0x30,0x5b,0x34,0xdb,0x36,0xdb,
-0x8e,0x59,0x92,0x59,0x9c,0xd9,0x9e,0xd9,0x3e,0xe7,0x88,0xd9,0x8a,0xd9,0x8c,0xd9,
-0xa6,0xd9,0xa8,0xd9,0xaa,0xd9,0xb4,0xd9,0xb6,0xd9,0xbe,0xd9,0xa0,0xd9,0xa2,0xd9,
-0xa4,0xd9,0xc0,0xd9,0xc2,0xd9,0xc4,0xd9,0xc6,0xd9,0xc8,0xd9,0xca,0xd9,0xd0,0xd9,
-0xd2,0xd9,0xd4,0xd9,0xd6,0xd9,0xd8,0xd9,0xda,0xd9,0xdc,0xd9,0xde,0xd9,0xe0,0xd9,
-0xe2,0xd9,0x6c,0xd9,0x6e,0xd9,0x46,0xd9,0x4a,0xd9,0x4c,0xd9,0x4e,0xd9,0x50,0xd9,
-0x3e,0xe7,0x38,0x97,0x3e,0xe7,0x08,0x81,0x50,0x9e,0x5a,0xd9,0x5c,0xd9,0x3e,0xe7,
-0x5e,0xd9,0x70,0xd9,0x72,0xd9,0x3e,0xe7,0x78,0xd9,0x7a,0xd9,0x7c,0xd9,0x7e,0xd9,
-0x80,0xd9,0x82,0xd9,0x84,0xd9,0x86,0xd9,0x3a,0x5a,0x1c,0xda,0x1e,0xda,0x20,0xda,
-0x22,0xda,0x24,0xda,0x26,0x5a,0x2a,0x5a,0x2e,0x5a,0x32,0x5a,0x36,0x5a,0xc6,0x5a,
-0xa8,0xda,0xaa,0xda,0xac,0xda,0xae,0xda,0xb0,0xda,0xb2,0x5a,0xb6,0x5a,0xba,0x5a,
-0xbe,0x5a,0xc2,0x5a,0xea,0x5a,0xee,0x5a,0xac,0xd9,0xae,0xd9,0xcc,0xd9,0xce,0xd9,
-0x3e,0x67,0x42,0xd9,0x44,0xd9,0x38,0xdf,0x3e,0xe7,0x48,0xd9,0xa0,0x80,0x3e,0xe7,
-0x3e,0xe7,0x60,0xd9,0x6a,0xda,0x3e,0xe7,0xb0,0xd9,0x3a,0xdf,0x64,0xd9,0x22,0xdb,
-0x62,0xd9,0x66,0xd9,0x6c,0x5a,0x90,0x81,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0xbe,0xe6,
-0xbc,0xe6,0xb8,0xd9,0xba,0xd9,0xce,0x98,0x5c,0xe4,0x60,0xe4,0x54,0xd9,0x56,0xd9,
-0xf8,0x59,0xfc,0x59,0x0c,0x5a,0x10,0x5a,0x52,0x5a,0x56,0x5a,0x80,0x5a,0x84,0x5a,
-0x94,0x5a,0x98,0x5a,0xde,0x5a,0xe2,0x5a,0xf2,0x5a,0xf6,0x5a,0x92,0x1e,0x68,0x59,
-0x6a,0x59,0xc2,0xe1,0xc6,0xe1,0xfa,0x81,0x2c,0xbc,0x32,0xbc,0x38,0xbc,0x44,0xbc,
-0x4a,0xbc,0x68,0xbc,0x6e,0xbc,0xcc,0x98,0xb0,0x80,0xb2,0x80,0xb4,0x80,0x11,0x00,
-0x00,0x00,0x00,0x00,0xb6,0x16,0x06,0x00,0x01,0x00,0xb4,0x61,0x06,0x00,0x01,0x00,
-0xbc,0x61,0x06,0x00,0x01,0x00,0x60,0x17,0x10,0x00,0x01,0x00,0x82,0x17,0x24,0x00,
-0x01,0x00,0xaa,0x17,0x0e,0x00,0x01,0x00,0xc4,0x16,0x20,0x00,0x01,0x00,0x50,0x5b,
-0xe8,0x03,0x14,0x00,0x3c,0x5f,0x78,0x00,0x14,0x00,0x62,0x64,0xc0,0x01,0x10,0x00,
-0xd8,0x61,0x80,0x02,0x10,0x00,0x2a,0x67,0x38,0x00,0x07,0x00,0xa6,0x59,0x99,0x01,
-0x01,0x00,0xaa,0x17,0x0e,0x00,0x01,0x00,0xb8,0x17,0x0e,0x00,0x01,0x00,0x02,0x01,
-0x06,0x00,0x01,0x00,0x16,0x67,0x00,0x00,0x00,0x00,0x34,0x12,0xaa,0x55,0xe8,0x59,
-0x02,0x00,0xec,0x59,0x02,0x00,0xf0,0x59,0x02,0x00,0xf4,0x59,0x02,0x00,0x00,0x5a,
-0x02,0x00,0x04,0x5a,0x02,0x00,0x08,0x5a,0x02,0x00,0x42,0x5a,0x02,0x00,0x5a,0x5a,
-0x01,0x00,0x70,0x5a,0x02,0x00,0x74,0x5a,0x02,0x00,0x78,0x5a,0x02,0x00,0x7c,0x5a,
-0x02,0x00,0x88,0x5a,0x02,0x00,0x8c,0x5a,0x02,0x00,0x90,0x5a,0x02,0x00,0x9c,0x5a,
-0x02,0x00,0xce,0x5a,0x02,0x00,0x9c,0x59,0x01,0x00,0x9e,0x59,0x01,0x00,0x3e,0x67,
-0x01,0x00,0x8a,0x59,0x01,0x00,0xa6,0x59,0x01,0x00,0xb4,0x59,0x01,0x00,0xb6,0x59,
-0x01,0x00,0xc0,0x59,0x01,0x00,0xc2,0x59,0x01,0x00,0xc4,0x59,0x01,0x00,0xc6,0x59,
-0x01,0x00,0xc8,0x59,0x01,0x00,0xca,0x59,0x01,0x00,0x6c,0x59,0x01,0x00,0x46,0x59,
-0x01,0x00,0x3e,0x67,0x01,0x00,0x38,0x17,0x01,0x00,0xfe,0x00,0x01,0x00,0xa0,0x00,
-0x01,0x00,0xf8,0x59,0x02,0x00,0xfc,0x59,0x02,0x00,0x0c,0x5a,0x02,0x00,0x10,0x5a,
-0x02,0x00,0x80,0x5a,0x02,0x00,0x84,0x5a,0x02,0x00,0x94,0x5a,0x02,0x00,0x98,0x5a,
-0x02,0x00,0xcc,0x18,0x01,0x00,0xb0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
-0xff,0xff,0x66,0xe8,0x7e,0x00,0x0e,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x40,0x8d,
-0x7f,0x00,0x06,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xe8,0x8d,0x7f,0x00,0x12,0x00,
-0x00,0x00,0x09,0x01,0x00,0x00,0xe6,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0a,0x01,
-0x00,0x00,0xc2,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0b,0x01,0x00,0x00,0x0c,0x8e,
-0x7f,0x00,0x24,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0x4c,0xe8,0x7e,0x00,0x0c,0x00,
-0x00,0x00,0x04,0x01,0x00,0x00,0x0a,0x81,0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,
-0x00,0x00,0x46,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0x40,0x81,
-0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0x50,0x8e,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x06,0x01,0x00,0x00,0x58,0xe8,0x7e,0x00,0x02,0x00,0x00,0x00,0x07,0x01,
-0x00,0x00,0x5a,0xe8,0x7e,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x26,0xe8,
-0x7e,0x00,0x0a,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xbe,0x8d,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x0a,0x00,0x00,0x00,0xc0,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0c,0x01,
-0x00,0x00,0x38,0x81,0x7f,0x00,0x02,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0xc4,0x8d,
-0x7f,0x00,0x02,0x00,0x00,0x00,0x0e,0x01,0x00,0x00,0xfa,0x8d,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x11,0x01,0x00,0x00,0xfc,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x00,0x18,
-0x7e,0x00,0xce,0xd0,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x01,0x00,0x21,0x00,
-0x02,0x00,0x02,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x02,0x00,
-0x01,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x5a,0xd7
-};
-
-#endif /* _FIRMWARE_WI_SPECTRUM24T_CF_H_ */
diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c
index 7df317b..f801493 100644
--- a/sys/dev/wpi/if_wpi.c
+++ b/sys/dev/wpi/if_wpi.c
@@ -152,6 +152,11 @@ static const struct wpi_ident wpi_ident_table[] = {
{ 0, 0, 0, NULL }
};
+static struct ieee80211vap *wpi_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void wpi_vap_delete(struct ieee80211vap *);
static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *,
void **, bus_size_t, bus_size_t, int);
static void wpi_dma_contig_free(struct wpi_dma_info *);
@@ -166,8 +171,7 @@ static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *,
static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static struct ieee80211_node *wpi_node_alloc(struct ieee80211_node_table *);
-static int wpi_media_change(struct ifnet *);
-static int wpi_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void wpi_mem_lock(struct wpi_softc *);
static void wpi_mem_unlock(struct wpi_softc *);
static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t);
@@ -193,11 +197,14 @@ static void wpi_watchdog(void *);
static int wpi_tx_data(struct wpi_softc *, struct mbuf *,
struct ieee80211_node *, int);
static void wpi_start(struct ifnet *);
+static void wpi_start_locked(struct ifnet *);
+static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void wpi_scan_start(struct ieee80211com *);
static void wpi_scan_end(struct ieee80211com *);
static void wpi_set_channel(struct ieee80211com *);
-static void wpi_scan_curchan(struct ieee80211com *, unsigned long);
-static void wpi_scan_mindwell(struct ieee80211com *);
+static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void wpi_scan_mindwell(struct ieee80211_scan_state *);
static int wpi_ioctl(struct ifnet *, u_long, caddr_t);
static void wpi_read_eeprom(struct wpi_softc *);
static void wpi_read_eeprom_channels(struct wpi_softc *, int);
@@ -210,8 +217,8 @@ static void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *);
#if 0
static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *);
#endif
-static int wpi_auth(struct wpi_softc *);
-static int wpi_run(struct wpi_softc *);
+static int wpi_auth(struct wpi_softc *, struct ieee80211vap *);
+static int wpi_run(struct wpi_softc *, struct ieee80211vap *);
static int wpi_scan(struct wpi_softc *);
static int wpi_config(struct wpi_softc *);
static void wpi_stop_master(struct wpi_softc *);
@@ -222,7 +229,6 @@ static void wpi_init(void *);
static void wpi_init_locked(struct wpi_softc *, int);
static void wpi_stop(struct wpi_softc *);
static void wpi_stop_locked(struct wpi_softc *);
-static void wpi_iter_func(void *, struct ieee80211_node *);
static void wpi_newassoc(struct ieee80211_node *, int);
static int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *,
@@ -301,7 +307,7 @@ wpi_probe(device_t dev)
static int
wpi_load_firmware(struct wpi_softc *sc)
{
- const struct firmware *fp ;
+ const struct firmware *fp;
struct wpi_dma_info *dma = &sc->fw_dma;
const struct wpi_firmware_hdr *hdr;
const uint8_t *itext, *idata, *rtext, *rdata, *btext;
@@ -476,7 +482,7 @@ wpi_attach(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
int ac, error, supportsa = 1;
uint32_t tmp;
const struct wpi_ident *ident;
@@ -499,7 +505,6 @@ wpi_attach(device_t dev)
}
}
-#if __FreeBSD_version >= 700000
/*
* Create the taskqueues used by the driver. Primarily
* sc_tq handles most the task
@@ -508,9 +513,6 @@ wpi_attach(device_t dev)
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
-#else
-#error "Sorry, this driver is not yet ready for FreeBSD < 7.0"
-#endif
/* Create the tasks that can be queued */
TASK_INIT(&sc->sc_opstask, 0, wpi_ops, sc);
@@ -607,17 +609,17 @@ wpi_attach(device_t dev)
goto fail;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
error = ENOMEM;
goto fail;
}
+ ic = ifp->if_l2com;
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
/* set device capabilities */
ic->ic_caps =
@@ -663,11 +665,12 @@ wpi_attach(device_t dev)
IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- ieee80211_ifattach(ic);
+ ieee80211_ifattach(ic);
/* override default methods */
ic->ic_node_alloc = wpi_node_alloc;
ic->ic_newassoc = wpi_newassoc;
+ ic->ic_raw_xmit = wpi_raw_xmit;
ic->ic_wme.wme_update = wpi_wme_update;
ic->ic_scan_start = wpi_scan_start;
ic->ic_scan_end = wpi_scan_end;
@@ -675,21 +678,11 @@ wpi_attach(device_t dev)
ic->ic_scan_curchan = wpi_scan_curchan;
ic->ic_scan_mindwell = wpi_scan_mindwell;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = wpi_newstate;
- ieee80211_media_init(ic, wpi_media_change, ieee80211_media_status);
+ ic->ic_vap_create = wpi_vap_create;
+ ic->ic_vap_delete = wpi_vap_delete;
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
-
- /* whilst ieee80211_ifattach will listen for ieee80211 frames,
- * we also want to listen for the lower level radio frames
- */
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -714,7 +707,6 @@ wpi_attach(device_t dev)
#ifdef XXX_DEBUG
ieee80211_announce_channels(ic);
#endif
-
return 0;
fail: wpi_detach(dev);
@@ -725,8 +717,8 @@ static int
wpi_detach(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int ac;
if (ifp != NULL) {
@@ -774,6 +766,48 @@ wpi_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+wpi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct wpi_vap *wvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (wvp == NULL)
+ return NULL;
+ vap = &wvp->vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ wvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = wpi_newstate;
+
+ ieee80211_amrr_init(&wvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /*ms*/);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+wpi_vap_delete(struct ieee80211vap *vap)
+{
+ struct wpi_vap *wvp = WPI_VAP(vap);
+
+ ieee80211_amrr_cleanup(&wvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(wvp, M_80211_VAP);
+}
+
static void
wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
@@ -1195,7 +1229,7 @@ static int
wpi_resume(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
@@ -1213,72 +1247,43 @@ wpi_node_alloc(struct ieee80211_node_table *ic)
{
struct wpi_node *wn;
- wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT |M_ZERO);
+ wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT | M_ZERO);
return &wn->ni;
}
-static int
-wpi_media_change(struct ifnet *ifp)
-{
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- wpi_init(ifp->if_softc);
-
- return 0;
-}
-
/**
* Called by net80211 when ever there is a change to 80211 state machine
*/
static int
-wpi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct wpi_vap *wvp = WPI_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
+ int error;
- DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]));
-
- switch (nstate) {
- case IEEE80211_S_SCAN:
- /*
- * Scanning is handled in net80211 via the scan_start,
- * scan_end, scan_curchan functions. Hence all we do when
- * changing to the SCAN state is update the leds
- */
-
- /* make the link LED blink while we're scanning */
- wpi_set_led(sc, WPI_LED_LINK, 20, 2);
- break;
+ DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate], sc->flags));
- case IEEE80211_S_AUTH:
+ if (nstate == IEEE80211_S_AUTH) {
/* Delay the auth transition until we can update the firmware */
- return wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL);
-
- case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_MONITOR) {
- /* link LED blinks while monitoring */
- wpi_set_led(sc, WPI_LED_LINK, 5, 5);
- break;
- }
- if (ic->ic_state != IEEE80211_S_RUN)
- /* set the association id first */
- return wpi_queue_cmd(sc, WPI_RUN, arg,
- WPI_QUEUE_NORMAL);
- break;
-
- default:
- break;
+ error = wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL);
+ return (error != 0 ? error : EINPROGRESS);
}
-
- return sc->sc_newstate(ic, nstate, arg);
+ if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
+ /* set the association id first */
+ error = wpi_queue_cmd(sc, WPI_RUN, arg, WPI_QUEUE_NORMAL);
+ return (error != 0 ? error : EINPROGRESS);
+ }
+ if (nstate == IEEE80211_S_RUN) {
+ /* RUN -> RUN transition; just restart the timers */
+ wpi_calib_timeout(sc);
+ /* XXX split out rate control timer */
+ }
+ return wvp->newstate(vap, nstate, arg);
}
/*
@@ -1431,8 +1436,8 @@ static void
wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
struct wpi_rx_data *data)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_rx_ring *ring = &sc->rxq;
struct wpi_rx_stat *stat;
struct wpi_rx_head *head;
@@ -1490,7 +1495,7 @@ wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
/* update Rx descriptor */
ring->desc[ring->cur] = htole32(paddr);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1523,27 +1528,25 @@ wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
if (le16toh(head->flags) & 0x4)
tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
WPI_UNLOCK(sc);
- /* XXX frame length > sizeof(struct ieee80211_frame_min)? */
- /* grab a reference to the source node */
ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, stat->rssi, 0, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, stat->rssi, 0, 0);
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, stat->rssi, 0, 0);
-
- /* release node reference */
- ieee80211_free_node(ni);
WPI_LOCK(sc);
}
static void
wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3];
struct wpi_tx_data *txdata = &ring->data[desc->idx];
struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1);
@@ -1584,7 +1587,7 @@ wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- wpi_start(ifp);
+ wpi_start_locked(ifp);
}
static void
@@ -1617,8 +1620,8 @@ wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
static void
wpi_notif_intr(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_rx_desc *desc;
struct wpi_rx_data *data;
uint32_t hw;
@@ -1698,28 +1701,30 @@ wpi_notif_intr(struct wpi_softc *sc)
{
struct wpi_stop_scan *scan =
(struct wpi_stop_scan *)(desc + 1);
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
DPRINTFN(WPI_DEBUG_SCANNING,
("scan finished nchan=%d status=%d chan=%d\n",
scan->nchan, scan->status, scan->chan));
sc->sc_scan_timer = 0;
- ieee80211_scan_next(ic);
+ ieee80211_scan_next(vap);
break;
}
case WPI_MISSED_BEACON:
{
- struct wpi_missed_beacon *beacon =
+ struct wpi_missed_beacon *beacon =
(struct wpi_missed_beacon *)(desc + 1);
-
- if (le32toh(beacon->consecutive) >=
- ic->ic_bmissthreshold) {
- DPRINTF(("Beacon miss: %u >= %u\n",
- le32toh(beacon->consecutive),
- ic->ic_bmissthreshold));
- ieee80211_beacon_miss(ic);
- }
- break;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (le32toh(beacon->consecutive) >=
+ vap->iv_bmissthreshold) {
+ DPRINTF(("Beacon miss: %u >= %u\n",
+ le32toh(beacon->consecutive),
+ vap->iv_bmissthreshold));
+ ieee80211_beacon_miss(ic);
+ }
+ break;
}
}
@@ -1811,7 +1816,9 @@ static int
wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
struct wpi_tx_ring *ring = &sc->txq[ac];
struct wpi_tx_desc *desc;
@@ -1819,6 +1826,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
struct wpi_tx_cmd *cmd;
struct wpi_cmd_data *tx;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
struct mbuf *mnew;
int i, error, nsegs, rate, hdrlen, ismcast;
@@ -1833,7 +1841,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1850,7 +1858,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tx = (struct wpi_cmd_data *)cmd->data;
tx->flags = htole32(WPI_TX_AUTO_SEQ);
- tx->timeout= htole16(0);
+ tx->timeout = htole16(0);
tx->ofdm_mask = 0xff;
tx->cck_mask = 0x0f;
tx->lifetime = htole32(WPI_LIFETIME_INFINITE);
@@ -1861,40 +1869,42 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 ||
!cap->cap_wmeParams[ac].wmep_noackPolicy)
tx->flags |= htole32(WPI_TX_NEED_ACK);
- if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP);
tx->rts_ntries = 7;
}
}
-
/* pick a rate */
- if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MASK) {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) {
uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
/* tell h/w to set timestamp in probe responses */
if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
tx->flags |= htole32(WPI_TX_INSERT_TSTAMP);
-
if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
tx->timeout = htole16(3);
else
tx->timeout = htole16(2);
-
- rate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL;
+ rate = tp->mgmtrate;
} else if (ismcast) {
- rate = ic->ic_mcast_rate;
- } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ rate = tp->mcastrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
- rate &= IEEE80211_RATE_VAL;
+ (void) ieee80211_amrr_choose(ni, &WPI_NODE(ni)->amn);
+ rate = ni->ni_txrate;
}
tx->rate = wpi_plcp_signal(rate);
/* be very persistant at sending frames out */
- tx->data_ntries = 15; /* XXX Way too high */
+#if 0
+ tx->data_ntries = tp->maxretry;
+#else
+ tx->data_ntries = 15; /* XXX way too high */
+#endif
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct wpi_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
@@ -1903,7 +1913,8 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_hwqueue = ac;
if (wh->i_fc[1] & IEEE80211_FC1_WEP)
tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
/* save and trim IEEE802.11 header */
@@ -1976,139 +1987,126 @@ static void
wpi_start(struct ifnet *ifp)
{
struct wpi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+
+ WPI_LOCK(sc);
+ wpi_start_locked(ifp);
+ WPI_UNLOCK(sc);
+}
+
+static void
+wpi_start_locked(struct ifnet *ifp)
+{
+ struct wpi_softc *sc = ifp->if_softc;
struct ieee80211_node *ni;
- struct ether_header *eh;
- struct mbuf *m0;
- int ac, waslocked;
+ struct mbuf *m;
+ int ac;
+
+ WPI_LOCK_ASSERT(sc);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
- waslocked = WPI_LOCK_OWNED(sc);
- if (!waslocked)
- WPI_LOCK(sc);
-
for (;;) {
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- /* management frames go into ring 0 */
- if (sc->txq[0].queued > sc->txq[0].count - 8) {
- ifp->if_oerrors++;
- continue;
- }
-
- if (wpi_tx_data(sc, m0, ni, 0) != 0) {
- ifp->if_oerrors++;
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
-
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
-
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (*eh) &&
- (m0 = m_pullup(m0, sizeof (*eh))) != NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
-
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- ac = M_WME_GETAC(m0);
- if (sc->txq[ac].queued > sc->txq[ac].count - 8) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
-
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (wpi_tx_data(sc, m0, ni, ac) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /* no QoS encapsulation for EAPOL frames */
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued > sc->txq[ac].count - 8) {
+ /* there is no place left in this ring */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (wpi_tx_data(sc, m, ni, ac) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
- if (!waslocked)
+static int
+wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wpi_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ WPI_LOCK(sc);
+
+ /* management frames go into ring 0 */
+ if (sc->txq[0].queued > sc->txq[0].count - 8) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
WPI_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
+
+ ifp->if_opackets++;
+ if (wpi_tx_data(sc, m, ni, 0) != 0)
+ goto bad;
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
+
+ WPI_UNLOCK(sc);
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ WPI_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
}
static int
wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct wpi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
WPI_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP)) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
wpi_init_locked(sc, 0);
+ startall = 1;
+ }
} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) ||
(sc->flags & WPI_FLAG_HW_RADIO_OFF))
wpi_stop_locked(sc);
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- WPI_UNLOCK(sc);
- error = ieee80211_ioctl(ic, cmd, data);
- WPI_LOCK(sc);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- wpi_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
WPI_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2118,7 +2116,8 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
static void
wpi_read_eeprom(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i;
/* read the hardware capabilities, revision and SKU type */
@@ -2222,7 +2221,6 @@ wpi_wme_update(struct ieee80211com *ic)
"txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin,
wme.ac[ac].cwmax, wme.ac[ac].txop));
}
-
return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1);
#undef WPI_USEC
#undef WPI_EXP2
@@ -2234,7 +2232,8 @@ wpi_wme_update(struct ieee80211com *ic)
static int
wpi_mrr_setup(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_mrr_setup mrr;
int i, error;
@@ -2326,7 +2325,8 @@ wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni)
static int
wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_tx_ring *ring = &sc->cmdq;
struct wpi_tx_desc *desc;
struct wpi_tx_data *data;
@@ -2395,10 +2395,10 @@ wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni)
#endif
static int
-wpi_auth(struct wpi_softc *sc)
+wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
struct wpi_node_info node;
int error;
@@ -2412,16 +2412,14 @@ wpi_auth(struct wpi_softc *sc)
sc->config.flags |= htole32(WPI_CONFIG_AUTO |
WPI_CONFIG_24GHZ);
}
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
+ if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
sc->config.cck_mask = 0;
sc->config.ofdm_mask = 0x15;
- break;
- case IEEE80211_MODE_11B:
+ } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
sc->config.cck_mask = 0x03;
sc->config.ofdm_mask = 0;
- break;
- default: /* assume 802.11b/g */
+ } else {
+ /* XXX assume 802.11b/g */
sc->config.cck_mask = 0x0f;
sc->config.ofdm_mask = 0x15;
}
@@ -2457,13 +2455,18 @@ wpi_auth(struct wpi_softc *sc)
}
static int
-wpi_run(struct wpi_softc *sc)
+wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
int error;
- ni = ic->ic_bss;
+ if (vap->iv_opmode == IEEE80211_M_MONITOR) {
+ /* link LED blinks while monitoring */
+ wpi_set_led(sc, WPI_LED_LINK, 5, 5);
+ return 0;
+ }
+
wpi_enable_tsf(sc, ni);
/* update adapter's configuration */
@@ -2488,22 +2491,22 @@ wpi_run(struct wpi_softc *sc)
return error;
}
- error = wpi_set_txpower(sc, ic->ic_bsschan, 1);
+ error = wpi_set_txpower(sc, ni->ni_chan, 1);
if (error != 0) {
device_printf(sc->sc_dev, "could set txpower\n");
return error;
}
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* fake a join to init the tx rate */
- wpi_newassoc(ic->ic_bss, 1);
+ wpi_newassoc(ni, 1);
}
/* link LED always on while associated */
wpi_set_led(sc, WPI_LED_LINK, 0, 1);
/* start automatic rate control timer */
- callout_reset(&sc->calib_to, hz/2, wpi_calib_timeout, sc);
+ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
return (error);
}
@@ -2519,7 +2522,8 @@ wpi_run(struct wpi_softc *sc)
static int
wpi_scan(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_scan_state *ss = ic->ic_scan;
struct wpi_tx_ring *ring = &sc->cmdq;
struct wpi_tx_desc *desc;
@@ -2534,7 +2538,6 @@ wpi_scan(struct wpi_softc *sc)
uint8_t *frm;
int nrates, pktlen, error, i, nssid;
bus_addr_t physaddr;
- struct ifnet *ifp = ic->ic_ifp;
desc = &ring->desc[ring->cur];
data = &ring->data[ring->cur];
@@ -2578,22 +2581,19 @@ wpi_scan(struct wpi_softc *sc)
hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE);
hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ);
- /*XXX Need to cater for multiple essids */
- memset(&hdr->scan_essids, 0, sizeof(hdr->scan_essids));
+ memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids));
nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS);
- for (i = 0; i < nssid; i++ ){
+ for (i = 0; i < nssid; i++) {
hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID;
hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, 32);
memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid,
hdr->scan_essids[i].esslen);
-#ifdef WPI_DEBUG
if (wpi_debug & WPI_DEBUG_SCANNING) {
printf("Scanning Essid: ");
- ieee80211_print_essid(ic->ic_des_ssid[i].ssid,
- ic->ic_des_ssid[i].len);
+ ieee80211_print_essid(hdr->scan_essids[i].essid,
+ hdr->scan_essids[i].esslen);
printf("\n");
}
-#endif
}
/*
@@ -2651,17 +2651,17 @@ wpi_scan(struct wpi_softc *sc)
chan->flags = 0;
if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
chan->flags |= WPI_CHAN_ACTIVE;
- if (ic->ic_des_ssid[0].len != 0)
+ if (nssid != 0)
chan->flags |= WPI_CHAN_DIRECT;
}
chan->gain_dsp = 0x6e; /* Default level */
if (IEEE80211_IS_CHAN_5GHZ(c)) {
chan->active = htole16(10);
- chan->passive = htole16(sc->maxdwell);
+ chan->passive = htole16(ss->ss_maxdwell);
chan->gain_radio = 0x3b;
} else {
chan->active = htole16(20);
- chan->passive = htole16(sc->maxdwell);
+ chan->passive = htole16(ss->ss_maxdwell);
chan->gain_radio = 0x28;
}
@@ -2746,8 +2746,8 @@ wpi_scan(struct wpi_softc *sc)
static int
wpi_config(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power power;
struct wpi_bluetooth bluetooth;
struct wpi_node_info node;
@@ -2968,7 +2968,8 @@ static void
wpi_rfkill_resume(struct wpi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int ntries;
/* enable firmware again */
@@ -3000,46 +3001,26 @@ wpi_rfkill_resume(struct wpi_softc *sc)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
sc->flags &= ~WPI_FLAG_HW_RADIO_OFF;
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_scan_next(ic);
-
- ieee80211_beacon_miss(ic);
-
- /* reset the led sequence */
- switch (ic->ic_state) {
- case IEEE80211_S_SCAN:
- wpi_set_led(sc, WPI_LED_LINK, 20, 2);
- break;
-
- case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- wpi_set_led(sc, WPI_LED_LINK, 5, 5);
- else
+ if (vap != NULL) {
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ ieee80211_beacon_miss(ic);
wpi_set_led(sc, WPI_LED_LINK, 0, 1);
- break;
-
- default:
- break; /* please compiler */
+ } else
+ wpi_set_led(sc, WPI_LED_LINK, 5, 5);
+ } else {
+ ieee80211_scan_next(vap);
+ wpi_set_led(sc, WPI_LED_LINK, 20, 2);
+ }
}
callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
}
static void
-wpi_init(void *arg)
-{
- struct wpi_softc *sc = arg;
-
- WPI_LOCK(sc);
- wpi_init_locked(sc, 0);
- WPI_UNLOCK(sc);
-}
-
-static void
wpi_init_locked(struct wpi_softc *sc, int force)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
int ntries, qid;
@@ -3143,29 +3124,27 @@ wpi_init_locked(struct wpi_softc *sc, int force)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
out:
callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
-
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- else if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- return;
}
static void
-wpi_stop(struct wpi_softc *sc)
+wpi_init(void *arg)
{
+ struct wpi_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
WPI_LOCK(sc);
- wpi_stop_locked(sc);
+ wpi_init_locked(sc, 0);
WPI_UNLOCK(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vaps */
}
+
static void
wpi_stop_locked(struct wpi_softc *sc)
-
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
int ac;
@@ -3212,66 +3191,44 @@ wpi_stop_locked(struct wpi_softc *sc)
tmp = WPI_READ(sc, WPI_RESET);
WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET);
sc->flags &= ~WPI_FLAG_BUSY;
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
static void
-wpi_iter_func(void *arg, struct ieee80211_node *ni)
+wpi_stop(struct wpi_softc *sc)
{
- struct wpi_softc *sc = arg;
- struct wpi_node *wn = (struct wpi_node *)ni;
-
- ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn);
+ WPI_LOCK(sc);
+ wpi_stop_locked(sc);
+ WPI_UNLOCK(sc);
}
static void
wpi_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc;
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct wpi_vap *wvp = WPI_VAP(vap);
- ieee80211_amrr_node_init(&sc->amrr, &((struct wpi_node *)ni)->amn);
-
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&wvp->amrr, &WPI_NODE(ni)->amn, ni);
}
static void
wpi_calib_timeout(void *arg)
{
struct wpi_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int temp;
- if (ic->ic_state != IEEE80211_S_RUN)
+ if (vap->iv_state != IEEE80211_S_RUN)
return;
- /* automatic rate control triggered every 500ms */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- if (ic->ic_opmode == IEEE80211_M_STA)
- wpi_iter_func(sc, ic->ic_bss);
- else
- ieee80211_iterate_nodes(&ic->ic_sta, wpi_iter_func, sc);
- }
-
/* update sensor data */
temp = (int)WPI_READ(sc, WPI_TEMPERATURE);
DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp));
-#if 0
- //XXX Used by OpenBSD Sensor Framework
- sc->sensor.value = temp + 260;
-#endif
- /* automatic power calibration every 60s */
- if (++sc->calib_cnt >= 120) {
- wpi_power_calibration(sc, temp);
- sc->calib_cnt = 0;
- }
+ wpi_power_calibration(sc, temp);
- callout_reset(&sc->calib_to, hz/2, wpi_calib_timeout, sc);
+ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
}
/*
@@ -3281,6 +3238,10 @@ wpi_calib_timeout(void *arg)
static void
wpi_power_calibration(struct wpi_softc *sc, int temp)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
/* sanity-check read value */
if (temp < -260 || temp > 25) {
/* this can't be correct, ignore */
@@ -3297,7 +3258,7 @@ wpi_power_calibration(struct wpi_softc *sc, int temp)
sc->temp = temp;
- if (wpi_set_txpower(sc, sc->sc_ic.ic_bss->ni_chan,1) != 0) {
+ if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) {
/* just warn, too bad for the automatic calibration... */
device_printf(sc->sc_dev,"could not adjust Tx power\n");
}
@@ -3310,7 +3271,8 @@ wpi_power_calibration(struct wpi_softc *sc, int temp)
static void
wpi_read_eeprom_channels(struct wpi_softc *sc, int n)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct wpi_chan_band *band = &wpi_bands[n];
struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND];
int chan, i, offset, passive;
@@ -3416,7 +3378,8 @@ wpi_read_eeprom_group(struct wpi_softc *sc, int n)
static int
wpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power_group *group;
struct wpi_cmd_txpower txpower;
u_int chan;
@@ -3476,7 +3439,8 @@ wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group,
#define interpolate(x, x1, y1, x2, y2, n) \
((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power_sample *sample;
int pwr, idx;
u_int chan;
@@ -3587,13 +3551,12 @@ wpi_set_channel(struct ieee80211com *ic)
* callback.
*/
static void
-wpi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
- sc->maxdwell = maxdwell;
-
wpi_queue_cmd(sc, WPI_SCAN_CURCHAN, 0, WPI_QUEUE_NORMAL);
}
@@ -3604,7 +3567,7 @@ wpi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
* us when it's finished and we have no way to interrupt it.
*/
static void
-wpi_scan_mindwell(struct ieee80211com *ic)
+wpi_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
@@ -3619,8 +3582,10 @@ static void
wpi_ops(void *arg0, int pending)
{
struct wpi_softc *sc = arg0;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int cmd, arg, error;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
again:
WPI_CMD_LOCK(sc);
@@ -3659,6 +3624,8 @@ again:
switch (cmd) {
case WPI_SCAN_START:
+ /* make the link LED blink while we're scanning */
+ wpi_set_led(sc, WPI_LED_LINK, 20, 2);
sc->flags |= WPI_FLAG_SCANNING;
break;
@@ -3668,7 +3635,7 @@ again:
case WPI_SCAN_CURCHAN:
if (wpi_scan(sc))
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
break;
case WPI_SET_CHAN:
@@ -3680,29 +3647,36 @@ again:
case WPI_AUTH:
/* The node must be registered in the firmware before auth */
- error = wpi_auth(sc);
+ error = wpi_auth(sc, vap);
+ WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to auth state, error %d\n",
__func__, error);
- WPI_UNLOCK(sc);
return;
}
- /* Send the auth frame now */
- sc->sc_newstate(ic, IEEE80211_S_AUTH, arg);
- break;
+ IEEE80211_LOCK(ic);
+ WPI_VAP(vap)->newstate(vap, IEEE80211_S_AUTH, arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_AUTH, arg);
+ IEEE80211_UNLOCK(ic);
+ goto again;
case WPI_RUN:
- error = wpi_run(sc);
+ error = wpi_run(sc, vap);
+ WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to run state, error %d\n",
__func__, error);
- WPI_UNLOCK(sc);
return;
}
- sc->sc_newstate(ic, IEEE80211_S_RUN, arg);
- break;
+ IEEE80211_LOCK(ic);
+ WPI_VAP(vap)->newstate(vap, IEEE80211_S_RUN, arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_RUN, arg);
+ IEEE80211_UNLOCK(ic);
+ goto again;
}
WPI_UNLOCK(sc);
@@ -3800,9 +3774,12 @@ wpi_watchdog(void *arg)
}
}
if (sc->sc_scan_timer > 0) {
- if (--sc->sc_scan_timer == 0) {
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ if (--sc->sc_scan_timer == 0 && vap != NULL) {
device_printf(sc->sc_dev,"scan timeout\n");
- ieee80211_cancel_scan(&sc->sc_ic);
+ ieee80211_cancel_scan(vap);
wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR);
}
}
@@ -3814,25 +3791,25 @@ wpi_watchdog(void *arg)
#ifdef WPI_DEBUG
static const char *wpi_cmd_str(int cmd)
{
- switch(cmd) {
- case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD";
- case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE";
- case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE";
- case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME";
- case WPI_CMD_TSF: return "WPI_CMD_TSF";
- case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE";
- case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA";
- case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP";
- case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED";
- case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE";
- case WPI_CMD_SCAN: return "WPI_CMD_SCAN";
- case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON";
- case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER";
- case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH";
-
- default:
+ switch (cmd) {
+ case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD";
+ case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE";
+ case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE";
+ case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME";
+ case WPI_CMD_TSF: return "WPI_CMD_TSF";
+ case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE";
+ case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA";
+ case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP";
+ case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED";
+ case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE";
+ case WPI_CMD_SCAN: return "WPI_CMD_SCAN";
+ case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON";
+ case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER";
+ case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH";
+
+ default:
KASSERT(1, ("Unknown Command: %d\n", cmd));
- return "UNKNOWN CMD"; // Make the compiler happy
+ return "UNKNOWN CMD"; /* Make the compiler happy */
}
}
#endif
diff --git a/sys/dev/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h
index 39020df..1436592 100644
--- a/sys/dev/wpi/if_wpivar.h
+++ b/sys/dev/wpi/if_wpivar.h
@@ -110,6 +110,7 @@ struct wpi_node {
struct ieee80211_node ni; /* must be the first */
struct ieee80211_amrr_node amn;
};
+#define WPI_NODE(ni) ((struct wpi_node *)(ni))
struct wpi_power_sample {
uint8_t index;
@@ -118,26 +119,26 @@ struct wpi_power_sample {
struct wpi_power_group {
#define WPI_SAMPLES_COUNT 5
- struct wpi_power_sample samples[WPI_SAMPLES_COUNT];
- uint8_t chan;
- int8_t maxpwr;
- int16_t temp;
+ struct wpi_power_sample samples[WPI_SAMPLES_COUNT];
+ uint8_t chan;
+ int8_t maxpwr;
+ int16_t temp;
};
-struct wpi_softc {
- device_t sc_dev;
- struct ifnet *sc_ifp;
+struct wpi_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_amrr amrr;
- /* net80211 driver specifics */
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
+ int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
- unsigned long maxdwell; /* Max dwell time whilst scanning */
+};
+#define WPI_VAP(vap) ((struct wpi_vap *)(vap))
+struct wpi_softc {
+ device_t sc_dev;
+ struct ifnet *sc_ifp;
struct mtx sc_mtx;
- struct ieee80211_amrr amrr;
-
/* Flags indicating the current state the driver
* expects the hardware to be in
*/
@@ -212,15 +213,15 @@ struct wpi_softc {
int sc_cmd_next; /* last queued scan task */
struct mtx sc_cmdlock;
- /* Task queues used to control the driver */
- struct taskqueue *sc_tq; /* Main command task queue */
- struct taskqueue *sc_tq2;/* firmware reset task queue */
+ /* Task queues used to control the driver */
+ struct taskqueue *sc_tq; /* Main command task queue */
+ struct taskqueue *sc_tq2; /* firmware reset task queue */
- /* Tasks used by the driver */
- struct task sc_radioontask; /* enable rf transmitter task*/
- struct task sc_radioofftask;/* disable rf transmitter task*/
- struct task sc_opstask; /* operation handling task */
- struct task sc_restarttask; /* reset firmware task */
+ /* Tasks used by the driver */
+ struct task sc_radioontask; /* enable rf transmitter task*/
+ struct task sc_radioofftask;/* disable rf transmitter task*/
+ struct task sc_opstask; /* operation handling task */
+ struct task sc_restarttask; /* reset firmware task */
/* Eeprom info */
uint8_t cap;
@@ -228,7 +229,7 @@ struct wpi_softc {
uint8_t type;
struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT];
int8_t maxpwr[IEEE80211_CHAN_MAX];
- char domain[4]; //reglatory domain //XXX
+ char domain[4]; /*reglatory domain XXX */
};
#define WPI_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
@@ -236,7 +237,6 @@ struct wpi_softc {
#define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define WPI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
-#define WPI_LOCK_OWNED(_sc) mtx_owned(&(_sc)->sc_mtx)
#define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
#define WPI_CMD_LOCK_INIT(_sc) \
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 36d68d1..2027692 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -253,8 +253,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/mips/conf/IDT b/sys/mips/conf/IDT
index 8b63260..73af1ee 100644
--- a/sys/mips/conf/IDT
+++ b/sys/mips/conf/IDT
@@ -46,8 +46,6 @@ device md
device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_ap #802.11 AP mode scanning
-device wlan_scan_sta #802.11 STA mode scanning
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
device ath_rate_sample # SampleRate tx rate control for ath
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index b5de9b8..46b5eb0 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -311,8 +311,7 @@ SUBDIR= ${_3dfx} \
wlan_acl \
wlan_amrr \
wlan_ccmp \
- wlan_scan_ap \
- wlan_scan_sta \
+ wlan_rssadapt \
wlan_tkip \
wlan_wep \
wlan_xauth \
diff --git a/sys/modules/ath_rate_amrr/Makefile b/sys/modules/ath_rate_amrr/Makefile
index 5523815..0405da2 100644
--- a/sys/modules/ath_rate_amrr/Makefile
+++ b/sys/modules/ath_rate_amrr/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= amrr.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+# echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+ echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/ath_rate_onoe/Makefile b/sys/modules/ath_rate_onoe/Makefile
index c111e26..2edd73f 100644
--- a/sys/modules/ath_rate_onoe/Makefile
+++ b/sys/modules/ath_rate_onoe/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= onoe.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+# echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/ath_rate_sample/Makefile b/sys/modules/ath_rate_sample/Makefile
index fc91e2b..4a13f23 100644
--- a/sys/modules/ath_rate_sample/Makefile
+++ b/sys/modules/ath_rate_sample/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= sample.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+# echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+ echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/malo/Makefile b/sys/modules/malo/Makefile
index c0e88c5..7886102 100644
--- a/sys/modules/malo/Makefile
+++ b/sys/modules/malo/Makefile
@@ -3,6 +3,10 @@
.PATH: ${.CURDIR}/../../dev/malo
KMOD = if_malo
-SRCS = if_malo.c if_malohal.c if_malo_pci.c device_if.h bus_if.h pci_if.h
+SRCS = if_malo.c if_malohal.c if_malo_pci.c
+SRCS+= device_if.h bus_if.h pci_if.h opt_malo.h
+
+opt_malo.h:
+ echo '#define MALO_DEBUG 1'> $@
.include <bsd.kmod.mk>
diff --git a/sys/modules/ral/Makefile b/sys/modules/ral/Makefile
index 2ec55bf..6a3b269 100644
--- a/sys/modules/ral/Makefile
+++ b/sys/modules/ral/Makefile
@@ -2,8 +2,8 @@
.PATH: ${.CURDIR}/../../dev/ral
-KMOD = if_ral
-SRCS = rt2560.c rt2661.c if_ralrate.c if_ral_pci.c \
- device_if.h bus_if.h pci_if.h
+KMOD= if_ral
+SRCS= rt2560.c rt2661.c if_ral_pci.c
+SRCS+= device_if.h bus_if.h pci_if.h
.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/Makefile b/sys/modules/ralfw/Makefile
new file mode 100644
index 0000000..3db53ad
--- /dev/null
+++ b/sys/modules/ralfw/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= rt2561 rt2561s rt2661 rt2860
+
+.include <bsd.subdir.mk>
diff --git a/sys/modules/ralfw/Makefile.inc b/sys/modules/ralfw/Makefile.inc
new file mode 100644
index 0000000..2dc6b47
--- /dev/null
+++ b/sys/modules/ralfw/Makefile.inc
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+#
+# Common rules for building firmware. Note this gets auto-included
+# by the subdir Makefile's as a consequence of included bsd.kmod.mk.
+#
+KMOD= ${IMG}fw
+_FIRM= ${IMG}.fw
+
+CLEANFILES+= ${_FIRM}
+
+FIRMWS= ${_FIRM}:${KMOD}
+
+${_FIRM}: ${.CURDIR}/../../../contrib/dev/ral/${_FIRM}.uu
+ uudecode -p $? > ${.TARGET}
diff --git a/sys/modules/ralfw/rt2561/Makefile b/sys/modules/ralfw/rt2561/Makefile
new file mode 100644
index 0000000..71044a6
--- /dev/null
+++ b/sys/modules/ralfw/rt2561/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+IMG= rt2561
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/rt2561s/Makefile b/sys/modules/ralfw/rt2561s/Makefile
new file mode 100644
index 0000000..40c6bc1
--- /dev/null
+++ b/sys/modules/ralfw/rt2561s/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+IMG= rt2561s
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/rt2661/Makefile b/sys/modules/ralfw/rt2661/Makefile
new file mode 100644
index 0000000..b372786
--- /dev/null
+++ b/sys/modules/ralfw/rt2661/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+IMG= rt2661
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/wlan/Makefile b/sys/modules/wlan/Makefile
index 5712218..b6f10e3 100644
--- a/sys/modules/wlan/Makefile
+++ b/sys/modules/wlan/Makefile
@@ -3,16 +3,18 @@
.PATH: ${.CURDIR}/../../net80211
KMOD= wlan
-SRCS= ieee80211.c ieee80211_crypto.c ieee80211_crypto_none.c \
+SRCS= ieee80211.c ieee80211_crypto.c ieee80211_crypto_none.c ieee80211_dfs.c \
ieee80211_freebsd.c ieee80211_input.c ieee80211_ioctl.c \
- ieee80211_node.c ieee80211_output.c ieee80211_power.c \
- ieee80211_proto.c ieee80211_scan.c ieee80211_regdomain.c \
- ieee80211_ht.c
-SRCS+= bus_if.h device_if.h opt_compat.h opt_inet.h opt_ipx.h
+ ieee80211_node.c ieee80211_output.c ieee80211_phy.c ieee80211_power.c \
+ ieee80211_proto.c ieee80211_scan.c ieee80211_scan_sta.c \
+ ieee80211_regdomain.c ieee80211_ht.c \
+ ieee80211_adhoc.c ieee80211_hostap.c ieee80211_monitor.c \
+ ieee80211_sta.c ieee80211_wds.c
+SRCS+= bus_if.h device_if.h opt_inet.h opt_ipx.h opt_wlan.h
.if !defined(KERNBUILDDIR)
-opt_compat.h:
- echo "#define COMPAT_FREEBSD6 1" > ${.TARGET}
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
opt_inet.h:
echo "#define INET 1" > opt_inet.h
diff --git a/sys/modules/wlan_acl/Makefile b/sys/modules/wlan_acl/Makefile
index aa71de2..5922859 100644
--- a/sys/modules/wlan_acl/Makefile
+++ b/sys/modules/wlan_acl/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_acl
SRCS= ieee80211_acl.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_amrr/Makefile b/sys/modules/wlan_amrr/Makefile
index ac7b5ce..481d124 100644
--- a/sys/modules/wlan_amrr/Makefile
+++ b/sys/modules/wlan_amrr/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_amrr
SRCS= ieee80211_amrr.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_ccmp/Makefile b/sys/modules/wlan_ccmp/Makefile
index 06bafce..bbf8f62 100644
--- a/sys/modules/wlan_ccmp/Makefile
+++ b/sys/modules/wlan_ccmp/Makefile
@@ -6,5 +6,11 @@
KMOD= wlan_ccmp
SRCS= ieee80211_crypto_ccmp.c
SRCS+= rijndael-alg-fst.c rijndael-api.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_rssadapt/Makefile b/sys/modules/wlan_rssadapt/Makefile
new file mode 100644
index 0000000..2285afc
--- /dev/null
+++ b/sys/modules/wlan_rssadapt/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../net80211
+
+KMOD= wlan_rssadapt
+SRCS= ieee80211_rssadapt.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_scan_ap/Makefile b/sys/modules/wlan_scan_ap/Makefile
deleted file mode 100644
index 117a6bc..0000000
--- a/sys/modules/wlan_scan_ap/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/../../net80211
-
-KMOD= wlan_scan_ap
-SRCS= ieee80211_scan_ap.c
-
-.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_scan_sta/Makefile b/sys/modules/wlan_scan_sta/Makefile
deleted file mode 100644
index 8d96875..0000000
--- a/sys/modules/wlan_scan_sta/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/../../net80211
-
-KMOD= wlan_scan_sta
-SRCS= ieee80211_scan_sta.c
-
-.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_tkip/Makefile b/sys/modules/wlan_tkip/Makefile
index 1449b78..80c19f8 100644
--- a/sys/modules/wlan_tkip/Makefile
+++ b/sys/modules/wlan_tkip/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_tkip
SRCS= ieee80211_crypto_tkip.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_wep/Makefile b/sys/modules/wlan_wep/Makefile
index 88ad322..3a5c6dc 100644
--- a/sys/modules/wlan_wep/Makefile
+++ b/sys/modules/wlan_wep/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_wep
SRCS= ieee80211_crypto_wep.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_xauth/Makefile b/sys/modules/wlan_xauth/Makefile
index fba6f8a..1877fd1 100644
--- a/sys/modules/wlan_xauth/Makefile
+++ b/sys/modules/wlan_xauth/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_xauth
SRCS= ieee80211_xauth.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h
index 9f50e3c4..d41659f 100644
--- a/sys/net80211/_ieee80211.h
+++ b/sys/net80211/_ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,16 +28,31 @@
#ifndef _NET80211__IEEE80211_H_
#define _NET80211__IEEE80211_H_
+/*
+ * 802.11 implementation definitions.
+ *
+ * NB: this file is used by applications.
+ */
+
+/*
+ * PHY type; mostly used to identify FH phys.
+ */
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
- IEEE80211_T_HT, /* high throughput, full GI */
+ IEEE80211_T_HT, /* high throughput */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
-/* XXX not really a mode; there are really multiple PHY's */
+/*
+ * PHY mode; this is not really a mode as multi-mode devices
+ * have multiple PHY's. Mode is mostly used as a shorthand
+ * for constraining which channels to consider in setting up
+ * operation. Modes used to be used more extensively when
+ * channels were identified as IEEE channel numbers.
+ */
enum ieee80211_phymode {
IEEE80211_MODE_AUTO = 0, /* autoselect */
IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
@@ -52,13 +67,18 @@ enum ieee80211_phymode {
};
#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1)
+/*
+ * Operating mode. Devices do not necessarily support
+ * all modes; they indicate which are supported in their
+ * capabilities.
+ */
enum ieee80211_opmode {
- IEEE80211_M_STA = 1, /* infrastructure station */
IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
+ IEEE80211_M_STA = 1, /* infrastructure station */
+ IEEE80211_M_WDS = 2, /* WDS link */
IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
- IEEE80211_M_HOSTAP = 6, /* Software Access Point */
- IEEE80211_M_MONITOR = 8, /* Monitor mode */
- IEEE80211_M_WDS = 2 /* WDS link */
+ IEEE80211_M_HOSTAP = 4, /* Software Access Point */
+ IEEE80211_M_MONITOR = 5, /* Monitor mode */
};
#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
@@ -72,7 +92,11 @@ enum ieee80211_protmode {
};
/*
- * Authentication mode.
+ * Authentication mode. The open and shared key authentication
+ * modes are implemented within the 802.11 layer. 802.1x and
+ * WPA/802.11i are implemented in user mode by setting the
+ * 802.11 layer into IEEE80211_AUTH_8021X and deferring
+ * authentication to user space programs.
*/
enum ieee80211_authmode {
IEEE80211_AUTH_NONE = 0,
@@ -265,18 +289,28 @@ struct ieee80211_channel {
#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once. For most AP's this is way more
+ * than is feasible so we use a default of 128. This
+ * number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN.
+ */
+#define IEEE80211_AID_DEF 128
+#define IEEE80211_AID_MIN 16
+
+/*
* 802.11 rate set.
*/
#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
struct ieee80211_rateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
/*
- * 802.11n variant of ieee80211_rateset. Instead
+ * 802.11n variant of ieee80211_rateset. Instead of
* legacy rates the entries are MCS rates. We define
* the structure such that it can be used interchangeably
* with an ieee80211_rateset (modulo structure size).
@@ -284,27 +318,58 @@ struct ieee80211_rateset {
#define IEEE80211_HTRATE_MAXSIZE 127
struct ieee80211_htrateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
};
#define IEEE80211_RATE_MCS 0x80
/*
- * Roaming state visible to user space. There are two
- * thresholds that control whether roaming is considered;
- * when either is exceeded the 802.11 layer will check
- * the scan cache for another AP. If the cache is stale
- * then a scan may be triggered.
+ * Per-mode transmit parameters/controls visible to user space.
+ * These can be used to set fixed transmit rate for all operating
+ * modes or on a per-client basis according to the capabilities
+ * of the client (e.g. an 11b client associated to an 11g ap).
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparam {
+ uint8_t ucastrate; /* ucast data rate (legacy/MCS|0x80) */
+ uint8_t mgmtrate; /* mgmt frame rate (legacy/MCS|0x80) */
+ uint8_t mcastrate; /* multicast rate (legacy/MCS|0x80) */
+ uint8_t maxretry; /* max unicast data retry count */
+};
+
+/*
+ * Per-mode roaming state visible to user space. There are two
+ * thresholds that control whether roaming is considered; when
+ * either is exceeded the 802.11 layer will check the scan cache
+ * for another AP. If the cache is stale then a scan may be
+ * triggered.
+ */
+struct ieee80211_roamparam {
+ int8_t rssi; /* rssi thresh (.5 dBm) */
+ uint8_t rate; /* tx rate thresh (.5 Mb/s or MCS) */
+ uint16_t pad; /* reserve */
+};
+
+/*
+ * Regulatory Information.
+ */
+struct ieee80211_regdomain {
+ uint16_t regdomain; /* SKU */
+ uint16_t country; /* ISO country code */
+ uint8_t location; /* I (indoor), O (outdoor), other */
+ uint8_t ecm; /* Extended Channel Mode */
+ char isocc[2]; /* country code string */
+ short pad[2];
+};
+
+/*
+ * MIMO antenna/radio state.
*/
-struct ieee80211_roam {
- int8_t rssi11a; /* rssi thresh for 11a bss */
- int8_t rssi11b; /* for 11g sta in 11b bss */
- int8_t rssi11bOnly; /* for 11b sta */
- uint8_t pad1;
- uint8_t rate11a; /* rate thresh for 11a bss */
- uint8_t rate11b; /* for 11g sta in 11b bss */
- uint8_t rate11bOnly; /* for 11b sta */
- uint8_t pad2;
+struct ieee80211_mimo_info {
+ int8_t rssi[3]; /* per-antenna rssi */
+ int8_t noise[3]; /* per-antenna noise floor */
+ uint32_t evm[3]; /* EVM data */
};
#endif /* _NET80211__IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index dbeb7a3..952f420 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic handler
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -38,10 +39,13 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -57,6 +61,20 @@ const char *ieee80211_phymode_name[] = {
"11na", /* IEEE80211_MODE_11NA */
"11ng", /* IEEE80211_MODE_11NG */
};
+static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag);
+static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag);
+static int ieee80211_media_setup(struct ieee80211com *ic,
+ struct ifmedia *media, int caps, int addsta,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat);
+static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *);
+static int ieee80211com_media_change(struct ifnet *);
+static int media_status(enum ieee80211_opmode,
+ const struct ieee80211_channel *);
+
+MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state");
/*
* Default supported rates for 802.11 operation (in IEEE .5Mb units).
@@ -75,66 +93,6 @@ static const struct ieee80211_rateset ieee80211_rateset_11g =
{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
#undef B
-static int media_status(enum ieee80211_opmode ,
- const struct ieee80211_channel *);
-
-/* list of all instances */
-SLIST_HEAD(ieee80211_list, ieee80211com);
-static struct ieee80211_list ieee80211_list =
- SLIST_HEAD_INITIALIZER(ieee80211_list);
-static uint8_t ieee80211_vapmap[32]; /* enough for 256 */
-static struct mtx ieee80211_vap_mtx;
-MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
-
-static void
-ieee80211_add_vap(struct ieee80211com *ic)
-{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- int i;
- uint8_t b;
-
- mtx_lock(&ieee80211_vap_mtx);
- ic->ic_vap = 0;
- for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
- ic->ic_vap += NBBY;
- if (i == N(ieee80211_vapmap))
- panic("vap table full");
- for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
- ic->ic_vap++;
- setbit(ieee80211_vapmap, ic->ic_vap);
- SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
- mtx_unlock(&ieee80211_vap_mtx);
-#undef N
-}
-
-static void
-ieee80211_remove_vap(struct ieee80211com *ic)
-{
- mtx_lock(&ieee80211_vap_mtx);
- SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
- KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
- ("invalid vap id %d", ic->ic_vap));
- KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
- ("vap id %d not allocated", ic->ic_vap));
- clrbit(ieee80211_vapmap, ic->ic_vap);
- mtx_unlock(&ieee80211_vap_mtx);
-}
-
-/*
- * Default reset method for use with the ioctl support. This
- * method is invoked after any state change in the 802.11
- * layer that should be propagated to the hardware but not
- * require re-initialization of the 802.11 state machine (e.g
- * rescanning for an ap). We always return ENETRESET which
- * should cause the driver to re-initialize the device. Drivers
- * can override this method to implement more optimized support.
- */
-static int
-ieee80211_default_reset(struct ifnet *ifp)
-{
- return ENETRESET;
-}
-
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
@@ -153,6 +111,7 @@ ieee80211_chan_init(struct ieee80211com *ic)
KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
("invalid number of channels specified: %u", ic->ic_nchans));
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+ memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps));
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
for (i = 0; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
@@ -186,9 +145,13 @@ ieee80211_chan_init(struct ieee80211com *ic)
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
sizeof(ic->ic_chan_avail));
- ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ /* sort channel table to allow lookup optimizations */
+ ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
+
+ /* invalidate any previous state */
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
ic->ic_prevchan = NULL;
+ ic->ic_csa_newchan = NULL;
/* arbitrarily pick the first channel */
ic->ic_curchan = &ic->ic_channels[0];
@@ -206,54 +169,44 @@ ieee80211_chan_init(struct ieee80211com *ic)
#undef DEFAULTRATES
}
+static void
+null_update_mcast(struct ifnet *ifp)
+{
+ if_printf(ifp, "need multicast update callback\n");
+}
+
+static void
+null_update_promisc(struct ifnet *ifp)
+{
+ if_printf(ifp, "need promiscuous mode update callback\n");
+}
+
+/*
+ * Attach/setup the common net80211 state. Called by
+ * the driver on attach to prior to creating any vap's.
+ */
void
ieee80211_ifattach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct sockaddr_dl *sdl;
+ struct ifaddr *ifa;
- ether_ifattach(ifp, ic->ic_myaddr);
- ifp->if_output = ieee80211_output;
-
- bpfattach2(ifp, DLT_IEEE802_11,
- sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
-
- /* override the 802.3 setting */
- ifp->if_hdrlen = ic->ic_headroom
- + sizeof(struct ieee80211_qosframe_addr4)
- + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
- + IEEE80211_WEP_EXTIVLEN;
- /* XXX no way to recalculate on ifdetach */
- if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
- /* XXX sanity check... */
- max_linkhdr = ALIGN(ifp->if_hdrlen);
- max_hdr = max_linkhdr + max_protohdr;
- max_datalen = MHLEN - max_hdr;
- }
+ KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type));
+ IEEE80211_LOCK_INIT(ic, "ieee80211com");
+ TAILQ_INIT(&ic->ic_vaps);
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
* channel if not already specified.
*/
- ieee80211_chan_init(ic);
+ ieee80211_media_init(ic);
- if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_BGSCAN;
-#if 0
- /* XXX not until WME+WPA issues resolved */
- if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_WME;
-#endif
- if (ic->ic_caps & IEEE80211_C_BURST)
- ic->ic_flags |= IEEE80211_F_BURST;
- ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ ic->ic_update_mcast = null_update_mcast;
+ ic->ic_update_promisc = null_update_promisc;
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
- ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
- ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
- IEEE80211_LOCK_INIT(ic, "ieee80211com");
- IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
-
ic->ic_lintval = ic->ic_bintval;
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
@@ -263,30 +216,42 @@ ieee80211_ifattach(struct ieee80211com *ic)
ieee80211_proto_attach(ic);
ieee80211_ht_attach(ic);
ieee80211_scan_attach(ic);
-
- ieee80211_add_vap(ic);
-
- ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */
-
- /*
- * Install a default reset method for the ioctl support.
- * The driver is expected to fill this in before calling us.
- */
- if (ic->ic_reset == NULL)
- ic->ic_reset = ieee80211_default_reset;
-
- KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed"));
- ifp->if_llsoftc = ic;
+ ieee80211_regdomain_attach(ic);
+
+ ieee80211_sysctl_attach(ic);
+
+ ifp->if_addrlen = IEEE80211_ADDR_LEN;
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = IEEE80211_MTU_MAX;
+ ifp->if_broadcastaddr = ieee80211broadcastaddr;
+
+ ifa = ifaddr_byindex(ifp->if_index);
+ KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */
+ sdl->sdl_alen = IEEE80211_ADDR_LEN;
+ IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr);
}
+/*
+ * Detach net80211 state on device detach. Tear down
+ * all vap's and reclaim all common state prior to the
+ * device state going away. Note we may call back into
+ * driver; it must be prepared for this.
+ */
void
ieee80211_ifdetach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
- ieee80211_remove_vap(ic);
+ /* XXX ieee80211_stop_all? */
+ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
+ ieee80211_vap_destroy(vap);
ieee80211_sysctl_detach(ic);
+ ieee80211_regdomain_detach(ic);
ieee80211_scan_detach(ic);
ieee80211_ht_detach(ic);
/* NB: must be called before ieee80211_node_detach */
@@ -297,10 +262,386 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ifmedia_removeall(&ic->ic_media);
IEEE80211_LOCK_DESTROY(ic);
- IEEE80211_BEACON_LOCK_DESTROY(ic);
+ if_detach(ifp);
+}
+
+/*
+ * Default reset method for use with the ioctl support. This
+ * method is invoked after any state change in the 802.11
+ * layer that should be propagated to the hardware but not
+ * require re-initialization of the 802.11 state machine (e.g
+ * rescanning for an ap). We always return ENETRESET which
+ * should cause the driver to re-initialize the device. Drivers
+ * can override this method to implement more optimized support.
+ */
+static int
+default_reset(struct ieee80211vap *vap, u_long cmd)
+{
+ return ENETRESET;
+}
+
+/*
+ * Prepare a vap for use. Drivers use this call to
+ * setup net80211 state in new vap's prior attaching
+ * them with ieee80211_vap_attach (below).
+ */
+int
+ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+#define IEEE80211_C_OPMODE \
+ (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
+ IEEE80211_C_MONITOR | IEEE80211_C_WDS)
+ struct ifnet *ifp;
+
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n",
+ __func__);
+ return ENOMEM;
+ }
+ if_initname(ifp, name, unit);
+ ifp->if_softc = vap; /* back pointer */
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_start = ieee80211_start;
+ ifp->if_ioctl = ieee80211_ioctl;
+ ifp->if_watchdog = NULL; /* NB: no watchdog routine */
+ ifp->if_init = ieee80211_init;
+ /* NB: input+output filled in by ether_ifattach */
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ vap->iv_ifp = ifp;
+ vap->iv_ic = ic;
+ vap->iv_flags = ic->ic_flags; /* propagate common flags */
+ vap->iv_flags_ext = ic->ic_flags_ext;
+ vap->iv_flags_ven = ic->ic_flags_ven;
+ vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
+ vap->iv_htcaps = ic->ic_htcaps;
+ vap->iv_opmode = opmode;
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* auto-enable s/w beacon miss support */
+ if (flags & IEEE80211_CLONE_NOBEACONS)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ break;
+ case IEEE80211_M_IBSS:
+ vap->iv_caps |= IEEE80211_C_IBSS;
+ break;
+ case IEEE80211_M_AHDEMO:
+ vap->iv_caps |= IEEE80211_C_AHDEMO;
+ break;
+ case IEEE80211_M_HOSTAP:
+ vap->iv_caps |= IEEE80211_C_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ vap->iv_caps |= IEEE80211_C_MONITOR;
+ break;
+ case IEEE80211_M_WDS:
+ vap->iv_caps |= IEEE80211_C_WDS;
+ /*
+ * WDS links must specify the bssid of the far end.
+ * For legacy operation this is a static relationship.
+ * For non-legacy operation the station must associate
+ * and be authorized to pass traffic. Plumbing the
+ * vap to the proper node happens when the vap
+ * transitions to RUN state.
+ */
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ if (flags & IEEE80211_CLONE_WDSLEGACY)
+ vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY;
+ break;
+ }
+ /*
+ * Enable various functionality by default if we're
+ * capable; the driver can override us if it knows better.
+ */
+ if (vap->iv_caps & IEEE80211_C_WME)
+ vap->iv_flags |= IEEE80211_F_WME;
+ if (vap->iv_caps & IEEE80211_C_BURST)
+ vap->iv_flags |= IEEE80211_F_BURST;
+ if (vap->iv_caps & IEEE80211_C_FF)
+ vap->iv_flags |= IEEE80211_F_FF;
+ if (vap->iv_caps & IEEE80211_C_TURBOP)
+ vap->iv_flags |= IEEE80211_F_TURBOP;
+ /* NB: bg scanning only makes sense for station mode right now */
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_caps & IEEE80211_C_BGSCAN))
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
+ vap->iv_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ /* XXX out of caps, just ena */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+
+ vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
+ vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT;
+ /*
+ * Install a default reset method for the ioctl support;
+ * the driver can override this.
+ */
+ vap->iv_reset = default_reset;
+
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr);
+
+ ieee80211_sysctl_vattach(vap);
+ ieee80211_crypto_vattach(vap);
+ ieee80211_node_vattach(vap);
+ ieee80211_power_vattach(vap);
+ ieee80211_proto_vattach(vap);
+ ieee80211_ht_vattach(vap);
+ ieee80211_scan_vattach(vap);
+ ieee80211_regdomain_vattach(vap);
+
+ return 0;
+#undef IEEE80211_C_OPMODE
+}
+
+/*
+ * Activate a vap. State should have been prepared with a
+ * call to ieee80211_vap_setup and by the driver. On return
+ * from this call the vap is ready for use.
+ */
+int
+ieee80211_vap_attach(struct ieee80211vap *vap,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifmediareq imr;
+ int maxrate;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s parent %s flags 0x%x flags_ext 0x%x\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext);
- bpfdetach(ifp);
+ /*
+ * Do late attach work that cannot happen until after
+ * the driver has had a chance to override defaults.
+ */
+ ieee80211_node_latevattach(vap);
+ ieee80211_power_latevattach(vap);
+
+ maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps,
+ vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat);
+ ieee80211_media_status(ifp, &imr);
+ /* NB: strip explicit mode; we're actually in autoselect */
+ ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK);
+ if (maxrate)
+ ifp->if_baudrate = IF_Mbps(maxrate);
+
+ ether_ifattach(ifp, vap->iv_myaddr);
+ /* hook output method setup by ether_ifattach */
+ vap->iv_output = ifp->if_output;
+ ifp->if_output = ieee80211_output;
+ /* NB: if_mtu set by ether_ifattach to ETHERMTU */
+ bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf);
+
+ IEEE80211_LOCK(ic);
+ TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ return 1;
+}
+
+/*
+ * Tear down vap state and reclaim the ifnet.
+ * The driver is assumed to have prepared for
+ * this; e.g. by turning off interrupts for the
+ * underlying device.
+ */
+void
+ieee80211_vap_detach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname);
+
+ IEEE80211_LOCK(ic);
+ /* block traffic from above */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ /*
+ * Evil hack. Clear the backpointer from the ifnet to the
+ * vap so any requests from above will return an error or
+ * be ignored. In particular this short-circuits requests
+ * by the bridge to turn off promiscuous mode as a result
+ * of calling ether_ifdetach.
+ */
+ ifp->if_softc = NULL;
+ /*
+ * Stop the vap before detaching the ifnet. Ideally we'd
+ * do this in the other order so the ifnet is inaccessible
+ * while we cleanup internal state but that is hard.
+ */
+ ieee80211_stop_locked(vap);
+
+ /* XXX accumulate iv_stats in ic_stats? */
+ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ /* XXX can't hold com lock */
+ /* NB: bpfattach is called by ether_ifdetach and claims all taps */
ether_ifdetach(ifp);
+
+ ifmedia_removeall(&vap->iv_media);
+
+ ieee80211_regdomain_vdetach(vap);
+ ieee80211_scan_vdetach(vap);
+ ieee80211_ht_vdetach(vap);
+ /* NB: must be before ieee80211_node_vdetach */
+ ieee80211_proto_vdetach(vap);
+ ieee80211_crypto_vdetach(vap);
+ ieee80211_power_vdetach(vap);
+ ieee80211_node_vdetach(vap);
+ ieee80211_sysctl_vdetach(vap);
+}
+
+/*
+ * Synchronize flag bit state in the parent ifnet structure
+ * according to the state of all vap ifnet's. This is used,
+ * for example, to handle IFF_PROMISC and IFF_ALLMULTI.
+ */
+void
+ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ int bit, oflags;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & flag) {
+ /*
+ * XXX the bridge sets PROMISC but we don't want to
+ * enable it on the device, discard here so all the
+ * drivers don't need to special-case it
+ */
+ if (flag == IFF_PROMISC &&
+ vap->iv_opmode == IEEE80211_M_HOSTAP)
+ continue;
+ bit = 1;
+ break;
+ }
+ oflags = ifp->if_flags;
+ if (bit)
+ ifp->if_flags |= flag;
+ else
+ ifp->if_flags &= ~flag;
+ if ((ifp->if_flags ^ oflags) & flag) {
+ /* XXX should we return 1/0 and let caller do this? */
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (flag == IFF_PROMISC)
+ ic->ic_update_promisc(ifp);
+ else if (flag == IFF_ALLMULTI)
+ ic->ic_update_mcast(ifp);
+ }
+ }
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags |= flag;
+ else
+ ic->ic_flags &= ~flag;
+}
+
+void
+ieee80211_syncflag(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags &= ~flag;
+ } else
+ vap->iv_flags |= flag;
+ ieee80211_syncflag_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags_ext & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags_ext |= flag;
+ else
+ ic->ic_flags_ext &= ~flag;
+}
+
+void
+ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags_ext &= ~flag;
+ } else
+ vap->iv_flags_ext |= flag;
+ ieee80211_syncflag_ext_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
}
static __inline int
@@ -416,7 +757,7 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
/*
* Locate a channel given a frequency+flags. We cache
- * the previous lookup to optimize swithing between two
+ * the previous lookup to optimize switching between two
* channels--as happens with dynamic turbo.
*/
struct ieee80211_channel *
@@ -467,80 +808,58 @@ ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
}
static void
-addmedia(struct ieee80211com *ic, int mode, int mword)
+addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
{
-#define TURBO(m) ((m) | IFM_IEEE80211_TURBO)
#define ADD(_ic, _s, _o) \
- ifmedia_add(&(_ic)->ic_media, \
+ ifmedia_add(media, \
IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
static const u_int mopts[IEEE80211_MODE_MAX] = {
- IFM_AUTO, /* IEEE80211_MODE_AUTO */
- IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */
- IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */
- IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */
- IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */
- TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */
- IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */
- IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */
+ IFM_AUTO,
+ IFM_IEEE80211_11A,
+ IFM_IEEE80211_11B,
+ IFM_IEEE80211_11G,
+ IFM_IEEE80211_FH,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11NA,
+ IFM_IEEE80211_11NG,
};
u_int mopt;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
mopt = mopts[mode];
- KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
- ("no media mapping for mode %u", mode));
-
- ADD(ic, mword, mopt); /* e.g. 11a auto */
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (addsta)
+ ADD(ic, mword, mopt); /* STA mode has no cap */
+ if (caps & IEEE80211_C_IBSS)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC);
+ if (caps & IEEE80211_C_HOSTAP)
+ ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP);
+ if (caps & IEEE80211_C_AHDEMO)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+ if (caps & IEEE80211_C_MONITOR)
+ ADD(media, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (caps & IEEE80211_C_WDS)
+ ADD(media, mword, mopt | IFM_IEEE80211_WDS);
#undef ADD
-#undef TURBO
}
/*
* Setup the media data structures according to the channel and
- * rate tables. This must be called by the driver after
- * ieee80211_attach and before most anything else.
+ * rate tables.
*/
-void
-ieee80211_media_init(struct ieee80211com *ic,
+static int
+ieee80211_media_setup(struct ieee80211com *ic,
+ struct ifmedia *media, int caps, int addsta,
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
{
- struct ifnet *ifp = ic->ic_ifp;
int i, j, mode, rate, maxrate, mword, r;
const struct ieee80211_rateset *rs;
struct ieee80211_rateset allrates;
- /* NB: this works because the structure is initialized to zero */
- if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
- /*
- * Do late attach work that must wait for any subclass
- * (i.e. driver) work such as overriding methods.
- */
- ieee80211_node_lateattach(ic);
- } else {
- /*
- * We are re-initializing the channel list; clear
- * the existing media state as the media routines
- * don't suppress duplicates.
- */
- ifmedia_removeall(&ic->ic_media);
- ieee80211_chan_init(ic);
- }
- ieee80211_power_lateattach(ic);
-
/*
* Fill in media characteristics.
*/
- ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
+ ifmedia_init(media, 0, media_change, media_stat);
maxrate = 0;
/*
* Add media for legacy operating modes.
@@ -549,7 +868,7 @@ ieee80211_media_init(struct ieee80211com *ic,
for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
if (mode == IEEE80211_MODE_AUTO)
continue;
rs = &ic->ic_sup_rates[mode];
@@ -558,7 +877,7 @@ ieee80211_media_init(struct ieee80211com *ic,
mword = ieee80211_rate2media(ic, rate, mode);
if (mword == 0)
continue;
- addmedia(ic, mode, mword);
+ addmedia(media, caps, addsta, mode, mword);
/*
* Add legacy rate to the collection of all rates.
*/
@@ -582,7 +901,8 @@ ieee80211_media_init(struct ieee80211com *ic,
if (mword == 0)
continue;
/* NB: remove media options from mword */
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
}
/*
* Add HT/11n media. Note that we do not have enough
@@ -593,24 +913,51 @@ ieee80211_media_init(struct ieee80211com *ic,
for (; mode < IEEE80211_MODE_MAX; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
- addmedia(ic, mode, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS);
}
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
/* XXX could walk htrates */
/* XXX known array size */
- if (ieee80211_htrates[15] > maxrate)
- maxrate = ieee80211_htrates[15];
+ if (ieee80211_htrates[15].ht40_rate_400ns > maxrate)
+ maxrate = ieee80211_htrates[15].ht40_rate_400ns;
+ }
+ return maxrate;
+}
+
+void
+ieee80211_media_init(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int maxrate;
+
+ /* NB: this works because the structure is initialized to zero */
+ if (!LIST_EMPTY(&ic->ic_media.ifm_list)) {
+ /*
+ * We are re-initializing the channel list; clear
+ * the existing media state as the media routines
+ * don't suppress duplicates.
+ */
+ ifmedia_removeall(&ic->ic_media);
}
+ ieee80211_chan_init(ic);
+ /*
+ * Recalculate media settings in case new channel list changes
+ * the set of available modes.
+ */
+ maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1,
+ ieee80211com_media_change, ieee80211com_media_status);
/* NB: strip explicit mode; we're actually in autoselect */
ifmedia_set(&ic->ic_media,
media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
-
if (maxrate)
ifp->if_baudrate = IF_Mbps(maxrate);
+
+ /* XXX need to propagate new media settings to vap's */
}
const struct ieee80211_rateset *
@@ -701,216 +1048,133 @@ ieee80211_announce_channels(struct ieee80211com *ic)
}
}
-/*
- * Find an instance by it's mac address.
- */
-struct ieee80211com *
-ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
- return ic;
- return NULL;
-}
-
-static struct ieee80211com *
-ieee80211_find_instance(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- /* XXX not right for multiple instances but works for now */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (ic->ic_ifp == ifp)
- return ic;
- return NULL;
-}
-
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
-{
-#define IEEERATE(_ic,_m,_i) \
- ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
- for (i = 0; i < nrates; i++)
- if (IEEERATE(ic, mode, i) == rate)
- return i;
- return -1;
-#undef IEEERATE
-}
-
-/*
- * Convert a media specification to a rate index and possibly a mode
- * (if the rate is fixed and the mode is specified as ``auto'' then
- * we need to lock down the mode so the index is meanginful).
- */
static int
-checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+media2mode(const struct ieee80211com *ic,
+ const struct ifmedia_entry *ime, enum ieee80211_phymode *mode)
{
-
- /*
- * Check the rate table for the specified/current phy.
- */
- if (mode == IEEE80211_MODE_AUTO) {
- int i;
- /*
- * In autoselect mode search for the rate.
- */
- for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
- if (isset(ic->ic_modecaps, i) &&
- findrate(ic, i, rate) != -1)
- return 1;
- }
- return 0;
- } else {
- /*
- * Mode is fixed, check for rate.
- */
- return (findrate(ic, mode, rate) != -1);
- }
-}
-
-/*
- * Handle a media change request.
- */
-int
-ieee80211_media_change(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
- struct ifmedia_entry *ime;
- enum ieee80211_opmode newopmode;
- enum ieee80211_phymode newphymode;
- int newrate, error = 0;
-
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return EINVAL;
- }
- ime = ic->ic_media.ifm_cur;
- /*
- * First, identify the phy mode.
- */
switch (IFM_MODE(ime->ifm_media)) {
case IFM_IEEE80211_11A:
- newphymode = IEEE80211_MODE_11A;
+ *mode = IEEE80211_MODE_11A;
break;
case IFM_IEEE80211_11B:
- newphymode = IEEE80211_MODE_11B;
+ *mode = IEEE80211_MODE_11B;
break;
case IFM_IEEE80211_11G:
- newphymode = IEEE80211_MODE_11G;
+ *mode = IEEE80211_MODE_11G;
break;
case IFM_IEEE80211_FH:
- newphymode = IEEE80211_MODE_FH;
+ *mode = IEEE80211_MODE_FH;
break;
case IFM_IEEE80211_11NA:
- newphymode = IEEE80211_MODE_11NA;
+ *mode = IEEE80211_MODE_11NA;
break;
case IFM_IEEE80211_11NG:
- newphymode = IEEE80211_MODE_11NG;
+ *mode = IEEE80211_MODE_11NG;
break;
case IFM_AUTO:
- newphymode = IEEE80211_MODE_AUTO;
+ *mode = IEEE80211_MODE_AUTO;
break;
default:
- return EINVAL;
+ return 0;
}
/*
* Turbo mode is an ``option''.
* XXX does not apply to AUTO
*/
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
- if (newphymode == IEEE80211_MODE_11A) {
+ if (*mode == IEEE80211_MODE_11A) {
if (ic->ic_flags & IEEE80211_F_TURBOP)
- newphymode = IEEE80211_MODE_TURBO_A;
+ *mode = IEEE80211_MODE_TURBO_A;
else
- newphymode = IEEE80211_MODE_STURBO_A;
- } else if (newphymode == IEEE80211_MODE_11G)
- newphymode = IEEE80211_MODE_TURBO_G;
+ *mode = IEEE80211_MODE_STURBO_A;
+ } else if (*mode == IEEE80211_MODE_11G)
+ *mode = IEEE80211_MODE_TURBO_G;
else
- return EINVAL;
+ return 0;
}
/* XXX HT40 +/- */
- /*
- * Next, the fixed/variable rate.
- */
- newrate = ic->ic_fixed_rate;
- if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
- /*
- * Convert media subtype to rate.
- */
- newrate = ieee80211_media2rate(ime->ifm_media);
- if (newrate == 0 || !checkrate(ic, newphymode, newrate))
- return EINVAL;
- } else
- newrate = IEEE80211_FIXED_RATE_NONE;
+ return 1;
+}
+
+/*
+ * Handle a media change request on the underlying
+ * interface; we accept mode changes only.
+ */
+int
+ieee80211com_media_change(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
+ enum ieee80211_phymode newphymode;
+ int error = 0;
/*
- * Deduce new operating mode but don't install it just yet.
+ * First, identify the phy mode.
*/
- if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
- (IFM_IEEE80211_ADHOC|IFM_FLAG0))
- newopmode = IEEE80211_M_AHDEMO;
- else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
- newopmode = IEEE80211_M_HOSTAP;
- else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
- newopmode = IEEE80211_M_IBSS;
- else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
- newopmode = IEEE80211_M_MONITOR;
- else
- newopmode = IEEE80211_M_STA;
+ if (!media2mode(ic, ime, &newphymode))
+ return EINVAL;
+ /* NB: mode must be supported, no need to check */
/*
* Handle phy mode change.
*/
- if (ic->ic_des_mode != newphymode) { /* change phy mode */
- ic->ic_des_mode = newphymode;
- error = ENETRESET;
- }
+ IEEE80211_LOCK(ic);
+ if (ic->ic_curmode != newphymode) { /* change phy mode */
+ struct ieee80211vap *vap;
- /*
- * Committed to changes, install the rate setting.
- */
- if (ic->ic_fixed_rate != newrate) {
- ic->ic_fixed_rate = newrate; /* set fixed tx rate */
- error = ENETRESET;
+ (void) ieee80211_setmode(ic, newphymode);
+ /*
+ * Propagate new state to each vap.
+ */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ }
}
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
- /*
- * Handle operating mode change.
- */
- if (ic->ic_opmode != newopmode) {
- ic->ic_opmode = newopmode;
- switch (newopmode) {
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_STA:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- ic->ic_flags &= ~IEEE80211_F_IBSSON;
- break;
- case IEEE80211_M_IBSS:
- ic->ic_flags |= IEEE80211_F_IBSSON;
- break;
- }
+static int
+findrate(const struct ieee80211com *ic, enum ieee80211_phymode m, int r)
+{
+ int i, nrates;
+
+ for (i = 0, nrates = ic->ic_sup_rates[m].rs_nrates; i < nrates; i++)
+ if ((ic->ic_sup_rates[m].rs_rates[i] & IEEE80211_RATE_VAL) == r)
+ return i;
+ return -1;
+}
+
+/*
+ * Handle a media change request on the vap interface.
+ */
+int
+ieee80211_media_change(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
+ struct ieee80211com *ic = vap->iv_ic;
+ int newrate;
+
+ /* XXX this won't work unless ic_curmode is != IEEE80211_MODE_AUTO */
+ if (ic->ic_curmode == IEEE80211_MODE_AUTO)
+ return EINVAL;
+ if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
/*
- * Yech, slot time may change depending on the
- * operating mode so reset it to be sure everything
- * is setup appropriately.
+ * NB: this can only be used to specify a legacy rate.
*/
- ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic); /* after opmode change */
- error = ENETRESET;
+ newrate = ieee80211_media2rate(ime->ifm_media);
+ if (newrate == 0)
+ return EINVAL;
+ if (findrate(ic, ic->ic_curmode, newrate) == -1)
+ return EINVAL;
+ } else {
+ newrate = IEEE80211_FIXED_RATE_NONE;
}
-#ifdef notdef
- if (error == 0)
- ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
-#endif
- return error;
+ if (newrate != vap->iv_txparms[ic->ic_curmode].ucastrate) {
+ vap->iv_txparms[ic->ic_curmode].ucastrate = newrate;
+ return ENETRESET;
+ }
+ return 0;
}
/*
@@ -939,7 +1203,7 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
break;
case IEEE80211_M_WDS:
- /* should not come here */
+ status |= IFM_IEEE80211_WDS;
break;
}
if (IEEE80211_IS_CHAN_HTA(chan)) {
@@ -959,53 +1223,70 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
if (IEEE80211_IS_CHAN_TURBO(chan))
status |= IFM_IEEE80211_TURBO;
-
+#if 0
+ if (IEEE80211_IS_CHAN_HT20(chan))
+ status |= IFM_IEEE80211_HT20;
+ if (IEEE80211_IS_CHAN_HT40(chan))
+ status |= IFM_IEEE80211_HT40;
+#endif
return status;
}
+static void
+ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap;
+
+ imr->ifm_status = IFM_AVALID;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & IFF_UP) {
+ imr->ifm_status |= IFM_ACTIVE;
+ break;
+ }
+ imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
+}
+
void
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ieee80211com *ic;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_phymode mode;
- const struct ieee80211_rateset *rs;
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return;
- }
imr->ifm_status = IFM_AVALID;
/*
* NB: use the current channel's mode to lock down a xmit
* rate only when running; otherwise we may have a mismatch
* in which case the rate will not be convertible.
*/
- if (ic->ic_state == IEEE80211_S_RUN) {
+ if (vap->iv_state == IEEE80211_S_RUN) {
imr->ifm_status |= IFM_ACTIVE;
mode = ieee80211_chan2mode(ic->ic_curchan);
} else
mode = IEEE80211_MODE_AUTO;
- imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan);
/*
* Calculate a current rate if possible.
*/
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+ if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) {
/*
* A fixed rate is set, report that.
*/
imr->ifm_active |= ieee80211_rate2media(ic,
- ic->ic_fixed_rate, mode);
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
+ vap->iv_txparms[mode].ucastrate, mode);
+ } else if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* In station mode report the current transmit rate.
- * XXX HT rate
*/
- rs = &ic->ic_bss->ni_rates;
imr->ifm_active |= ieee80211_rate2media(ic,
- rs->rs_rates[ic->ic_bss->ni_txrate], mode);
+ vap->iv_bss->ni_txrate, mode);
} else
imr->ifm_active |= IFM_AUTO;
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
}
/*
@@ -1024,11 +1305,10 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
* and used instead.
*/
if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
- ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode);
+ ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode);
ic->ic_curmode = mode;
ieee80211_reset_erp(ic); /* reset ERP state */
- ieee80211_wme_initparams(ic); /* reset WME stat */
return 0;
}
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index 97f9365..3e91068 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -696,9 +696,13 @@ struct ieee80211_country_ie {
uint8_t schan; /* starting channel */
uint8_t nchan; /* number channels */
uint8_t maxtxpwr; /* tx power cap */
- } __packed band[10]; /* sub bands */
+ } __packed band[1]; /* sub bands (NB: var size) */
} __packed;
+#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */
+#define IEEE80211_COUNTRY_MAX_SIZE \
+ (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1))
+
/*
* 802.11h Channel Switch Announcement (CSA).
*/
@@ -889,6 +893,9 @@ enum {
#define IEEE80211_WEP_IVLEN 3 /* 24bit */
#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \
+ IEEE80211_WEP_KIDLEN + \
+ IEEE80211_WEP_CRCLEN)
#define IEEE80211_WEP_NKID 4 /* number of key ids */
/*
@@ -924,12 +931,12 @@ enum {
/*
* The 802.11 spec says at most 2007 stations may be
* associated at once. For most AP's this is way more
- * than is feasible so we use a default of 128. This
- * number may be overridden by the driver and/or by
- * user configuration.
+ * than is feasible so we use a default of IEEE80211_AID_DEF.
+ * This number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN
+ * (see _ieee80211.h for implementation-specific settings).
*/
#define IEEE80211_AID_MAX 2007
-#define IEEE80211_AID_DEF 128
#define IEEE80211_AID(b) ((b) &~ 0xc000)
diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c
index c5305d0..13407a4 100644
--- a/sys/net80211/ieee80211_acl.c
+++ b/sys/net80211/ieee80211_acl.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 MAC ACL support.
*
- * When this module is loaded the sender address of each received
+ * When this module is loaded the sender address of each auth mgt
* frame is passed to the iac_check method and the module indicates
* if the frame should be accepted or rejected. If the policy is
* set to ACL_POLICY_OPEN then all frames are accepted w/o checking
@@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
* and if found the frame is either accepted (ACL_POLICY_ALLOW)
* or rejected (ACL_POLICY_DENT).
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -57,6 +59,12 @@ enum {
ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
ACL_POLICY_DENY = 2, /* deny traffic from MAC */
+ /*
+ * NB: ACL_POLICY_RADIUS must be the same value as
+ * IEEE80211_MACCMD_POLICY_RADIUS because of the way
+ * acl_getpolicy() works.
+ */
+ ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */
};
#define ACL_HASHSIZE 32
@@ -72,7 +80,7 @@ struct aclstate {
int as_nacls;
TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
- struct ieee80211com *as_ic;
+ struct ieee80211vap *as_vap;
};
/* simple hash is enough for variation of macaddr */
@@ -81,10 +89,13 @@ struct aclstate {
MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
-static int acl_free_all(struct ieee80211com *);
+static int acl_free_all(struct ieee80211vap *);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
static int
-acl_attach(struct ieee80211com *ic)
+acl_attach(struct ieee80211vap *vap)
{
struct aclstate *as;
@@ -95,20 +106,24 @@ acl_attach(struct ieee80211com *ic)
ACL_LOCK_INIT(as, "acl");
TAILQ_INIT(&as->as_list);
as->as_policy = ACL_POLICY_OPEN;
- as->as_ic = ic;
- ic->ic_as = as;
+ as->as_vap = vap;
+ vap->iv_as = as;
+ nrefs++; /* NB: we assume caller locking */
return 1;
}
static void
-acl_detach(struct ieee80211com *ic)
+acl_detach(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- acl_free_all(ic);
- ic->ic_as = NULL;
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+
+ acl_free_all(vap);
+ vap->iv_as = NULL;
ACL_LOCK_DESTROY(as);
- FREE(as, M_DEVBUF);
+ FREE(as, M_80211_ACL);
}
static __inline struct acl *
@@ -137,12 +152,13 @@ _acl_free(struct aclstate *as, struct acl *acl)
}
static int
-acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
switch (as->as_policy) {
case ACL_POLICY_OPEN:
+ case ACL_POLICY_RADIUS:
return 1;
case ACL_POLICY_ALLOW:
return _find_acl(as, mac) != NULL;
@@ -153,15 +169,15 @@ acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl, *new;
int hash;
MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
if (new == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
/* XXX statistic */
return ENOMEM;
@@ -173,7 +189,7 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
ACL_UNLOCK(as);
FREE(new, M_80211_ACL);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, already present\n",
ether_sprintf(mac));
return EEXIST;
@@ -185,15 +201,15 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
as->as_nacls++;
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s\n", ether_sprintf(mac));
return 0;
}
static int
-acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
ACL_LOCK(as);
@@ -202,7 +218,7 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
_acl_free(as, acl);
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: remove %s%s\n", ether_sprintf(mac),
acl == NULL ? ", not present" : "");
@@ -210,12 +226,12 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_free_all(struct ieee80211com *ic)
+acl_free_all(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
ACL_LOCK(as);
while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
@@ -226,11 +242,11 @@ acl_free_all(struct ieee80211com *ic)
}
static int
-acl_setpolicy(struct ieee80211com *ic, int policy)
+acl_setpolicy(struct ieee80211vap *vap, int policy)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: set policy to %u\n", policy);
switch (policy) {
@@ -243,6 +259,9 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
case IEEE80211_MACCMD_POLICY_DENY:
as->as_policy = ACL_POLICY_DENY;
break;
+ case IEEE80211_MACCMD_POLICY_RADIUS:
+ as->as_policy = ACL_POLICY_RADIUS;
+ break;
default:
return EINVAL;
}
@@ -250,24 +269,24 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
}
static int
-acl_getpolicy(struct ieee80211com *ic)
+acl_getpolicy(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
return as->as_policy;
}
static int
-acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
return EINVAL;
}
static int
-acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
struct ieee80211req_maclist *ap;
int error, space, i;
@@ -283,7 +302,7 @@ acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0; /* NB: must not error */
}
MALLOC(ap, struct ieee80211req_maclist *, space,
- M_TEMP, M_NOWAIT);
+ M_TEMP, M_NOWAIT);
if (ap == NULL)
return ENOMEM;
i = 0;
@@ -317,31 +336,4 @@ static const struct ieee80211_aclator mac = {
.iac_setioctl = acl_setioctl,
.iac_getioctl = acl_getioctl,
};
-
-/*
- * Module glue.
- */
-static int
-wlan_acl_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan: <802.11 MAC ACL support>\n");
- ieee80211_aclator_register(&mac);
- return 0;
- case MOD_UNLOAD:
- ieee80211_aclator_unregister(&mac);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_acl_mod = {
- "wlan_acl",
- wlan_acl_modevent,
- 0
-};
-DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_acl, 1);
-MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
+IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c
new file mode 100644
index 0000000..ce9c4cb
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.c
@@ -0,0 +1,877 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 IBSS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_input.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void adhoc_vattach(struct ieee80211vap *);
+static int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int adhoc_input(struct ieee80211_node *, struct mbuf *,
+ int rssi, int noise, uint32_t rstamp);
+static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_adhoc_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
+ ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
+}
+
+void
+ieee80211_adhoc_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+adhoc_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+adhoc_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = adhoc_newstate;
+ vap->iv_input = adhoc_input;
+ vap->iv_recv_mgmt = adhoc_recv_mgmt;
+ vap->iv_opdetach = adhoc_vdetach;
+}
+
+/*
+ * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
+ */
+static int
+adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211com *ic = vap->iv_ic;
+#endif
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_RUN: /* beacon miss */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * This can happen because of a change in state
+ * that requires a reset. Trigger a new scan
+ * unless we're in manual roaming mode in which
+ * case an application must issue an explicit request.
+ */
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ goto invalid;
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 1;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ /*
+ * Data frame, cons up a node when it doesn't
+ * exist. This should probably done after an ACL check.
+ */
+ if (type == IEEE80211_FC0_TYPE_DATA &&
+ ni == vap->iv_bss &&
+ !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Fake up a node for this newly
+ * discovered member of the IBSS.
+ */
+ ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: stat kept for alloc failure */
+ goto err;
+ }
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* XXX no power-save support */
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ /* NB: only IBSS mode gets mgt frames */
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response
+ * frames to discover neighbors.
+ */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Create a new entry in the neighbor table.
+ */
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ } else if (ni->ni_capinfo == 0) {
+ /*
+ * Update faked node created on transmit.
+ * Note this also updates the tsf.
+ */
+ ieee80211_init_neighbor(ni, wh, &scan);
+ } else {
+ /*
+ * Record tsf for potential resync.
+ */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ }
+ if (ni != NULL) {
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ return;
+ }
+
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ACTION: {
+ const struct ieee80211_action *ia;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+#undef IEEE80211_VERIFY_LENGTH
+#undef IEEE80211_VERIFY_ELEMENT
diff --git a/sys/net80211/ieee80211_adhoc.h b/sys/net80211/ieee80211_adhoc.h
new file mode 100644
index 0000000..d8e19e5
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_ADHOC_H_
+#define _NET80211_IEEE80211_ADHOC_H_
+
+/*
+ * Adhoc-mode (ibss+ahdemo) implementation definitions.
+ */
+void ieee80211_adhoc_attach(struct ieee80211com *);
+void ieee80211_adhoc_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c
index 5b34768..a759e64 100644
--- a/sys/net80211/ieee80211_amrr.c
+++ b/sys/net80211/ieee80211_amrr.c
@@ -28,6 +28,8 @@ __FBSDID("$FreeBSD$");
* INRIA Sophia - Projet Planete
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
@@ -51,66 +53,90 @@ __FBSDID("$FreeBSD$");
((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
#define is_enough(amn) \
((amn)->amn_txcnt > 10)
-#define is_min_rate(ni) \
- ((ni)->ni_txrate == 0)
-#define is_max_rate(ni) \
- ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
-#define increase_rate(ni) \
- ((ni)->ni_txrate++)
-#define decrease_rate(ni) \
- ((ni)->ni_txrate--)
-#define reset_cnt(amn) \
- do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+static void amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_amrr_setinterval(struct ieee80211_amrr *amrr, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ amrr->amrr_interval = (t < 1) ? 1 : t;
+}
void
ieee80211_amrr_init(struct ieee80211_amrr *amrr,
- struct ieee80211com *ic, int amin, int amax)
+ struct ieee80211vap *vap, int amin, int amax, int interval)
{
/* XXX bounds check? */
amrr->amrr_min_success_threshold = amin;
amrr->amrr_max_success_threshold = amax;
- amrr->amrr_ic = ic;
+ ieee80211_amrr_setinterval(amrr, interval);
+
+ amrr_sysctlattach(amrr, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_amrr_cleanup(struct ieee80211_amrr *amrr)
+{
}
void
ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
- struct ieee80211_amrr_node *amn)
+ struct ieee80211_amrr_node *amn, struct ieee80211_node *ni)
{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ amn->amn_amrr = amrr;
amn->amn_success = 0;
amn->amn_recovery = 0;
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
+
+ /* pick initial rate */
+ for (amn->amn_rix = rs->rs_nrates - 1;
+ amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
+ amn->amn_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ amn->amn_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR initial rate %d", ni->ni_txrate);
}
-/*
- * Update ni->ni_txrate.
- */
-void
-ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
- struct ieee80211_amrr_node *amn)
+static int
+amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
+ struct ieee80211_node *ni)
{
- int need_change = 0;
+ int rix = amn->amn_rix;
+
+ KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
- if (is_success(amn) && is_enough(amn)) {
+ if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
- !is_max_rate(ni)) {
+ rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
- increase_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR increasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix++;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
} else {
amn->amn_recovery = 0;
}
} else if (is_failure(amn)) {
amn->amn_success = 0;
- if (!is_min_rate(ni)) {
+ if (rix > 0) {
if (amn->amn_recovery) {
amn->amn_success_threshold *= 2;
if (amn->amn_success_threshold >
@@ -121,44 +147,80 @@ ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
amn->amn_success_threshold =
amrr->amrr_min_success_threshold;
}
- decrease_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR decreasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix--;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
}
amn->amn_recovery = 0;
}
- if (is_enough(amn) || need_change)
- reset_cnt(amn);
+ /* reset counters */
+ amn->amn_txcnt = 0;
+ amn->amn_retrycnt = 0;
+
+ return rix;
}
/*
- * Module glue.
+ * Return the rate index to use in sending a data frame.
+ * Update our internal state if it's been long enough.
+ * If the rate changes we also update ni_txrate to match.
*/
+int
+ieee80211_amrr_choose(struct ieee80211_node *ni,
+ struct ieee80211_amrr_node *amn)
+{
+ struct ieee80211_amrr *amrr = amn->amn_amrr;
+ int rix;
+
+ if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
+ rix = amrr_update(amrr, amn, ni);
+ if (rix != amn->amn_rix) {
+ /* update public rate */
+ ni->ni_txrate =
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ amn->amn_rix = rix;
+ }
+ amn->amn_ticks = ticks;
+ } else
+ rix = amn->amn_rix;
+ return rix;
+}
+
static int
-amrr_modevent(module_t mod, int type, void *unused)
+amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n");
- return 0;
- case MOD_UNLOAD:
- return 0;
- }
- return EINVAL;
+ struct ieee80211_amrr *amrr = arg1;
+ int msecs = ticks_to_msecs(amrr->amrr_interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_amrr_setinterval(amrr, msecs);
+ return 0;
+}
+
+static void
+amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, amrr,
+ 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
+ /* XXX bounds check values */
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_max_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_max_success_threshold, 0, "");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_min_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_min_success_threshold, 0, "");
}
-static moduledata_t amrr_mod = {
- "wlan_amrr",
- amrr_modevent,
- 0
-};
-DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_amrr, 1);
-MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1);
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(amrr, 1);
diff --git a/sys/net80211/ieee80211_amrr.h b/sys/net80211/ieee80211_amrr.h
index 947d617..c03f699 100644
--- a/sys/net80211/ieee80211_amrr.h
+++ b/sys/net80211/ieee80211_amrr.h
@@ -32,12 +32,12 @@
/*
* Rate control settings.
*/
-struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_amrr {
u_int amrr_min_success_threshold;
u_int amrr_max_success_threshold;
- struct ieee80211com *amrr_ic;
+ int amrr_interval; /* update interval (ticks) */
};
#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1
@@ -47,18 +47,55 @@ struct ieee80211_amrr {
* Rate control state for a given node.
*/
struct ieee80211_amrr_node {
+ struct ieee80211_amrr *amn_amrr;/* backpointer */
+ int amn_rix; /* current rate index */
+ int amn_ticks; /* time of last update */
+ /* statistics */
+ u_int amn_txcnt;
u_int amn_success;
- u_int amn_recovery;
u_int amn_success_threshold;
- u_int amn_txcnt;
+ u_int amn_recovery;
u_int amn_retrycnt;
};
-void ieee80211_amrr_init(struct ieee80211_amrr *,
- struct ieee80211com *ic, int, int);
+void ieee80211_amrr_init(struct ieee80211_amrr *, struct ieee80211vap *,
+ int, int, int);
+void ieee80211_amrr_cleanup(struct ieee80211_amrr *);
+void ieee80211_amrr_setinterval(struct ieee80211_amrr *, int);
void ieee80211_amrr_node_init(struct ieee80211_amrr *,
+ struct ieee80211_amrr_node *, struct ieee80211_node *);
+int ieee80211_amrr_choose(struct ieee80211_node *,
struct ieee80211_amrr_node *);
-void ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *,
- struct ieee80211_amrr_node *);
+#define IEEE80211_AMRR_SUCCESS 1
+#define IEEE80211_AMRR_FAILURE 0
+
+/*
+ * Update statistics with tx complete status. Ok is non-zero
+ * if the packet is known to be ACK'd. Retries has the number
+ * retransmissions (i.e. xmit attempts - 1).
+ */
+static __inline void
+ieee80211_amrr_tx_complete(struct ieee80211_amrr_node *amn,
+ int ok, int retries)
+{
+ amn->amn_txcnt++;
+ if (ok)
+ amn->amn_success++;
+ amn->amn_retrycnt += retries;
+}
+
+/*
+ * Set tx count/retry statistics explicitly. Intended for
+ * drivers that poll the device for statistics maintained
+ * in the device.
+ */
+static __inline void
+ieee80211_amrr_tx_update(struct ieee80211_amrr_node *amn,
+ int txcnt, int success, int retrycnt)
+{
+ amn->amn_txcnt = txcnt;
+ amn->amn_success = success;
+ amn->amn_retrycnt = retrycnt;
+}
#endif /* _NET80211_IEEE80211_AMRR_H_ */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 83d7c3f..d644d0c 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,11 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
@@ -41,23 +45,25 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state");
+
+static int _ieee80211_crypto_delkey(struct ieee80211vap *,
+ struct ieee80211_key *);
+
/*
* Table of registered cipher modules.
*/
static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
-static int _ieee80211_crypto_delkey(struct ieee80211com *,
- struct ieee80211_key *);
-
/*
* Default "null" key management routines.
*/
static int
-null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- if (!(&ic->ic_nw_keys[0] <= k &&
- k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/*
* Not in the global key table, the driver should handle this
* by allocating a slot in the h/w key table/cache. In
@@ -72,23 +78,23 @@ null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
return 0;
*keyix = 0; /* NB: use key index 0 for ucast key */
} else {
- *keyix = k - ic->ic_nw_keys;
+ *keyix = k - vap->iv_nw_keys;
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
return 1;
}
static int
-null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return 1;
}
static int
-null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
return 1;
}
-static void null_key_update(struct ieee80211com *ic) {}
+static void null_key_update(struct ieee80211vap *vap) {}
/*
* Write-arounds for common operations.
@@ -100,70 +106,86 @@ cipher_detach(struct ieee80211_key *key)
}
static __inline void *
-cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key)
{
- return key->wk_cipher->ic_attach(ic, key);
+ return key->wk_cipher->ic_attach(vap, key);
}
/*
* Wrappers for driver key management methods.
*/
static __inline int
-dev_key_alloc(struct ieee80211com *ic,
+dev_key_alloc(struct ieee80211vap *vap,
const struct ieee80211_key *key,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix);
+ return vap->iv_key_alloc(vap, key, keyix, rxkeyix);
}
static __inline int
-dev_key_delete(struct ieee80211com *ic,
+dev_key_delete(struct ieee80211vap *vap,
const struct ieee80211_key *key)
{
- return ic->ic_crypto.cs_key_delete(ic, key);
+ return vap->iv_key_delete(vap, key);
}
static __inline int
-dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
- return ic->ic_crypto.cs_key_set(ic, key, mac);
+ return vap->iv_key_set(vap, key, mac);
}
/*
- * Setup crypto support.
+ * Setup crypto support for a device/shared instance.
*/
void
ieee80211_crypto_attach(struct ieee80211com *ic)
{
- struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+ /* NB: we assume everything is pre-zero'd */
+ ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+}
+
+/*
+ * Teardown crypto support.
+ */
+void
+ieee80211_crypto_detach(struct ieee80211com *ic)
+{
+}
+
+/*
+ * Setup crypto support for a vap.
+ */
+void
+ieee80211_crypto_vattach(struct ieee80211vap *vap)
+{
int i;
/* NB: we assume everything is pre-zero'd */
- cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
- cs->cs_max_keyix = IEEE80211_WEP_NKID;
- ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+ vap->iv_max_keyix = IEEE80211_WEP_NKID;
+ vap->iv_def_txkey = IEEE80211_KEYIX_NONE;
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
+ ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i],
IEEE80211_KEYIX_NONE);
/*
* Initialize the driver key support routines to noop entries.
* This is useful especially for the cipher test modules.
*/
- cs->cs_key_alloc = null_key_alloc;
- cs->cs_key_set = null_key_set;
- cs->cs_key_delete = null_key_delete;
- cs->cs_key_update_begin = null_key_update;
- cs->cs_key_update_end = null_key_update;
+ vap->iv_key_alloc = null_key_alloc;
+ vap->iv_key_set = null_key_set;
+ vap->iv_key_delete = null_key_delete;
+ vap->iv_key_update_begin = null_key_update;
+ vap->iv_key_update_end = null_key_update;
}
/*
- * Teardown crypto support.
+ * Teardown crypto support for a vap.
*/
void
-ieee80211_crypto_detach(struct ieee80211com *ic)
+ieee80211_crypto_vdetach(struct ieee80211vap *vap)
{
- ieee80211_crypto_delglobalkeys(ic);
+ ieee80211_crypto_delglobalkeys(vap);
}
/*
@@ -213,12 +235,14 @@ ieee80211_crypto_available(u_int cipher)
}
/* XXX well-known names! */
-static const char *cipher_modnames[] = {
+static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = {
"wlan_wep", /* IEEE80211_CIPHER_WEP */
"wlan_tkip", /* IEEE80211_CIPHER_TKIP */
"wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */
"wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */
+ "#4", /* reserved */
"wlan_ckip", /* IEEE80211_CIPHER_CKIP */
+ "wlan_none", /* IEEE80211_CIPHER_NONE */
};
/*
@@ -231,14 +255,14 @@ static const char *cipher_modnames[] = {
* routines assume wk_cipher is setup.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_newkey(struct ieee80211com *ic,
+ieee80211_crypto_newkey(struct ieee80211vap *vap,
int cipher, int flags, struct ieee80211_key *key)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_cipher *cip;
ieee80211_keyix keyix, rxkeyix;
void *keyctx;
@@ -248,9 +272,9 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* Validate cipher and set reference to cipher routines.
*/
if (cipher >= IEEE80211_CIPHER_MAX) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: invalid cipher %u\n", __func__, cipher);
- ic->ic_stats.is_crypto_badcipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: invalid cipher %u\n", __func__, cipher);
+ vap->iv_stats.is_crypto_badcipher++;
return 0;
}
cip = ciphers[cipher];
@@ -261,25 +285,21 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* than numbers and craft a module name based on the cipher
* name; e.g. wlan_cipher_<cipher-name>.
*/
- if (cipher < N(cipher_modnames)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unregistered cipher %u, load module %s\n",
- __func__, cipher, cipher_modnames[cipher]);
- ieee80211_load_module(cipher_modnames[cipher]);
- /*
- * If cipher module loaded it should immediately
- * call ieee80211_crypto_register which will fill
- * in the entry in the ciphers array.
- */
- cip = ciphers[cipher];
- }
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unregistered cipher %u, load module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ ieee80211_load_module(cipher_modnames[cipher]);
+ /*
+ * If cipher module loaded it should immediately
+ * call ieee80211_crypto_register which will fill
+ * in the entry in the ciphers array.
+ */
+ cip = ciphers[cipher];
if (cip == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unable to load cipher %u, module %s\n",
- __func__, cipher,
- cipher < N(cipher_modnames) ?
- cipher_modnames[cipher] : "<unknown>");
- ic->ic_stats.is_crypto_nocipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unable to load cipher %u, module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ vap->iv_stats.is_crypto_nocipher++;
return 0;
}
}
@@ -290,8 +310,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* If the hardware does not support the cipher then
* fallback to a host-based implementation.
*/
- if ((ic->ic_caps & (1<<cipher)) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if ((ic->ic_cryptocaps & (1<<cipher)) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for cipher %s, falling back to s/w\n",
__func__, cip->ic_name);
flags |= IEEE80211_KEY_SWCRYPT;
@@ -302,8 +322,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* the cipher modules honor it.
*/
if (cipher == IEEE80211_CIPHER_TKIP &&
- (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ (ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for TKIP MIC, falling back to s/w\n",
__func__);
flags |= IEEE80211_KEY_SWMIC;
@@ -327,13 +347,13 @@ again:
* fails and we try to restore previous state.
*/
key->wk_flags = flags;
- keyctx = cip->ic_attach(ic, key);
+ keyctx = cip->ic_attach(vap, key);
if (keyctx == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to attach cipher %s\n",
__func__, cip->ic_name);
key->wk_flags = oflags; /* restore old flags */
- ic->ic_stats.is_crypto_attachfail++;
+ vap->iv_stats.is_crypto_attachfail++;
return 0;
}
cipher_detach(key);
@@ -354,7 +374,7 @@ again:
* crypto we also call the driver to give us a key index.
*/
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
- if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) {
+ if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) {
/*
* Driver has no room; fallback to doing crypto
* in the host. We change the flags and start the
@@ -364,8 +384,8 @@ again:
* continues to use it.
*/
if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
- ic->ic_stats.is_crypto_swfallback++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_swfallback++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w resources for cipher %s, "
"falling back to s/w\n", __func__,
cip->ic_name);
@@ -375,8 +395,8 @@ again:
flags |= IEEE80211_KEY_SWMIC;
goto again;
}
- ic->ic_stats.is_crypto_keyfail++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_keyfail++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to setup cipher %s\n",
__func__, cip->ic_name);
return 0;
@@ -385,24 +405,24 @@ again:
key->wk_rxkeyix = rxkeyix;
}
return 1;
-#undef N
}
/*
* Remove the key (no locking, for internal use).
*/
static int
-_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+_ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
ieee80211_keyix keyix;
KASSERT(key->wk_cipher != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
__func__, key->wk_cipher->ic_name,
key->wk_keyix, key->wk_flags,
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
keyix = key->wk_keyix;
if (keyix != IEEE80211_KEYIX_NONE) {
@@ -410,17 +430,17 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove hardware entry.
*/
/* XXX key cache */
- if (!dev_key_delete(ic, key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if (!dev_key_delete(vap, key)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: driver did not delete key index %u\n",
__func__, keyix);
- ic->ic_stats.is_crypto_delkey++;
+ vap->iv_stats.is_crypto_delkey++;
/* XXX recovery? */
}
}
cipher_detach(key);
memset(key, 0, sizeof(*key));
- ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE);
return 1;
}
@@ -428,13 +448,13 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove the specified key.
*/
int
-ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
int status;
- ieee80211_key_update_begin(ic);
- status = _ieee80211_crypto_delkey(ic, key);
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_begin(vap);
+ status = _ieee80211_crypto_delkey(vap, key);
+ ieee80211_key_update_end(vap);
return status;
}
@@ -442,66 +462,67 @@ ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Clear the global key table.
*/
void
-ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap)
{
int i;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
- ieee80211_key_update_end(ic);
+ (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]);
+ ieee80211_key_update_end(vap);
}
/*
* Set the contents of the specified key.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
const struct ieee80211_cipher *cip = key->wk_cipher;
KASSERT(cip != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_flags, ether_sprintf(macaddr),
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
/*
* Give cipher a chance to validate key contents.
* XXX should happen before modifying state.
*/
if (!cip->ic_setkey(key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: cipher %s rejected key index %u len %u flags 0x%x\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_keylen, key->wk_flags);
- ic->ic_stats.is_crypto_setkey_cipher++;
+ vap->iv_stats.is_crypto_setkey_cipher++;
return 0;
}
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
/* XXX nothing allocated, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no key index; should not happen!\n", __func__);
- ic->ic_stats.is_crypto_setkey_nokey++;
+ vap->iv_stats.is_crypto_setkey_nokey++;
return 0;
}
- return dev_key_set(ic, key, macaddr);
+ return dev_key_set(vap, key, macaddr);
}
/*
* Add privacy headers appropriate for the specified key.
*/
struct ieee80211_key *
-ieee80211_crypto_encap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
@@ -516,16 +537,16 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
wh = mtod(m, struct ieee80211_frame *);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(wh->i_addr1), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr1,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
return NULL;
}
- keyid = ic->ic_def_txkey;
- k = &ic->ic_nw_keys[ic->ic_def_txkey];
+ keyid = vap->iv_def_txkey;
+ k = &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
keyid = 0;
k = &ni->ni_ucastkey;
@@ -539,25 +560,24 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
* received frame that has the WEP/Privacy bit set.
*/
struct ieee80211_key *
-ieee80211_crypto_decap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
+ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
{
#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
(sizeof(struct ieee80211_frame) + \
IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
- const uint8_t *ivp;
uint8_t keyid;
/* NB: this minimum size data frame could be bigger */
if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
"%s: WEP data frame too short, len %u\n",
__func__, m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */
+ vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */
return NULL;
}
@@ -568,11 +588,10 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
* the key id in the header is meaningless (typically 0).
*/
wh = mtod(m, struct ieee80211_frame *);
- ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */
- keyid = ivp[IEEE80211_WEP_IVLEN];
+ m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
- k = &ic->ic_nw_keys[keyid >> 6];
+ k = &vap->iv_nw_keys[keyid >> 6];
else
k = &ni->ni_ucastkey;
@@ -582,10 +601,9 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
cip = k->wk_cipher;
if (m->m_len < hdrlen + cip->ic_header &&
(m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] unable to pullup %s header\n",
- ether_sprintf(wh->i_addr2), cip->ic_name);
- ic->ic_stats.is_rx_wepfail++; /* XXX */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "unable to pullup %s header", cip->ic_name);
+ vap->iv_stats.is_rx_wepfail++; /* XXX */
return NULL;
}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index ee99caa..999f139 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,15 @@ struct ieee80211_wepkey {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
};
+struct ieee80211_rsnparms {
+ uint8_t rsn_mcastcipher; /* mcast/group cipher */
+ uint8_t rsn_mcastkeylen; /* mcast key length */
+ uint8_t rsn_ucastcipher; /* selected unicast cipher */
+ uint8_t rsn_ucastkeylen; /* unicast key length */
+ uint8_t rsn_keymgmt; /* selected key mgmt algo */
+ uint16_t rsn_caps; /* capabilities */
+};
+
struct ieee80211_cipher;
/*
@@ -76,7 +85,8 @@ struct ieee80211_key {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */
#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */
- uint64_t wk_keyrsc; /* key receive sequence counter */
+ /* key receive sequence counter */
+ uint64_t wk_keyrsc[IEEE80211_TID_SIZE];
uint64_t wk_keytsc; /* key transmit sequence counter */
const struct ieee80211_cipher *wk_cipher;
void *wk_private; /* private cipher state */
@@ -84,61 +94,54 @@ struct ieee80211_key {
#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\
(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
+#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+
/*
* NB: these values are ordered carefully; there are lots of
- * of implications in any reordering. In particular beware
- * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ * of implications in any reordering. Beware that 4 is used
+ * only to indicate h/w TKIP MIC support in driver capabilities;
+ * there is no separate cipher support (it's rolled into the
+ * TKIP cipher support).
*/
#define IEEE80211_CIPHER_WEP 0
#define IEEE80211_CIPHER_TKIP 1
#define IEEE80211_CIPHER_AES_OCB 2
#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */
#define IEEE80211_CIPHER_CKIP 5
#define IEEE80211_CIPHER_NONE 6 /* pseudo value */
#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
-#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+/* capability bits in ic_cryptocaps/iv_cryptocaps */
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_OCB (1<<IEEE80211_CIPHER_AES_OCB)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_CRYPTO_TKIPMIC (1<<IEEE80211_CIPHER_TKIPMIC)
+#define IEEE80211_CRYPTO_CKIP (1<<IEEE80211_CIPHER_CKIP)
#if defined(__KERNEL__) || defined(_KERNEL)
struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_node;
struct mbuf;
-/*
- * Crypto state kept in each ieee80211com. Some of this
- * can/should be shared when virtual AP's are supported.
- *
- * XXX save reference to ieee80211com to properly encapsulate state.
- * XXX split out crypto capabilities from ic_caps
- */
-struct ieee80211_crypto_state {
- struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID];
- ieee80211_keyix cs_def_txkey; /* default/group tx key index */
- uint16_t cs_max_keyix; /* max h/w key index */
-
- int (*cs_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
- int (*cs_key_delete)(struct ieee80211com *,
- const struct ieee80211_key *);
- int (*cs_key_set)(struct ieee80211com *,
- const struct ieee80211_key *,
- const uint8_t mac[IEEE80211_ADDR_LEN]);
- void (*cs_key_update_begin)(struct ieee80211com *);
- void (*cs_key_update_end)(struct ieee80211com *);
-};
+MALLOC_DECLARE(M_80211_CRYPTO);
void ieee80211_crypto_attach(struct ieee80211com *);
void ieee80211_crypto_detach(struct ieee80211com *);
-int ieee80211_crypto_newkey(struct ieee80211com *,
+void ieee80211_crypto_vattach(struct ieee80211vap *);
+void ieee80211_crypto_vdetach(struct ieee80211vap *);
+int ieee80211_crypto_newkey(struct ieee80211vap *,
int cipher, int flags, struct ieee80211_key *);
-int ieee80211_crypto_delkey(struct ieee80211com *,
+int ieee80211_crypto_delkey(struct ieee80211vap *,
struct ieee80211_key *);
-int ieee80211_crypto_setkey(struct ieee80211com *,
- struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]);
-void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+int ieee80211_crypto_setkey(struct ieee80211vap *,
+ struct ieee80211_key *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+void ieee80211_crypto_delglobalkeys(struct ieee80211vap *);
/*
* Template for a supported cipher. Ciphers register with the
@@ -152,7 +155,7 @@ struct ieee80211_cipher {
u_int ic_header; /* size of privacy header (bytes) */
u_int ic_trailer; /* size of privacy trailer (bytes) */
u_int ic_miclen; /* size of mic trailer (bytes) */
- void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+ void* (*ic_attach)(struct ieee80211vap *, struct ieee80211_key *);
void (*ic_detach)(struct ieee80211_key *);
int (*ic_setkey)(struct ieee80211_key *);
int (*ic_encap)(struct ieee80211_key *, struct mbuf *,
@@ -170,16 +173,16 @@ void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
-struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
+struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
+ struct mbuf *);
+struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
+ struct mbuf *, int);
/*
* Check and remove any MIC.
*/
static __inline int
-ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
+ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -190,7 +193,7 @@ ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
* Add any MIC.
*/
static __inline int
-ieee80211_crypto_enmic(struct ieee80211com *ic,
+ieee80211_crypto_enmic(struct ieee80211vap *vap,
struct ieee80211_key *k, struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -203,11 +206,11 @@ ieee80211_crypto_enmic(struct ieee80211com *ic,
* key data) is properly setup before a key is used.
*/
static __inline void
-ieee80211_crypto_resetkey(struct ieee80211com *ic,
+ieee80211_crypto_resetkey(struct ieee80211vap *vap,
struct ieee80211_key *k, ieee80211_keyix ix)
{
k->wk_cipher = &ieee80211_cipher_none;;
- k->wk_private = k->wk_cipher->ic_attach(ic, k);
+ k->wk_private = k->wk_cipher->ic_attach(vap, k);
k->wk_keyix = k->wk_rxkeyix = ix;
k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
}
@@ -215,10 +218,10 @@ ieee80211_crypto_resetkey(struct ieee80211com *ic,
/*
* Crypt-related notification methods.
*/
-void ieee80211_notify_replay_failure(struct ieee80211com *,
+void ieee80211_notify_replay_failure(struct ieee80211vap *,
const struct ieee80211_frame *, const struct ieee80211_key *,
- u_int64_t rsc);
-void ieee80211_notify_michael_failure(struct ieee80211com *,
+ uint64_t rsc);
+void ieee80211_notify_michael_failure(struct ieee80211vap *,
const struct ieee80211_frame *, u_int keyix);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c
index 525fe9a..d3b1af5 100644
--- a/sys/net80211/ieee80211_crypto_ccmp.c
+++ b/sys/net80211/ieee80211_crypto_ccmp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -53,11 +55,12 @@ __FBSDID("$FreeBSD$");
#define AES_BLOCK_LEN 16
struct ccmp_ctx {
- struct ieee80211com *cc_ic; /* for diagnostics */
+ struct ieee80211vap *cc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *cc_ic;
rijndael_ctx cc_aes;
};
-static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *);
static void ccmp_detach(struct ieee80211_key *);
static int ccmp_setkey(struct ieee80211_key *);
static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid);
@@ -89,17 +92,18 @@ static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
static int nrefs = 0;
static void *
-ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct ccmp_ctx *ctx;
MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->cc_ic = ic;
+ ctx->cc_vap = vap;
+ ctx->cc_ic = vap->iv_ic;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -109,7 +113,7 @@ ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -120,7 +124,7 @@ ccmp_setkey(struct ieee80211_key *k)
struct ccmp_ctx *ctx = k->wk_private;
if (k->wk_keylen != (128/NBBY)) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -200,8 +204,9 @@ static int
ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
uint64_t pn;
/*
@@ -214,19 +219,19 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] Missing ExtIV for AES-CCM cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for AES-CCM cipher");
+ vap->iv_stats.is_rx_ccmpformat++;
return 0;
}
+ tid = ieee80211_gettid(wh);
pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (pn <= k->wk_keyrsc) {
+ if (pn <= k->wk_keyrsc[tid]) {
/*
* Replay violation.
*/
- ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
- ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, pn);
+ vap->iv_stats.is_rx_ccmpreplay++;
return 0;
}
@@ -251,7 +256,7 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* Ok to update rsc now.
*/
- k->wk_keyrsc = pn;
+ k->wk_keyrsc[tid] = pn;
return 1;
}
@@ -406,7 +411,7 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
uint8_t *pos;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
@@ -544,6 +549,7 @@ static int
ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t aad[2 * AES_BLOCK_LEN];
uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
@@ -553,7 +559,7 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
uint8_t *pos;
u_int space;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
@@ -616,10 +622,9 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
}
}
if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] AES-CCM decrypt failed; MIC mismatch\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "AES-CCM decrypt failed; MIC mismatch");
+ vap->iv_stats.is_rx_ccmpmic++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c
index 7fbb53d..b1ffbb4 100644
--- a/sys/net80211/ieee80211_crypto_none.c
+++ b/sys/net80211/ieee80211_crypto_none.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,10 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 NULL crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
@@ -42,7 +45,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *none_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *none_attach(struct ieee80211vap *, struct ieee80211_key *);
static void none_detach(struct ieee80211_key *);
static int none_setkey(struct ieee80211_key *);
static int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t);
@@ -66,9 +69,9 @@ const struct ieee80211_cipher ieee80211_cipher_none = {
};
static void *
-none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+none_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
- return ic; /* for diagnostics+stats */
+ return vap; /* for diagnostics+stats */
}
static void
@@ -87,7 +90,7 @@ none_setkey(struct ieee80211_key *k)
static int
none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
@@ -96,17 +99,16 @@ none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
* The specified key is not setup; this can
* happen, at least, when changing keys.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (encap)\n",
- ether_sprintf(wh->i_addr1), keyid>>6);
- ic->ic_stats.is_tx_badcipher++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1,
+ "key id %u is not set (encap)", keyid>>6);
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
const uint8_t *ivp = (const uint8_t *)&wh[1];
@@ -117,27 +119,26 @@ none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
* happen, at least, when changing keys.
*/
/* XXX useful to know dst too */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (decap)\n",
- ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6);
- ic->ic_stats.is_rx_badkeyid++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "key id %u is not set (decap)", ivp[IEEE80211_WEP_IVLEN] >> 6);
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
static int
none_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_tx_badcipher++;
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_rx_badkeyid++;
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c
index 327306a..398246f 100644
--- a/sys/net80211/ieee80211_crypto_tkip.c
+++ b/sys/net80211/ieee80211_crypto_tkip.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -49,7 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *tkip_attach(struct ieee80211vap *, struct ieee80211_key *);
static void tkip_detach(struct ieee80211_key *);
static int tkip_setkey(struct ieee80211_key *);
static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid);
@@ -77,10 +79,9 @@ typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t __u32;
typedef uint32_t u32;
-#define memmove(dst, src, n) ovbcopy(src, dst, n)
struct tkip_ctx {
- struct ieee80211com *tc_ic; /* for diagnostics */
+ struct ieee80211vap *tc_vap; /* for diagnostics+statistics */
u16 tx_ttak[5];
int tx_phase1_done;
@@ -104,18 +105,18 @@ static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
static int nrefs = 0;
static void *
-tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+tkip_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct tkip_ctx *ctx;
MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->tc_ic = ic;
+ ctx->tc_vap = vap;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -125,7 +126,7 @@ tkip_detach(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -137,7 +138,7 @@ tkip_setkey(struct ieee80211_key *k)
if (k->wk_keylen != (128/NBBY)) {
(void) ctx; /* XXX */
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->tc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -153,22 +154,22 @@ static int
tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
uint8_t *ivp;
int hdrlen;
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] Discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -215,11 +216,12 @@ tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int hdrlen;
uint8_t mic[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipenmic++;
+ vap->iv_stats.is_crypto_tkipenmic++;
hdrlen = ieee80211_hdrspace(ic, wh);
@@ -247,9 +249,9 @@ static int
tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
/*
* Header should have extended IV and sequence number;
@@ -261,30 +263,29 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] missing ExtIV for TKIP cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for TKIP cipher");
+ vap->iv_stats.is_rx_tkipformat++;
return 0;
}
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
+ tid = ieee80211_gettid(wh);
ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (ctx->rx_rsc <= k->wk_keyrsc) {
+ if (ctx->rx_rsc <= k->wk_keyrsc[tid]) {
/*
* Replay violation; notify upper layer.
*/
- ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc);
- ctx->tc_ic->ic_stats.is_rx_tkipreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, ctx->rx_rsc);
+ vap->iv_stats.is_rx_tkipreplay++;
return 0;
}
/*
@@ -322,15 +323,17 @@ static int
tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ uint8_t tid;
+ wh = mtod(m, struct ieee80211_frame *);
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
- struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
- int hdrlen = ieee80211_hdrspace(ic, wh);
+ struct ieee80211vap *vap = ctx->tc_vap;
+ int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
u8 mic[IEEE80211_WEP_MICLEN];
u8 mic0[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipdemic++;
+ vap->iv_stats.is_crypto_tkipdemic++;
michael_mic(ctx, k->wk_rxmic,
m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
@@ -339,7 +342,7 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
tkip.ic_miclen, mic0);
if (memcmp(mic, mic0, tkip.ic_miclen)) {
/* NB: 802.11 layer handles statistic and debug msg */
- ieee80211_notify_michael_failure(ic, wh,
+ ieee80211_notify_michael_failure(vap, wh,
k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
k->wk_rxkeyix : k->wk_keyix);
return 0;
@@ -353,7 +356,8 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
/*
* Ok to update rsc now that MIC has been verified.
*/
- k->wk_keyrsc = ctx->rx_rsc;
+ tid = ieee80211_gettid(wh);
+ k->wk_keyrsc[tid] = ctx->rx_rsc;
return 1;
}
@@ -904,7 +908,7 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct ieee80211_frame *wh;
uint8_t icv[IEEE80211_WEP_CRCLEN];
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ ctx->tc_vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
if (!ctx->tx_phase1_done) {
@@ -932,17 +936,20 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct mbuf *m, int hdrlen)
{
struct ieee80211_frame *wh;
+ struct ieee80211vap *vap = ctx->tc_vap;
u32 iv32;
u16 iv16;
+ u8 tid;
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
/* NB: tkip_decap already verified header and left seq in rx_rsc */
iv16 = (u16) ctx->rx_rsc;
iv32 = (u32) (ctx->rx_rsc >> 16);
- if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) {
+ tid = ieee80211_gettid(wh);
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16) || !ctx->rx_phase1_done) {
tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
wh->i_addr2, iv32);
ctx->rx_phase1_done = 1;
@@ -953,15 +960,14 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
if (wep_decrypt(ctx->rx_rc4key,
m, hdrlen + tkip.ic_header,
m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
- if (iv32 != (u32)(key->wk_keyrsc >> 16)) {
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16)) {
/* Previously cached Phase1 result was already lost, so
* it needs to be recalculated for the next packet. */
ctx->rx_phase1_done = 0;
}
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] TKIP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipicv++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "TKIP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_tkipicv++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c
index 81d15cc..df988a6 100644
--- a/sys/net80211/ieee80211_crypto_wep.c
+++ b/sys/net80211/ieee80211_crypto_wep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 WEP crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -45,7 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
static void wep_detach(struct ieee80211_key *);
static int wep_setkey(struct ieee80211_key *);
static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
@@ -72,7 +74,8 @@ static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
struct wep_ctx {
- struct ieee80211com *wc_ic; /* for diagnostics */
+ struct ieee80211vap *wc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *wc_ic;
uint32_t wc_iv; /* initial vector for crypto */
};
@@ -80,18 +83,19 @@ struct wep_ctx {
static int nrefs = 0;
static void *
-wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct wep_ctx *ctx;
MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->wc_ic = ic;
+ ctx->wc_vap = vap;
+ ctx->wc_ic = vap->iv_ic;
get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
nrefs++; /* NB: we assume caller locking */
return ctx;
@@ -102,7 +106,7 @@ wep_detach(struct ieee80211_key *k)
{
struct wep_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -208,6 +212,7 @@ static int
wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct wep_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct ieee80211_frame *wh;
wh = mtod(m, struct ieee80211_frame *);
@@ -219,10 +224,9 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
*/
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
!wep_decrypt(k, m, hdrlen)) {
- IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] WEP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->wc_ic->ic_stats.is_rx_wepfail++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "WEP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_wepfail++;
return 0;
}
@@ -305,6 +309,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -314,7 +319,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -351,12 +356,12 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
}
if (m->m_next == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
ether_sprintf(mtod(m0,
struct ieee80211_frame *)->i_addr2),
+ "out of data for WEP (data_len %zu)",
data_len);
+ /* XXX stat */
return 0;
}
break;
@@ -387,6 +392,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -396,7 +402,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -435,11 +441,9 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
m = m->m_next;
if (m == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
- ether_sprintf(mtod(m0,
- struct ieee80211_frame *)->i_addr2),
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ mtod(m0, struct ieee80211_frame *)->i_addr2,
+ "out of data for WEP (data_len %zu)",
data_len);
return 0;
}
diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c
new file mode 100644
index 0000000..5b78283
--- /dev/null
+++ b/sys/net80211/ieee80211_ddb.c
@@ -0,0 +1,789 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+#include "opt_wlan.h"
+
+#ifdef DDB
+/*
+ * IEEE 802.11 DDB support
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <ddb/ddb.h>
+
+#define IEEE80211_MSG_BITS \
+ "\20\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \
+ "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1X\22POWER" \
+ "\23STATE\24OUTPUT\25SCAN\26AUTH\27ASSOC\30NODE\31ELEMID\32XRATE" \
+ "\33INPUT\34CRYPTO\35DUPMPKTS\36DEBUG\3711N"
+
+#define IEEE80211_F_BITS \
+ "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \
+ "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \
+ "\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
+ "\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \
+ "\37DOTH\40DWDS"
+
+#define IEEE80211_FEXT_BITS \
+ "\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\12NONEPR_PR"\
+ "\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX" \
+ "\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20" \
+ "\34SHORTGI40\35HTCOMPAT"
+
+#define IEEE80211_FVEN_BITS "\20"
+
+#define IEEE80211_C_BITS \
+ "\20\7FF\10TURBOP\11IBSS\12PMGT" \
+ "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
+ "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
+ "\37TXFRAG"
+
+#define IEEE80211_C_CRYPTO_BITS \
+ "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT"
+
+#define IEEE80211_C_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\21AMPDU\22AMSDU\23HT"
+
+/* NB: policy bits not included */
+#define IEEE80211_CHAN_BITS \
+ "\20\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ\12PASSIVE\13DYN\14GFSK" \
+ "\15STURBO\16HALF\17QUARTER\20HT20\21HT40U\22HT40D\23DFS"
+
+#define IEEE80211_NODE_BITS \
+ "\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \
+ "\13AMPDU_RX\14AMPDU_TX"
+
+#define IEEE80211_ERP_BITS \
+ "\20\1NON_ERP_PRESENT\2USE_PROTECTION\3LONG_PREAMBLE"
+
+#define IEEE80211_CAPINFO_BITS \
+ "\20\1ESS\2IBSS\3CF_POLLABLE\4CF_POLLREQ\5PRIVACY\6SHORT_PREAMBLE" \
+ "\7PBCC\10CHNL_AGILITY\11SPECTRUM_MGMT\13SHORT_SLOTTIME\14RSN" \
+ "\16DSSOFDM"
+
+#define IEEE80211_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \
+ "\20LSIGTXOPPROT"
+
+#define IEEE80211_AGGR_BITS \
+ "\20\1IMMEDIATE\2XCHGPEND\3RUNNING\4SETUP\5NAK"
+
+static void _db_show_sta(const struct ieee80211_node *);
+static void _db_show_vap(const struct ieee80211vap *, int);
+static void _db_show_com(const struct ieee80211com *,
+ int showvaps, int showsta, int showprocs);
+
+static void _db_show_channel(const char *tag, const struct ieee80211_channel *);
+static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *);
+static void _db_show_appie(const char *tag, const struct ieee80211_appie *);
+static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *);
+static void _db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp);
+static void _db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp);
+static void _db_show_stats(const struct ieee80211_stats *);
+
+DB_SHOW_COMMAND(sta, db_show_sta)
+{
+ if (!have_addr) {
+ db_printf("usage: show sta <addr>\n");
+ return;
+ }
+ _db_show_sta((const struct ieee80211_node *) addr);
+}
+
+DB_SHOW_COMMAND(vap, db_show_vap)
+{
+ int i, showprocs = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show vap <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showprocs = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+ _db_show_vap((const struct ieee80211vap *) addr, showprocs);
+}
+
+DB_SHOW_COMMAND(com, db_show_com)
+{
+ const struct ieee80211com *ic;
+ int i, showprocs = 0, showvaps = 0, showsta = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show com <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showsta = showvaps = showprocs = 1;
+ break;
+ case 's':
+ showsta = 1;
+ break;
+ case 'v':
+ showvaps = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+
+ ic = (const struct ieee80211com *) addr;
+ _db_show_com(ic, showvaps, showsta, showprocs);
+}
+
+DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
+{
+ const struct ifnet *ifp;
+ int i, showall = 0;
+
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showall = 1;
+ break;
+ }
+
+ TAILQ_FOREACH(ifp, &ifnet, if_list)
+ if (ifp->if_type == IFT_IEEE80211) {
+ const struct ieee80211com *ic = ifp->if_l2com;
+
+ if (!showall) {
+ const struct ieee80211vap *vap;
+ db_printf("%s: com %p vaps:",
+ ifp->if_xname, ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ db_printf(" %s(%p)",
+ vap->iv_ifp->if_xname, vap);
+ db_printf("\n");
+ } else
+ _db_show_com(ic, 1, 1, 1);
+ }
+}
+
+static void
+_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap)
+{
+ db_printf("%stxampdu[%d]: %p flags %b ac %u\n",
+ sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS, tap->txa_ac);
+ db_printf("%s token %u qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, tap->txa_token, tap->txa_qbytes, tap->txa_qframes,
+ tap->txa_seqstart, tap->txa_start, tap->txa_wnd);
+ db_printf("%s attempts %d nextrequest %d\n",
+ sep, tap->txa_attempts, tap->txa_nextrequest);
+ /* XXX packet q + timer */
+}
+
+static void
+_db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap)
+{
+ db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n",
+ sep, ix, rap, rap->rxa_flags, ix /*XXX */);
+ db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, rap->rxa_qbytes, rap->rxa_qframes,
+ rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd);
+ db_printf("%s age %d nframes %d\n",
+ sep, rap->rxa_age, rap->rxa_nframes);
+}
+
+static void
+_db_show_sta(const struct ieee80211_node *ni)
+{
+ int i;
+
+ db_printf("0x%p: mac %s refcnt %d\n", ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
+ db_printf("\tvap %p wdsvap %p ic %p table %p\n",
+ ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table);
+ db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS);
+ db_printf("\tscangen %u authmode %u ath_flags 0x%x ath_defkeyix %u\n",
+ ni->ni_scangen, ni->ni_authmode,
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ db_printf("\tassocid 0x%x txpower %u vlan %u\n",
+ ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+ db_printf("\tjointime %d (%lu secs) challenge %p\n",
+ ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime),
+ ni->ni_challenge);
+ db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len);
+ db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n",
+ ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie,
+ ni->ni_ies.ath_ie);
+ db_printf("\t htcap_ie %p htinfo_ie %p]\n",
+ ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie);
+ db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+ ni->ni_txseqs[IEEE80211_NONQOS_TID],
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxfragstamp);
+ db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n",
+ ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]);
+ db_printf("\trstamp %u avgrssi 0x%x (rssi %d) noise %d\n",
+ ni->ni_rstamp, ni->ni_avgrssi,
+ IEEE80211_RSSI_GET(ni->ni_avgrssi), ni->ni_noise);
+ db_printf("\tintval %u capinfo %b\n",
+ ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS);
+ db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid));
+ _db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid);
+ db_printf("\n");
+ _db_show_channel("\tchannel", ni->ni_chan);
+ db_printf("\n");
+ db_printf("\terp %b dtim_period %u dtim_count %u\n",
+ ni->ni_erp, IEEE80211_ERP_BITS,
+ ni->ni_dtim_period, ni->ni_dtim_count);
+
+ db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n",
+ ni->ni_htcap, IEEE80211_HTCAP_BITS,
+ ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan);
+ db_printf("\thtopmode 0x%x htstbc 0x%x reqcw %u chw %u\n",
+ ni->ni_htopmode, ni->ni_htstbc, ni->ni_reqcw, ni->ni_chw);
+
+ /* XXX ampdu state */
+ for (i = 0; i < WME_NUM_AC; i++)
+ if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP)
+ _db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]);
+ for (i = 0; i < WME_NUM_TID; i++)
+ if (ni->ni_rx_ampdu[i].rxa_nframes)
+ _db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]);
+
+ db_printf("\tinact %u inact_reload %u txrate %u\n",
+ ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate);
+ /* XXX savedq */
+ /* XXX wdsq */
+}
+
+static void
+_db_show_vap(const struct ieee80211vap *vap, int showprocs)
+{
+ const struct ieee80211com *ic = vap->iv_ic;
+ int i;
+
+ db_printf("%p:", vap);
+ db_printf(" bss %p", vap->iv_bss);
+ db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr));
+ db_printf("\n");
+
+ db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
+ db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
+ db_printf(" ifp %p", vap->iv_ifp);
+ db_printf("\n");
+
+ db_printf("\tic %p", vap->iv_ic);
+ db_printf(" media %p", &vap->iv_media);
+ db_printf(" bpf_if %p", vap->iv_rawbpf);
+ db_printf(" mgtsend %p", &vap->iv_mgtsend);
+#if 0
+ struct sysctllog *iv_sysctl; /* dynamic sysctl context */
+#endif
+ db_printf("\n");
+ db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS);
+
+ db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS);
+ db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS);
+
+ _db_show_stats(&vap->iv_stats);
+
+ db_printf("\tinact_init %d", vap->iv_inact_init);
+ db_printf(" inact_auth %d", vap->iv_inact_auth);
+ db_printf(" inact_run %d", vap->iv_inact_run);
+ db_printf(" inact_probe %d", vap->iv_inact_probe);
+ db_printf("\n");
+
+ db_printf("\tdes_nssid %d", vap->iv_des_nssid);
+ if (vap->iv_des_nssid)
+ _db_show_ssid(" des_ssid[%u] ", 0,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid));
+ db_printf("\n");
+ db_printf("\tdes_mode %d", vap->iv_des_mode);
+ _db_show_channel(" des_chan", vap->iv_des_chan);
+ db_printf("\n");
+#if 0
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+#endif
+ db_printf("\tbgscanidle %u", vap->iv_bgscanidle);
+ db_printf(" bgscanintvl %u", vap->iv_bgscanintvl);
+ db_printf(" scanvalid %u", vap->iv_scanvalid);
+ db_printf("\n");
+ db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration);
+ db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell);
+ db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell);
+ db_printf("\n");
+ db_printf(" scanreq_flags 0x%x", vap->iv_scanreq_flags);
+ db_printf("\tscanreq_nssid %d", vap->iv_scanreq_nssid);
+ for (i = 0; i < vap->iv_scanreq_nssid; i++)
+ _db_show_ssid(" scanreq_ssid[%u]", i,
+ vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid);
+ db_printf(" roaming %d", vap->iv_roaming);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_roamparams("\troamparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_roamparms[i]);
+ db_printf("\n");
+ }
+
+ db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold);
+ db_printf(" bmiss_max %u", vap->iv_bmiss_count);
+ db_printf(" bmiss_max %d", vap->iv_bmiss_max);
+ db_printf("\n");
+ db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count);
+ db_printf(" swbmiss_period %u", vap->iv_swbmiss_period);
+ db_printf(" swbmiss %p", &vap->iv_swbmiss);
+ db_printf("\n");
+
+ db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax);
+ db_printf(" ampdu_density %d", vap->iv_ampdu_density);
+ db_printf(" ampdu_limit %d", vap->iv_ampdu_limit);
+ db_printf(" amsdu_limit %d", vap->iv_amsdu_limit);
+ db_printf("\n");
+
+ db_printf("\tmax_aid %u", vap->iv_max_aid);
+ db_printf(" aid_bitmap %p", vap->iv_aid_bitmap);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
+ db_printf(" ps_sta %u", vap->iv_ps_sta);
+ db_printf(" ps_pending %u", vap->iv_ps_pending);
+ db_printf(" tim_len %u", vap->iv_tim_len);
+ db_printf(" tim_bitmap %p", vap->iv_tim_bitmap);
+ db_printf("\n");
+ db_printf("\tdtim_period %u", vap->iv_dtim_period);
+ db_printf(" dtim_count %u", vap->iv_dtim_count);
+ db_printf(" set_tim %p", vap->iv_set_tim);
+ db_printf(" csa_count %d", vap->iv_csa_count);
+ db_printf("\n");
+
+ db_printf("\trtsthreshold %u", vap->iv_rtsthreshold);
+ db_printf(" fragthreshold %u", vap->iv_fragthreshold);
+ db_printf(" inact_timer %d", vap->iv_inact_timer);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_txparams("\ttxparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_txparms[i]);
+ db_printf("\n");
+ }
+
+ /* application-specified IE's to attach to mgt frames */
+ _db_show_appie("\tappie_beacon", vap->iv_appie_beacon);
+ _db_show_appie("\tappie_probereq", vap->iv_appie_probereq);
+ _db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp);
+ _db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq);
+ _db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp);
+ _db_show_appie("\tappie_wpa", vap->iv_appie_wpa);
+ if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) {
+ if (vap->iv_wpa_ie != NULL)
+ db_printf("\twpa_ie %p", vap->iv_wpa_ie);
+ if (vap->iv_rsn_ie != NULL)
+ db_printf("\trsn_ie %p", vap->iv_rsn_ie);
+ db_printf("\n");
+ }
+ db_printf("\tmax_keyix %u", vap->iv_max_keyix);
+ db_printf(" def_txkey %d", vap->iv_def_txkey);
+ db_printf("\n");
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]);
+
+ db_printf("\tauth %p", vap->iv_auth);
+ db_printf(" ec %p", vap->iv_ec);
+
+ db_printf(" acl %p", vap->iv_acl);
+ db_printf(" as %p", vap->iv_as);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tiv_key_alloc %p\n", vap->iv_key_alloc);
+ db_printf("\tiv_key_delete %p\n", vap->iv_key_delete);
+ db_printf("\tiv_key_set %p\n", vap->iv_key_set);
+ db_printf("\tiv_key_update_begin %p\n", vap->iv_key_update_begin);
+ db_printf("\tiv_key_update_end %p\n", vap->iv_key_update_end);
+ db_printf("\tiv_opdetach %p\n", vap->iv_opdetach);
+ db_printf("\tiv_input %p\n", vap->iv_input);
+ db_printf("\tiv_recv_mgmt %p\n", vap->iv_recv_mgmt);
+ db_printf("\tiv_deliver_data %p\n", vap->iv_deliver_data);
+ db_printf("\tiv_bmiss %p\n", vap->iv_bmiss);
+ db_printf("\tiv_reset %p\n", vap->iv_reset);
+ db_printf("\tiv_update_beacon %p\n", vap->iv_update_beacon);
+ db_printf("\tiv_newstate %p\n", vap->iv_newstate);
+ db_printf("\tiv_output %p\n", vap->iv_output);
+ }
+}
+
+static void
+_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
+{
+ struct ieee80211vap *vap;
+
+ db_printf("%p:", ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ db_printf(" %s(%p)", vap->iv_ifp->if_xname, vap);
+ db_printf("\n");
+ db_printf("\tifp %p", ic->ic_ifp);
+ db_printf(" comlock %p", &ic->ic_comlock);
+ db_printf("\n");
+ _db_show_stats(&ic->ic_stats);
+ db_printf("\theadroom %d", ic->ic_headroom);
+ db_printf(" phytype %d", ic->ic_phytype);
+ db_printf(" opmode %s", ieee80211_opmode_name[ic->ic_opmode]);
+ db_printf("\n");
+ db_printf("\tmedia %p", &ic->ic_media);
+ db_printf(" myaddr %s", ether_sprintf(ic->ic_myaddr));
+ db_printf(" inact %p", &ic->ic_inact);
+ db_printf("\n");
+
+ db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS);
+ db_printf("\tcryptocaps=%b\n",
+ ic->ic_cryptocaps, IEEE80211_C_CRYPTO_BITS);
+ db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS);
+
+#if 0
+ uint8_t ic_modecaps[2]; /* set of mode capabilities */
+#endif
+ db_printf("\tcurmode %u", ic->ic_curmode);
+ db_printf(" promisc %u", ic->ic_promisc);
+ db_printf(" allmulti %u", ic->ic_allmulti);
+ db_printf(" nrunning %u", ic->ic_nrunning);
+ db_printf("\n");
+ db_printf("\tbintval %u", ic->ic_bintval);
+ db_printf(" lintval %u", ic->ic_lintval);
+ db_printf(" holdover %u", ic->ic_holdover);
+ db_printf(" txpowlimit %u", ic->ic_txpowlimit);
+ db_printf("\n");
+#if 0
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+#endif
+ /*
+ * Channel state:
+ *
+ * ic_channels is the set of available channels for the device;
+ * it is setup by the driver
+ * ic_nchans is the number of valid entries in ic_channels
+ * ic_chan_avail is a bit vector of these channels used to check
+ * whether a channel is available w/o searching the channel table.
+ * ic_chan_active is a (potentially) constrained subset of
+ * ic_chan_avail that reflects any mode setting or user-specified
+ * limit on the set of channels to use/scan
+ * ic_curchan is the current channel the device is set to; it may
+ * be different from ic_bsschan when we are off-channel scanning
+ * or otherwise doing background work
+ * ic_bsschan is the channel selected for operation; it may
+ * be undefined (IEEE80211_CHAN_ANYC)
+ * ic_prevchan is a cached ``previous channel'' used to optimize
+ * lookups when switching back+forth between two channels
+ * (e.g. for dynamic turbo)
+ */
+ db_printf("\tnchans %d", ic->ic_nchans);
+#if 0
+ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
+ uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
+#endif
+ db_printf("\n");
+ _db_show_channel("\tcurchan", ic->ic_curchan);
+ db_printf("\n");
+ _db_show_channel("\tbsschan", ic->ic_bsschan);
+ db_printf("\n");
+ _db_show_channel("\tprevchan", ic->ic_prevchan);
+ db_printf("\n");
+ db_printf("\tregdomain %p", &ic->ic_regdomain);
+ db_printf("\n");
+
+ _db_show_channel("\tcsa_newchan", ic->ic_csa_newchan);
+ db_printf(" csa_count %d", ic->ic_csa_count);
+ db_printf( "dfs %p", &ic->ic_dfs);
+ db_printf("\n");
+
+ db_printf("\tscan %p", ic->ic_scan);
+ db_printf(" lastdata %d", ic->ic_lastdata);
+ db_printf(" lastscan %d", ic->ic_lastscan);
+ db_printf("\n");
+
+ db_printf("\tmax_keyix %d", ic->ic_max_keyix);
+ db_printf(" sta %p", &ic->ic_sta);
+ db_printf(" wme %p", &ic->ic_wme);
+ db_printf("\n");
+
+ db_printf("\tprotmode %d", ic->ic_protmode);
+ db_printf(" nonerpsta %u", ic->ic_nonerpsta);
+ db_printf(" longslotsta %u", ic->ic_longslotsta);
+ db_printf(" lastnonerp %d", ic->ic_lastnonerp);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
+ db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
+ db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
+ db_printf("\n");
+ db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
+ db_printf(" htprotmode %d", ic->ic_htprotmode);
+ db_printf(" lastnonht %d", ic->ic_lastnonht);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tic_vap_create %p\n", ic->ic_vap_create);
+ db_printf("\tic_vap_delete %p\n", ic->ic_vap_delete);
+#if 0
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+#endif
+ db_printf("\tic_newassoc %p\n", ic->ic_newassoc);
+ db_printf("\tic_getradiocaps %p\n", ic->ic_getradiocaps);
+ db_printf("\tic_setregdomain %p\n", ic->ic_setregdomain);
+ db_printf("\tic_send_mgmt %p\n", ic->ic_send_mgmt);
+ db_printf("\tic_raw_xmit %p\n", ic->ic_raw_xmit);
+ db_printf("\tic_updateslot %p\n", ic->ic_updateslot);
+ db_printf("\tic_update_mcast %p\n", ic->ic_update_mcast);
+ db_printf("\tic_update_promisc %p\n", ic->ic_update_promisc);
+ db_printf("\tic_node_alloc %p\n", ic->ic_node_alloc);
+ db_printf("\tic_node_free %p\n", ic->ic_node_free);
+ db_printf("\tic_node_cleanup %p\n", ic->ic_node_cleanup);
+ db_printf("\tic_node_getrssi %p\n", ic->ic_node_getrssi);
+ db_printf("\tic_node_getsignal %p\n", ic->ic_node_getsignal);
+ db_printf("\tic_node_getmimoinfo %p\n", ic->ic_node_getmimoinfo);
+ db_printf("\tic_scan_start %p\n", ic->ic_scan_start);
+ db_printf("\tic_scan_end %p\n", ic->ic_scan_end);
+ db_printf("\tic_set_channel %p\n", ic->ic_set_channel);
+ db_printf("\tic_scan_curchan %p\n", ic->ic_scan_curchan);
+ db_printf("\tic_scan_mindwell %p\n", ic->ic_scan_mindwell);
+ db_printf("\tic_recv_action %p\n", ic->ic_recv_action);
+ db_printf("\tic_send_action %p\n", ic->ic_send_action);
+ db_printf("\tic_addba_request %p\n", ic->ic_addba_request);
+ db_printf("\tic_addba_response %p\n", ic->ic_addba_response);
+ db_printf("\tic_addba_stop %p\n", ic->ic_addba_stop);
+ }
+ if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
+ db_printf("\n");
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ _db_show_vap(vap, showprocs);
+ }
+ if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
+ const struct ieee80211_node_table *nt = &ic->ic_sta;
+ const struct ieee80211_node *ni;
+
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ db_printf("\n");
+ _db_show_sta(ni);
+ }
+ }
+}
+
+static void
+_db_show_channel(const char *tag, const struct ieee80211_channel *c)
+{
+ db_printf("%s ", tag);
+ if (c == NULL)
+ db_printf("<NULL>");
+ else if (c == IEEE80211_CHAN_ANYC)
+ db_printf("<ANY>");
+ else
+ db_printf("[%u (%u) flags=%b maxreg %u maxpow %u minpow %u state 0x%x extieee %u]",
+ c->ic_freq, c->ic_ieee,
+ c->ic_flags, IEEE80211_CHAN_BITS,
+ c->ic_maxregpower, c->ic_maxpower, c->ic_minpower,
+ c->ic_state, c->ic_extieee);
+}
+
+static void
+_db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid)
+{
+ const uint8_t *p;
+ int i;
+
+ db_printf(tag, ix);
+
+ if (len > IEEE80211_NWID_LEN)
+ len = IEEE80211_NWID_LEN;
+ /* determine printable or not */
+ for (i = 0, p = ssid; i < len; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i == len) {
+ db_printf("\"");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%c", *p);
+ db_printf("\"");
+ } else {
+ db_printf("0x");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%02x", *p);
+ }
+}
+
+static void
+_db_show_appie(const char *tag, const struct ieee80211_appie *ie)
+{
+ const uint8_t *p;
+ int i;
+
+ if (ie == NULL)
+ return;
+ db_printf("%s [0x", tag);
+ for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++)
+ db_printf("%02x", *p);
+ db_printf("]\n");
+}
+
+static void
+_db_show_key(const char *tag, int ix, const struct ieee80211_key *wk)
+{
+ static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
+ const struct ieee80211_cipher *cip = wk->wk_cipher;
+ int keylen = wk->wk_keylen;
+
+ if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
+ return;
+ db_printf(tag, ix);
+ switch (cip->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ /* compatibility */
+ db_printf(" wepkey %u:%s", wk->wk_keyix+1,
+ keylen <= 5 ? "40-bit" :
+ keylen <= 13 ? "104-bit" : "128-bit");
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ if (keylen > 128/8)
+ keylen -= 128/8; /* ignore MIC for now */
+ db_printf(" TKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_OCB:
+ db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_CKIP:
+ db_printf(" CKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_NONE:
+ db_printf(" NULL %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ default:
+ db_printf(" UNKNOWN (0x%x) %u:%u-bit",
+ cip->ic_cipher, wk->wk_keyix+1, 8*keylen);
+ break;
+ }
+ if (memcmp(wk->wk_key, zerodata, keylen) != 0) {
+ int i;
+
+ db_printf(" <");
+ for (i = 0; i < keylen; i++)
+ db_printf("%02x", wk->wk_key[i]);
+ db_printf(">");
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0)
+ db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]);
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keytsc != 0)
+ db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc);
+ if (wk->wk_flags != 0) {
+ const char *sep = " ";
+
+ if (wk->wk_flags & IEEE80211_KEY_XMIT)
+ db_printf("%stx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_RECV)
+ db_printf("%srx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_DEFAULT)
+ db_printf("%sdef", sep), sep = "+";
+ }
+ db_printf("\n");
+ }
+}
+
+static void
+printrate(const char *tag, int v)
+{
+ if (v == IEEE80211_FIXED_RATE_NONE)
+ db_printf(" %s <none>", tag);
+ else if (v == 11)
+ db_printf(" %s 5.5", tag);
+ else if (v & IEEE80211_RATE_MCS)
+ db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS);
+ else
+ db_printf(" %s %d", tag, v/2);
+}
+
+static void
+_db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp)
+{
+
+ db_printf(tag, arg);
+ if (rp->rssi & 1)
+ db_printf(" rssi %u.5", rp->rssi/2);
+ else
+ db_printf(" rssi %u", rp->rssi/2);
+ printrate("rate", rp->rate);
+}
+
+static void
+_db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp)
+{
+
+ db_printf(tag, arg);
+ printrate("ucastrate", tp->ucastrate);
+ printrate("mcastrate", tp->mcastrate);
+ printrate("mgmtrate", tp->mgmtrate);
+ db_printf(" maxretry %d", tp->maxretry);
+}
+
+static void
+_db_show_stats(const struct ieee80211_stats *is)
+{
+}
+#endif /* DDB */
diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c
new file mode 100644
index 0000000..0351cb8
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 DFS/Radar support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+
+MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
+
+/* XXX public for sysctl hookup */
+int ieee80211_nol_timeout = 30*60; /* 30 minutes */
+#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
+int ieee80211_cac_timeout = 60; /* 60 seconds */
+#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
+
+void
+ieee80211_dfs_attach(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ callout_init(&dfs->nol_timer, CALLOUT_MPSAFE);
+ callout_init(&dfs->cac_timer, CALLOUT_MPSAFE);
+}
+
+void
+ieee80211_dfs_detach(struct ieee80211com *ic)
+{
+ /* NB: we assume no locking is needed */
+ ieee80211_dfs_reset(ic);
+}
+
+void
+ieee80211_dfs_reset(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ /* NB: we assume no locking is needed */
+ /* NB: cac_timer should be cleared by the state machine */
+ callout_drain(&dfs->nol_timer);
+ for (i = 0; i < ic->ic_nchans; i++)
+ ic->ic_channels[i].ic_state = 0;
+ dfs->lastchan = NULL;
+}
+
+static void
+cac_timeout(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
+ return;
+ /*
+ * When radar is detected during a CAC we are woken
+ * up prematurely to switch to a new channel.
+ * Check the channel to decide how to act.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_RADAR);
+
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) stopped due to radar\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+
+ /* XXX clobbers any existing desired channel */
+ /* NB: dfs->newchan may be NULL, that's ok */
+ vap->iv_des_chan = dfs->newchan;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) expired; "
+ "no radar detected\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ /*
+ * Mark all channels with the current frequency
+ * as having completed CAC; this keeps us from
+ * doing it again until we change channels.
+ */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == ic->ic_curchan->ic_freq)
+ c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
+ }
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_EXPIRE);
+ ieee80211_cac_completeswitch(vap);
+ }
+}
+
+/*
+ * Initiate the CAC timer. The driver is responsible
+ * for setting up the hardware to scan for radar on the
+ * channnel, we just handle timing things out.
+ */
+void
+ieee80211_dfs_cac_start(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
+ if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
+ ticks_to_secs(CAC_TIMEOUT),
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
+}
+
+/*
+ * Clear the CAC timer.
+ */
+void
+ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /* NB: racey but not important */
+ if (callout_pending(&dfs->cac_timer)) {
+ if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_STOP);
+ }
+ /* XXX cannot use drain 'cuz holding a lock */
+ callout_stop(&dfs->cac_timer);
+}
+
+void
+ieee80211_dfs_cac_clear(struct ieee80211com *ic,
+ const struct ieee80211_channel *chan)
+{
+ int i;
+
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq)
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ }
+}
+
+static void
+dfs_timeout(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ struct ieee80211_channel *c;
+ int i, oldest, now;
+
+ IEEE80211_LOCK(ic);
+ now = oldest = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (IEEE80211_IS_CHAN_RADAR(c)) {
+ if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
+ if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
+ /*
+ * NB: do this here so we get only one
+ * msg instead of one for every channel
+ * table entry.
+ */
+ if_printf(ic->ic_ifp, "radar on channel"
+ " %u (%u MHz) cleared after timeout\n",
+ c->ic_ieee, c->ic_freq);
+ /* notify user space */
+ c->ic_state &=
+ ~IEEE80211_CHANSTATE_NORADAR;
+ ieee80211_notify_radar(ic, c);
+ }
+ } else if (dfs->nol_event[i] < oldest)
+ oldest = dfs->nol_event[i];
+ }
+ }
+ if (oldest != now) {
+ /* arrange to process next channel up for a status change */
+ callout_reset(&dfs->nol_timer, oldest + NOL_TIMEOUT,
+ dfs_timeout, ic);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
+ const struct ieee80211_channel *newchan)
+{
+ if (newchan == NULL)
+ if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq);
+ else
+ if_printf(ifp, "radar detected on channel %u (%u MHz), "
+ "moving to channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq,
+ newchan->ic_ieee, newchan->ic_freq);
+}
+
+/*
+ * Handle a radar detection event on a channel. The channel is
+ * added to the NOL list and we record the time of the event.
+ * Entries are aged out after NOL_TIMEOUT. If radar was
+ * detected while doing CAC we force a state/channel change.
+ * Otherwise radar triggers a channel switch using the CSA
+ * mechanism (when the channel is the bss channel).
+ */
+void
+ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i, now;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /*
+ * Mark all entries with this frequency. Notify user
+ * space and arrange for notification when the radar
+ * indication is cleared. Then kick the NOL processing
+ * thread if not already running.
+ */
+ now = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ c->ic_state |= IEEE80211_CHANSTATE_RADAR;
+ dfs->nol_event[i] = now;
+ }
+ }
+ ieee80211_notify_radar(ic, chan);
+ chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
+ if (!callout_pending(&dfs->nol_timer))
+ callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
+
+ /*
+ * If radar is detected on the bss channel while
+ * doing CAC; force a state change by scheduling the
+ * callout to be dispatched asap. Otherwise, if this
+ * event is for the bss channel then we must quiet
+ * traffic and schedule a channel switch.
+ *
+ * Note this allows us to receive notification about
+ * channels other than the bss channel; not sure
+ * that can/will happen but it's simple to support.
+ */
+ if (chan == ic->ic_bsschan) {
+ /* XXX need a way to defer to user app */
+ dfs->newchan = ieee80211_dfs_pickchannel(ic);
+
+ announce_radar(ic->ic_ifp, chan, dfs->newchan);
+
+ if (callout_pending(&dfs->cac_timer))
+ callout_reset(&dfs->nol_timer, 0, dfs_timeout, ic);
+ else if (dfs->newchan != NULL) {
+ /* XXX mode 1, switch count 2 */
+ /* XXX calculate switch count based on max
+ switch time and beacon interval? */
+ ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
+ } else {
+ /*
+ * Spec says to stop all transmissions and
+ * wait on the current channel for an entry
+ * on the NOL to expire.
+ */
+ /*XXX*/
+ }
+ } else {
+ /*
+ * Issue rate-limited console msgs.
+ */
+ if (dfs->lastchan != chan) {
+ dfs->lastchan = chan;
+ dfs->cureps = 0;
+ announce_radar(ic->ic_ifp, chan, NULL);
+ } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
+ announce_radar(ic->ic_ifp, chan, NULL);
+ }
+ }
+}
+
+struct ieee80211_channel *
+ieee80211_dfs_pickchannel(struct ieee80211com *ic)
+{
+ struct ieee80211_channel *c;
+ int i, flags;
+ uint16_t v;
+
+ /*
+ * Consult the scan cache first.
+ */
+ flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
+ /*
+ * XXX if curchan is HT this will never find a channel
+ * XXX 'cuz we scan only legacy channels
+ */
+ c = ieee80211_scan_pickchannel(ic, flags);
+ if (c != NULL)
+ return c;
+ /*
+ * No channel found in scan cache; select a compatible
+ * one at random (skipping channels where radar has
+ * been detected).
+ */
+ get_random_bytes(&v, sizeof(v));
+ v %= ic->ic_nchans;
+ for (i = v; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ for (i = 0; i < v; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
+ return NULL;
+}
diff --git a/sys/net80211/ieee80211_dfs.h b/sys/net80211/ieee80211_dfs.h
new file mode 100644
index 0000000..36d14aa
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_DFS_H_
+#define _NET80211_IEEE80211_DFS_H_
+
+/*
+ * 802.11h/DFS definitions.
+ */
+
+struct ieee80211_dfs_state {
+ int nol_event[IEEE80211_CHAN_MAX+1];
+ struct callout nol_timer; /* NOL list processing */
+ struct callout cac_timer; /* CAC timer */
+ struct timeval lastevent; /* time of last radar event */
+ int cureps; /* current events/second */
+ const struct ieee80211_channel *lastchan;/* chan w/ last radar event */
+ struct ieee80211_channel *newchan; /* chan selected next */
+};
+
+void ieee80211_dfs_attach(struct ieee80211com *);
+void ieee80211_dfs_detach(struct ieee80211com *);
+
+void ieee80211_dfs_reset(struct ieee80211com *);
+
+void ieee80211_dfs_cac_start(struct ieee80211vap *);
+void ieee80211_dfs_cac_stop(struct ieee80211vap *);
+void ieee80211_dfs_cac_clear(struct ieee80211com *,
+ const struct ieee80211_channel *);
+
+void ieee80211_dfs_notify_radar(struct ieee80211com *,
+ struct ieee80211_channel *);
+struct ieee80211_channel *ieee80211_dfs_pickchannel(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_DFS_H_ */
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
index 1cb7813..d0b1b69 100644
--- a/sys/net80211/ieee80211_freebsd.c
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 support (FreeBSD-specific code)
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -41,7 +43,9 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_clone.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net/route.h>
@@ -57,24 +61,112 @@ SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
extern int ieee80211_recv_bar_ena;
SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
0, "BAR frame processing (ena/dis)");
+extern int ieee80211_nol_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
+ &ieee80211_nol_timeout, 0, "NOL timeout (secs)");
+extern int ieee80211_cac_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
+ &ieee80211_cac_timeout, 0, "CAC timeout (secs)");
+
+MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state");
+
+/*
+ * Allocate/free com structure in conjunction with ifnet;
+ * these routines are registered with if_register_com_alloc
+ * below and are called automatically by the ifnet code
+ * when the ifnet of the parent device is created.
+ */
+static void *
+wlan_alloc(u_char type, struct ifnet *ifp)
+{
+ struct ieee80211com *ic;
+
+ ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO);
+ ic->ic_ifp = ifp;
+
+ return (ic);
+}
+
+static void
+wlan_free(void *ic, u_char type)
+{
+ free(ic, M_80211_COM);
+}
-#ifdef IEEE80211_AMPDU_AGE
static int
-ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
+wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
{
- extern int ieee80211_ampdu_age;
- int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
+ struct ieee80211_clone_params cp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
int error;
- error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
+ error = copyin(params, &cp, sizeof(cp));
+ if (error)
+ return error;
+ ifp = ifunit(cp.icp_parent);
+ if (ifp == NULL)
+ return ENXIO;
+ if (ifp->if_type != IFT_IEEE80211) {
+ if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__);
+ return EINVAL;
+ }
+ ic = ifp->if_l2com;
+ vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
+ cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
+ cp.icp_flags & IEEE80211_CLONE_MACADDR ?
+ cp.icp_macaddr : ic->ic_myaddr);
+ return (vap == NULL ? EIO : 0);
+}
+
+static void
+wlan_clone_destroy(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ic->ic_vap_delete(vap);
+}
+IFC_SIMPLE_DECLARE(wlan, 0);
+
+void
+ieee80211_vap_destroy(struct ieee80211vap *vap)
+{
+ ifc_simple_destroy(&wlan_cloner, vap->iv_ifp);
+}
+
+static int
+ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
+{
+ int msecs = ticks_to_msecs(*(int *)arg1);
+ int error, t;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
if (error || !req->newptr)
return error;
- ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
+ t = msecs_to_ticks(msecs);
+ *(int *)arg1 = (t < 1) ? 1 : t;
return 0;
}
-SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
- ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
+
+#ifdef IEEE80211_AMPDU_AGE
+extern int ieee80211_ampdu_age;
+SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW,
+ &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "AMPDU max reorder age (ms)");
#endif
+extern int ieee80211_addba_timeout;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW,
+ &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request timeout (ms)");
+extern int ieee80211_addba_backoff;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW,
+ &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request backoff (ms)");
+extern int ieee80211_addba_maxtries;
+SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
+ &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
static int
ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
@@ -101,6 +193,17 @@ ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
void
ieee80211_sysctl_attach(struct ieee80211com *ic)
{
+}
+
+void
+ieee80211_sysctl_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_sysctl_vattach(struct ieee80211vap *vap)
+{
+ struct ifnet *ifp = vap->iv_ifp;
struct sysctl_ctx_list *ctx;
struct sysctl_oid *oid;
char num[14]; /* sufficient for 32 bits */
@@ -108,57 +211,76 @@ ieee80211_sysctl_attach(struct ieee80211com *ic)
MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
+ if_printf(ifp, "%s: cannot allocate sysctl context!\n",
__func__);
return;
}
sysctl_ctx_init(ctx);
- snprintf(num, sizeof(num), "%u", ic->ic_vap);
+ snprintf(num, sizeof(num), "%u", ifp->if_dunit);
oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
OID_AUTO, num, CTLFLAG_RD, NULL, "");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
- "parent device");
+ "%parent", CTLFLAG_RD, vap->iv_ic, 0,
+ ieee80211_sysctl_parent, "A", "parent device");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0,
+ "driver capabilities");
#ifdef IEEE80211_DEBUG
- ic->ic_debug = ieee80211_debug;
+ vap->iv_debug = ieee80211_debug;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "debug", CTLFLAG_RW, &ic->ic_debug, 0,
+ "debug", CTLFLAG_RW, &vap->iv_debug, 0,
"control debugging printfs");
#endif
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0,
+ "consecutive beacon misses before scanning");
/* XXX inherit from tunables */
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
+ "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0,
ieee80211_sysctl_inact, "I",
"station inactivity timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
+ "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0,
ieee80211_sysctl_inact, "I",
"station inactivity probe timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
+ "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0,
ieee80211_sysctl_inact, "I",
"station authentication timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
+ "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0,
ieee80211_sysctl_inact, "I",
"station initial state timeout (sec)");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
- "driver capabilities");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0,
- "consecutive beacon misses before scanning");
- ic->ic_sysctl = ctx;
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_bk", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BK], 0,
+ "BK traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_be", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BE], 0,
+ "BE traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vo", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VO], 0,
+ "VO traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vi", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VI], 0,
+ "VI traffic tx aggr threshold (pps)");
+ }
+ vap->iv_sysctl = ctx;
+ vap->iv_oid = oid;
}
void
-ieee80211_sysctl_detach(struct ieee80211com *ic)
+ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_sysctl != NULL) {
- sysctl_ctx_free(ic->ic_sysctl);
- FREE(ic->ic_sysctl, M_DEVBUF);
- ic->ic_sysctl = NULL;
+ if (vap->iv_sysctl != NULL) {
+ sysctl_ctx_free(vap->iv_sysctl);
+ FREE(vap->iv_sysctl, M_DEVBUF);
+ vap->iv_sysctl = NULL;
}
}
@@ -190,6 +312,33 @@ ieee80211_drain_ifq(struct ifqueue *ifq)
}
}
+void
+ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m, **mprev;
+
+ IF_LOCK(ifq);
+ mprev = &ifq->ifq_head;
+ while ((m = *mprev) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (ni != NULL && ni->ni_vap == vap) {
+ *mprev = m->m_nextpkt; /* remove from list */
+ ifq->ifq_len--;
+
+ m_freem(m);
+ ieee80211_free_node(ni); /* reclaim ref */
+ } else
+ mprev = &m->m_nextpkt;
+ }
+ /* recalculate tail ptr */
+ m = ifq->ifq_head;
+ for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt)
+ ;
+ ifq->ifq_tail = m;
+ IF_UNLOCK(ifq);
+}
+
/*
* As above, for mbufs allocated with m_gethdr/MGETHDR
* or initialized by M_COPY_PKTHDR.
@@ -290,66 +439,78 @@ get_random_bytes(void *p, size_t n)
}
}
-void
-ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
+/*
+ * Helper function for events that pass just a single mac address.
+ */
+static void
+notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_join_event iev;
memset(&iev, 0, sizeof(iev));
- if (ni == ic->ic_bss) {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
- &iev, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, mac);
+ rt_ieee80211msg(ifp, op, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid);
if_link_state_change(ifp, LINK_STATE_UP);
} else {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
- &iev, sizeof(iev));
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr);
}
}
void
-ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_notify_node_leave(struct ieee80211_node *ni)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_leave_event iev;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
- if (ni == ic->ic_bss) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
if_link_state_change(ifp, LINK_STATE_DOWN);
} else {
/* fire off wireless event station leaving */
- memset(&iev, 0, sizeof(iev));
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
+ notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr);
}
}
void
-ieee80211_notify_scan_done(struct ieee80211com *ic)
+ieee80211_notify_scan_done(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
/* dispatch wireless event indicating scan completed */
rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
}
void
-ieee80211_notify_replay_failure(struct ieee80211com *ic,
+ieee80211_notify_replay_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, const struct ieee80211_key *k,
u_int64_t rsc)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
- ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
- (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
+ k->wk_cipher->ic_name, (intmax_t) rsc,
+ (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID],
k->wk_keyix, k->wk_rxkeyix);
if (ifp != NULL) { /* NB: for cipher test modules */
@@ -362,22 +523,21 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic,
iev.iev_keyix = k->wk_rxkeyix;
else
iev.iev_keyix = k->wk_keyix;
- iev.iev_keyrsc = k->wk_keyrsc;
+ iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */
iev.iev_rsc = rsc;
rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
}
}
void
-ieee80211_notify_michael_failure(struct ieee80211com *ic,
+ieee80211_notify_michael_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, u_int keyix)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] michael MIC verification failed <keyix %u>\n",
- ether_sprintf(wh->i_addr2), keyix);
- ic->ic_stats.is_rx_tkipmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "michael MIC verification failed <keyix %u>", keyix);
+ vap->iv_stats.is_rx_tkipmic++;
if (ifp != NULL) { /* NB: for cipher test modules */
struct ieee80211_michael_event iev;
@@ -391,6 +551,107 @@ ieee80211_notify_michael_failure(struct ieee80211com *ic,
}
void
+ieee80211_notify_wds_discover(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_csa(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, int mode, int count)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_csa_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_mode = mode;
+ iev.iev_count = count;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radar(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radar_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_cac(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_cac_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_type = type;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_deauth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_node_auth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_country(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_country_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
+ iev.iev_cc[0] = cc[0];
+ iev.iev_cc[1] = cc[1];
+ rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radio(struct ieee80211com *ic, int state)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radio_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_state = state;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+}
+
+void
ieee80211_load_module(const char *modname)
{
@@ -413,8 +674,12 @@ wlan_modevent(module_t mod, int type, void *unused)
case MOD_LOAD:
if (bootverbose)
printf("wlan: <802.11 Link Layer>\n");
+ if_clone_attach(&wlan_cloner);
+ if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free);
return 0;
case MOD_UNLOAD:
+ if_deregister_com_alloc(IFT_IEEE80211);
+ if_clone_detach(&wlan_cloner);
return 0;
}
return EINVAL;
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 0052dfe..bccc0cf 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,12 +28,18 @@
#define _NET80211_IEEE80211_FREEBSD_H_
#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+
/*
* Common state locking definitions.
*/
typedef struct mtx ieee80211_com_lock_t;
#define IEEE80211_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF)
+ mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", \
+ MTX_DEF | MTX_RECURSE)
#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock)
#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock)
#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock)
@@ -41,43 +47,61 @@ typedef struct mtx ieee80211_com_lock_t;
mtx_assert(&(_ic)->ic_comlock, MA_OWNED)
/*
- * Beacon locking definitions.
- */
-typedef struct mtx ieee80211_beacon_lock_t;
-#define IEEE80211_BEACON_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_beaconlock, _name, "802.11 beacon lock", MTX_DEF)
-#define IEEE80211_BEACON_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK(_ic) mtx_lock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK_ASSERT(_ic) \
- mtx_assert(&(_ic)->ic_beaconlock, MA_OWNED)
-
-/*
* Node locking definitions.
- * NB: MTX_DUPOK is because we don't generate per-interface strings.
*/
-typedef struct mtx ieee80211_node_lock_t;
-#define IEEE80211_NODE_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", \
- MTX_DEF | MTX_DUPOK)
-#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_IS_LOCKED(_nt) mtx_owned(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_nodelock, MA_OWNED)
+typedef struct {
+ char name[16]; /* e.g. "ath0_node_lock" */
+ struct mtx mtx;
+} ieee80211_node_lock_t;
+#define IEEE80211_NODE_LOCK_INIT(_nt, _name) do { \
+ ieee80211_node_lock_t *nl = &(_nt)->nt_nodelock; \
+ snprintf(nl->name, sizeof(nl->name), "%s_node_lock", _name); \
+ mtx_init(&nl->mtx, NULL, nl->name, MTX_DEF | MTX_RECURSE); \
+} while (0)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_IS_LOCKED(_nt) \
+ mtx_owned(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
+ mtx_assert(&(_nt)->nt_nodelock.mtx, MA_OWNED)
/*
- * Node table scangen locking definitions.
+ * Node table iteration locking definitions; this protects the
+ * scan generation # used to iterate over the station table
+ * while grabbing+releasing the node lock.
*/
-typedef struct mtx ieee80211_scan_lock_t;
-#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF)
-#define IEEE80211_SCAN_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK(_nt) mtx_lock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_scanlock, MA_OWNED)
+typedef struct {
+ char name[16]; /* e.g. "ath0_scan_lock" */
+ struct mtx mtx;
+} ieee80211_scan_lock_t;
+#define IEEE80211_NODE_ITERATE_LOCK_INIT(_nt, _name) do { \
+ ieee80211_scan_lock_t *sl = &(_nt)->nt_scanlock; \
+ snprintf(sl->name, sizeof(sl->name), "%s_scan_lock", _name); \
+ mtx_init(&sl->mtx, NULL, sl->name, MTX_DEF); \
+} while (0)
+#define IEEE80211_NODE_ITERATE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_scanlock.mtx)
+
+#define _AGEQ_ENQUEUE(_ifq, _m, _qlen, _age) do { \
+ (_m)->m_nextpkt = NULL; \
+ if ((_ifq)->ifq_tail != NULL) { \
+ _age -= M_AGE_GET((_ifq)->ifq_tail); \
+ (_ifq)->ifq_tail->m_nextpkt = (_m); \
+ } else { \
+ (_ifq)->ifq_head = (_m); \
+ } \
+ M_AGE_SET(_m, _age); \
+ (_ifq)->ifq_tail = (_m); \
+ (_qlen) = ++(_ifq)->ifq_len; \
+} while (0)
/*
* Per-node power-save queue definitions.
@@ -113,16 +137,7 @@ typedef struct mtx ieee80211_scan_lock_t;
_IF_DEQUEUE(&(_ni)->ni_savedq, m); \
} while (0)
#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\
- (_m)->m_nextpkt = NULL; \
- if ((_ni)->ni_savedq.ifq_tail != NULL) { \
- _age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail); \
- (_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m); \
- } else { \
- (_ni)->ni_savedq.ifq_head = (_m); \
- } \
- M_AGE_SET(_m, _age); \
- (_ni)->ni_savedq.ifq_tail = (_m); \
- (_qlen) = ++(_ni)->ni_savedq.ifq_len; \
+ _AGEQ_ENQUEUE(&ni->ni_savedq, _m, _qlen, _age); \
} while (0)
#define IEEE80211_TAPQ_INIT(_tap) do { \
@@ -147,6 +162,24 @@ typedef struct mtx ieee80211_scan_lock_t;
} while (0)
#endif /* IF_PREPEND_LIST */
+/* XXX temporary */
+#define IEEE80211_NODE_WDSQ_INIT(_ni, _name) do { \
+ mtx_init(&(_ni)->ni_wdsq.ifq_mtx, _name, "802.11 wds queue", MTX_DEF);\
+ (_ni)->ni_wdsq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \
+} while (0)
+#define IEEE80211_NODE_WDSQ_DESTROY(_ni) do { \
+ mtx_destroy(&(_ni)->ni_wdsq.ifq_mtx); \
+} while (0)
+#define IEEE80211_NODE_WDSQ_QLEN(_ni) _IF_QLEN(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_LOCK(_ni) IF_LOCK(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_UNLOCK(_ni) IF_UNLOCK(&(_ni)->ni_wdsq)
+#define _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(_ni, _m) do { \
+ _IF_DEQUEUE(&(_ni)->ni_wdsq, m); \
+} while (0)
+#define _IEEE80211_NODE_WDSQ_ENQUEUE(_ni, _m, _qlen, _age) do { \
+ _AGEQ_ENQUEUE(&ni->ni_wdsq, _m, _qlen, _age); \
+} while (0)
+
/*
* 802.1x MAC ACL database locking definitions.
*/
@@ -182,43 +215,53 @@ int ieee80211_node_dectestref(struct ieee80211_node *ni);
#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
struct ifqueue;
+struct ieee80211vap;
void ieee80211_drain_ifq(struct ifqueue *);
+void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *);
+
+void ieee80211_vap_destroy(struct ieee80211vap *);
+
+#define IFNET_IS_UP_RUNNING(_ifp) \
+ (((_ifp)->if_flags & IFF_UP) && \
+ ((_ifp)->if_drv_flags & IFF_DRV_RUNNING))
#define msecs_to_ticks(ms) (((ms)*hz)/1000)
-#define ticks_to_msecs(t) ((t) / hz)
+#define ticks_to_msecs(t) (1000*(t) / hz)
+#define ticks_to_secs(t) ((t) / hz)
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
#define time_before_eq(a,b) time_after_eq(b,a)
+#define memmove(dst, src, n) ovbcopy(src, dst, n)
+
struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
/* tx path usage */
#define M_LINK0 M_PROTO1 /* WEP requested */
+#define M_WDS M_PROTO2 /* WDS frame */
+#define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */
#define M_PWR_SAV M_PROTO4 /* bypass PS handling */
#define M_MORE_DATA M_PROTO5 /* more data frames to follow */
-#define M_FF 0x20000 /* fast frame */
-#define M_TXCB 0x40000 /* do tx complete callback */
-#define M_80211_TX (0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5)
+#define M_FF M_PROTO6 /* fast frame */
+#define M_TXCB M_PROTO7 /* do tx complete callback */
+#define M_80211_TX \
+ (M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB)
/* rx path usage */
#define M_AMPDU M_PROTO1 /* A-MPDU processing done */
#define M_WEP M_PROTO2 /* WEP done by hardware */
#define M_80211_RX (M_AMPDU|M_WEP)
/*
- * Encode WME access control bits in the PROTO flags.
- * This is safe since it's passed directly in to the
- * driver and there's no chance someone else will clobber
- * them on us.
+ * Store WME access control bits in the vlan tag.
+ * This is safe since it's done after the packet is classified
+ * (where we use any previous tag) and because it's passed
+ * directly in to the driver and there's no chance someone
+ * else will clobber them on us.
*/
-#define M_WME_AC_MASK (M_PROTO2|M_PROTO3)
-/* XXX 5 is wrong if M_PROTO* are redefined */
-#define M_WME_AC_SHIFT 5
-
#define M_WME_SETAC(m, ac) \
- ((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \
- ((ac) << M_WME_AC_SHIFT))
-#define M_WME_GETAC(m) (((m)->m_flags >> M_WME_AC_SHIFT) & 0x3)
+ ((m)->m_pkthdr.ether_vtag = (ac))
+#define M_WME_GETAC(m) ((m)->m_pkthdr.ether_vtag)
/*
* Mbufs on the power save queue are tagged with an age and
@@ -246,16 +289,29 @@ struct ieee80211com;
void ieee80211_sysctl_attach(struct ieee80211com *);
void ieee80211_sysctl_detach(struct ieee80211com *);
+void ieee80211_sysctl_vattach(struct ieee80211vap *);
+void ieee80211_sysctl_vdetach(struct ieee80211vap *);
void ieee80211_load_module(const char *);
-#define IEEE80211_CRYPTO_MODULE(name, version) \
+/*
+ * A "policy module" is an adjunct module to net80211 that provides
+ * functionality that typically includes policy decisions. This
+ * modularity enables extensibility and vendor-supplied functionality.
+ */
+#define _IEEE80211_POLICY_MODULE(policy, name, version) \
+typedef void (*policy##_setup)(int); \
+SET_DECLARE(policy##_set, policy##_setup); \
static int \
-name##_modevent(module_t mod, int type, void *unused) \
+wlan_##name##_modevent(module_t mod, int type, void *unused) \
{ \
+ policy##_setup * const *iter, f; \
switch (type) { \
case MOD_LOAD: \
- ieee80211_crypto_register(&name); \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
return 0; \
case MOD_UNLOAD: \
case MOD_QUIESCE: \
@@ -264,20 +320,100 @@ name##_modevent(module_t mod, int type, void *unused) \
nrefs); \
return EBUSY; \
} \
- if (type == MOD_UNLOAD) \
- ieee80211_crypto_unregister(&name); \
+ if (type == MOD_UNLOAD) { \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
+ } \
return 0; \
} \
return EINVAL; \
} \
static moduledata_t name##_mod = { \
"wlan_" #name, \
- name##_modevent, \
+ wlan_##name##_modevent, \
0 \
}; \
DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
MODULE_VERSION(wlan_##name, version); \
MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1)
+
+/*
+ * Crypto modules implement cipher support.
+ */
+#define IEEE80211_CRYPTO_MODULE(name, version) \
+_IEEE80211_POLICY_MODULE(crypto, name, version); \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_crypto_register(&name); \
+ else \
+ ieee80211_crypto_unregister(&name); \
+} \
+TEXT_SET(crypto##_set, name##_modevent)
+
+/*
+ * Scanner modules provide scanning policy.
+ */
+#define IEEE80211_SCANNER_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(scanner, name, version)
+
+#define IEEE80211_SCANNER_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_scanner_register(alg, &v); \
+ else \
+ ieee80211_scanner_unregister(alg, &v); \
+} \
+TEXT_SET(scanner_set, name##_modevent); \
+
+/*
+ * ACL modules implement acl policy.
+ */
+#define IEEE80211_ACL_MODULE(name, alg, version) \
+_IEEE80211_POLICY_MODULE(acl, name, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_aclator_register(&alg); \
+ else \
+ ieee80211_aclator_unregister(&alg); \
+} \
+TEXT_SET(acl_set, alg##_modevent); \
+
+/*
+ * Authenticator modules handle 802.1x/WPA authentication.
+ */
+#define IEEE80211_AUTH_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(auth, name, version)
+
+#define IEEE80211_AUTH_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_authenticator_register(alg, &v); \
+ else \
+ ieee80211_authenticator_unregister(alg); \
+} \
+TEXT_SET(auth_set, name##_modevent)
+
+/*
+ * Rate control modules provide tx rate control support.
+ */
+#define IEEE80211_RATE_MODULE(alg, version) \
+_IEEE80211_POLICY_MODULE(rate, alg, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ /* XXX nothing to do until the rate control framework arrives */\
+} \
+TEXT_SET(rate##_set, alg##_modevent)
#endif /* _KERNEL */
/* XXX this stuff belongs elsewhere */
@@ -310,6 +446,50 @@ struct ieee80211_michael_event {
uint8_t iev_keyix; /* key id/index */
};
+struct ieee80211_wds_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_csa_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ uint8_t iev_mode; /* CSA mode */
+ uint8_t iev_count; /* CSA count */
+};
+
+struct ieee80211_cac_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+ uint8_t iev_type; /* IEEE80211_NOTIFY_CAC_* */
+};
+
+struct ieee80211_radar_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+};
+
+struct ieee80211_auth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_deauth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_country_event {
+ uint8_t iev_addr[6];
+ uint8_t iev_cc[2]; /* ISO country code */
+};
+
+struct ieee80211_radio_event {
+ uint8_t iev_state; /* 1 on, 0 off */
+};
+
#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */
#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */
#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */
@@ -319,6 +499,14 @@ struct ieee80211_michael_event {
#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */
#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */
#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */
+#define RTM_IEEE80211_WDS 109 /* WDS discovery (ap mode) */
+#define RTM_IEEE80211_CSA 110 /* Channel Switch Announcement event */
+#define RTM_IEEE80211_RADAR 111 /* radar event */
+#define RTM_IEEE80211_CAC 112 /* Channel Availability Check event */
+#define RTM_IEEE80211_DEAUTH 113 /* station deauthenticate */
+#define RTM_IEEE80211_AUTH 114 /* station authenticate (ap mode) */
+#define RTM_IEEE80211_COUNTRY 115 /* discovered country code (sta mode) */
+#define RTM_IEEE80211_RADIO 116 /* RF kill switch state change */
/*
* Structure prepended to raw packets sent through the bpf
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
new file mode 100644
index 0000000..3c15505
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.c
@@ -0,0 +1,2236 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 HOSTAP mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void hostap_vattach(struct ieee80211vap *);
+static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+static void hostap_deliver_data(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *);
+
+void
+ieee80211_hostap_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach;
+}
+
+void
+ieee80211_hostap_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+hostap_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+hostap_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = hostap_newstate;
+ vap->iv_input = hostap_input;
+ vap->iv_recv_mgmt = hostap_recv_mgmt;
+ vap->iv_opdetach = hostap_vdetach;
+ vap->iv_deliver_data = hostap_deliver_data;
+}
+
+static void
+sta_disassoc(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (ni->ni_vap == vap && ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_node_leave(ni);
+ }
+}
+
+/*
+ * IEEE80211_M_HOSTAP vap state machine handler.
+ */
+static int
+hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_S_CAC:
+ ieee80211_dfs_cac_stop(vap);
+ break;
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_CSA:
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ /*
+ * Clear overlapping BSS state; the beacon frame
+ * will be reconstructed on transition to the RUN
+ * state and the timeout routines check if the flag
+ * is set before doing anything so this is sufficient.
+ */
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
+ /* fall thru... */
+ case IEEE80211_S_CAC:
+ /*
+ * NB: We may get here because of a manual channel
+ * change in which case we need to stop CAC
+ * XXX no need to stop if ostate RUN but it's ok
+ */
+ ieee80211_dfs_cac_stop(vap);
+ /* fall thru... */
+ case IEEE80211_S_INIT:
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * ieee80211_create_ibss will call back to
+ * move us to RUN state.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * A state change requires a reset; scan.
+ */
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * Start CAC on a DFS channel. We come here when starting
+ * a bss on a DFS channel (see ieee80211_create_ibss).
+ */
+ ieee80211_dfs_cac_start(vap);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * Note that ieee80211_create_ibss will call
+ * back to do a RUN->RUN state change.
+ */
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic,
+ ic->ic_curchan, vap->iv_flags_ext));
+ /* NB: iv_bss is changed on return */
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * NB: This is the normal state change when CAC
+ * expires and no radar was detected; no need to
+ * clear the CAC timer as it's already expired.
+ */
+ /* fall thru... */
+ case IEEE80211_S_CSA:
+ /*
+ * Update bss node channel to reflect where
+ * we landed after CSA.
+ */
+ ieee80211_node_set_chan(vap->iv_bss,
+ ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ ieee80211_htchanflags(vap->iv_bss->ni_chan)));
+ /* XXX bypass debug msgs */
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_RUN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(ni->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ /*
+ * Start/stop the authenticator. We delay until here
+ * to allow configuration to happen out of order.
+ */
+ if (vap->iv_auth->ia_attach != NULL) {
+ /* XXX check failure */
+ vap->iv_auth->ia_attach(vap);
+ } else if (vap->iv_auth->ia_detach != NULL) {
+ vap->iv_auth->ia_detach(vap);
+ }
+ ieee80211_node_authorize(vap->iv_bss);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void
+hostap_deliver_data(struct ieee80211vap *vap,
+ struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ether_header *eh = mtod(m, struct ether_header *);
+ struct ifnet *ifp = vap->iv_ifp;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("gack, opmode %d", vap->iv_opmode));
+ /*
+ * Do accounting.
+ */
+ ifp->if_ipackets++;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ m->m_flags |= M_MCAST; /* XXX M_BCAST? */
+ IEEE80211_NODE_STAT(ni, rx_mcast);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_ucast);
+
+ /* clear driver/net80211 flags before passing up */
+ m->m_flags &= ~M_80211_RX;
+
+ /* perform as a bridge within the AP */
+ if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ struct mbuf *mcopy = NULL;
+
+ if (m->m_flags & M_MCAST) {
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL)
+ ifp->if_oerrors++;
+ else
+ mcopy->m_flags |= M_MCAST;
+ } else {
+ /*
+ * Check if the destination is associated with the
+ * same vap and authorized to receive traffic.
+ * Beware of traffic destined for the vap itself;
+ * sending it will not work; just let it be delivered
+ * normally.
+ */
+ struct ieee80211_node *sta = ieee80211_find_vap_node(
+ &vap->iv_ic->ic_sta, vap, eh->ether_dhost);
+ if (sta != NULL) {
+ if (ieee80211_node_is_authorized(sta)) {
+ /*
+ * Beware of sending to ourself; this
+ * needs to happen via the normal
+ * input path.
+ */
+ if (sta != vap->iv_bss) {
+ mcopy = m;
+ m = NULL;
+ }
+ } else {
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(sta, rx_unauth);
+ }
+ ieee80211_free_node(sta);
+ }
+ }
+ if (mcopy != NULL) {
+ int len, err;
+ len = mcopy->m_pkthdr.len;
+ IFQ_HANDOFF(ifp, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mcopy */
+ } else {
+ ifp->if_opackets++;
+ }
+ }
+ }
+ if (m != NULL) {
+ /*
+ * Mark frame as coming from vap's interface.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ if (m->m_flags & M_MCAST) {
+ /*
+ * Spam DWDS vap's w/ multicast traffic.
+ */
+ /* XXX only if dwds in use? */
+ ieee80211_dwds_mcast(vap, m);
+ }
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
+ }
+ ifp->if_input(ifp, m);
+ }
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!(type == IEEE80211_FC0_TYPE_MGT &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
+ !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (!(dir == IEEE80211_FC1_DIR_TODS ||
+ (dir == IEEE80211_FC1_DIR_DSTODS &&
+ (vap->iv_flags & IEEE80211_F_DWDS)))) {
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "data",
+ "incorrect dir 0x%x", dir);
+ } else {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "DWDS not enabled");
+ }
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* check if source STA is associated */
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unknown src");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_AUTHED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unassoc src");
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_NOT_ASSOCED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+
+ /*
+ * Check for power save state change.
+ * XXX out-of-order A-MPDU frames?
+ */
+ if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+ ieee80211_node_pwrsave(ni,
+ wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
+ /*
+ * For 4-address packets handle WDS discovery
+ * notifications. Once a WDS link is setup frames
+ * are just delivered to the WDS vap (see below).
+ */
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) {
+ if (!ieee80211_node_is_authorized(ni)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "unauthorized port");
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ ieee80211_dwds_discover(ni, m);
+ return type;
+ }
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ hostap_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* ensure return frames are unicast */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "source is multicast: %s",
+ ether_sprintf(wh->i_addr2));
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ hostap_recv_pspoll(ni, m);
+ break;
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(ni, m);
+ break;
+ }
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ /*
+ * Clear any challenge text that may be there if
+ * a previous shared key auth failed and then an
+ * open auth is attempted.
+ */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (IEEE80211_STATUS_ALG<<16));
+ return;
+ }
+ if (seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ /* always accept open authentication requests */
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL)
+ return;
+ } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr,
+ "%s", "station authenticated (open)");
+ /*
+ * When 802.1x is not in use mark the port
+ * authorized at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ }
+}
+
+static void
+hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int allocbs, estatus;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like Mac OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_REQUEST:
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: no way to return an error */
+ return;
+ }
+ allocbs = 1;
+ } else {
+ if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ allocbs = 0;
+ }
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (!ieee80211_alloc_challenge(ni)) {
+ /* NB: don't return error so they rexmit */
+ return;
+ }
+ get_random_bytes(ni->ni_challenge,
+ IEEE80211_CHALLENGE_LEN);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "shared key %sauth request", allocbs ? "" : "re");
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL,
+ ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ return;
+ }
+ break;
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "unknown station");
+ /* NB: don't send a response */
+ return;
+ }
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "no challenge recorded");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (memcmp(ni->ni_challenge, &challenge[2],
+ challenge[1]) != 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "challenge mismatch");
+ vap->iv_stats.is_rx_auth_fail++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "%s", "station authenticated (shared key)");
+ ieee80211_node_authorize(ni);
+ break;
+ default:
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_SEQUENCE;
+ goto bad;
+ }
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ return;
+bad:
+ /*
+ * Send an error response; but only when operating as an AP.
+ */
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (estatus<<16));
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case WPA_SEL(WPA_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case WPA_SEL(WPA_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(const uint8_t *sel)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+ return WPA_ASE_8021X_UNSPEC;
+ case WPA_SEL(WPA_ASE_8021X_PSK):
+ return WPA_ASE_8021X_PSK;
+ case WPA_SEL(WPA_ASE_NONE):
+ return WPA_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters.
+ * Note that we do not validate security parameters; that
+ * is handled by the authenticator; the parsing done here
+ * is just for internal use in making operational decisions.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts: OUI, type,
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 14) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 6, len -= 4; /* NB: len is payload only */
+ /* NB: iswapoui already validated the OUI and type */
+ w = LE_READ_2(frm);
+ if (w != WPA_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= wpa_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & WPA_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+ if (len > 2) /* optional capabilities */
+ rsn->rsn_caps = LE_READ_2(frm);
+
+ return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case RSN_SEL(RSN_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case RSN_SEL(RSN_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ case RSN_SEL(RSN_CSE_WRAP):
+ return IEEE80211_CIPHER_AES_OCB;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(const uint8_t *sel)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+ return RSN_ASE_8021X_UNSPEC;
+ case RSN_SEL(RSN_ASE_8021X_PSK):
+ return RSN_ASE_8021X_PSK;
+ case RSN_SEL(RSN_ASE_NONE):
+ return RSN_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts:
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 10) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2;
+ w = LE_READ_2(frm);
+ if (w != RSN_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= rsn_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & RSN_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+ /* optional RSN capabilities */
+ if (len > 2)
+ rsn->rsn_caps = LE_READ_2(frm);
+ /* XXXPMKID */
+
+ return 0;
+}
+
+/*
+ * WPA/802.11i assocation request processing.
+ */
+static int
+wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms,
+ const struct ieee80211_frame *wh, const uint8_t *wpa,
+ const uint8_t *rsn, uint16_t capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t reason;
+ int badwparsn;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN);
+ if (wpa == NULL && rsn == NULL) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) {
+ /*
+ * W-Fi Protected Setup (WPS) permits
+ * clients to associate and pass EAPOL frames
+ * to establish initial credentials.
+ */
+ ni->ni_flags |= IEEE80211_NODE_WPS;
+ return 1;
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) &&
+ (capinfo & IEEE80211_CAPINFO_PRIVACY)) {
+ /*
+ * Transitional Security Network. Permits clients
+ * to associate and use WEP while WPA is configured.
+ */
+ ni->ni_flags |= IEEE80211_NODE_TSN;
+ return 1;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL, "%s", "no WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /* assert right association security credentials */
+ badwparsn = 0; /* NB: to silence compiler */
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
+ badwparsn = (wpa == NULL);
+ break;
+ case IEEE80211_F_WPA2:
+ badwparsn = (rsn == NULL);
+ break;
+ case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
+ badwparsn = (wpa == NULL && rsn == NULL);
+ break;
+ }
+ if (badwparsn) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL,
+ "%s", "missing WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /*
+ * Parse WPA/RSN information element.
+ */
+ if (wpa != NULL)
+ reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh);
+ else
+ reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh);
+ if (reason != 0) {
+ /* XXX distinguish WPA/RSN? */
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni,
+ "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x",
+ wpa != NULL ? "WPA" : "RSN",
+ rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen,
+ rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen,
+ rsnparms->rsn_keymgmt, rsnparms->rsn_caps);
+
+ return 1;
+bad:
+ ieee80211_node_deauth(ni, reason);
+ return 0;
+}
+
+/* XXX find a better place for definition */
+struct l2_update_frame {
+ struct ether_header eh;
+ uint8_t dsap;
+ uint8_t ssap;
+ uint8_t control;
+ uint8_t xid[3];
+} __packed;
+
+/*
+ * Deliver a TGf L2UF frame on behalf of a station.
+ * This primes any bridge when the station is roaming
+ * between ap's on the same wired network.
+ */
+static void
+ieee80211_deliver_l2uf(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ struct l2_update_frame *l2uf;
+ struct ether_header *eh;
+
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "no mbuf for l2uf frame");
+ vap->iv_stats.is_rx_nobuf++; /* XXX not right */
+ return;
+ }
+ l2uf = mtod(m, struct l2_update_frame *);
+ eh = &l2uf->eh;
+ /* dst: Broadcast address */
+ IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
+ /* src: associated STA */
+ IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
+ eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
+
+ l2uf->dsap = 0;
+ l2uf->ssap = 0;
+ l2uf->control = 0xf5;
+ l2uf->xid[0] = 0x81;
+ l2uf->xid[1] = 0x80;
+ l2uf->xid[2] = 0x00;
+
+ m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
+ hostap_deliver_data(vap, ni, m);
+}
+
+static void
+ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int rate)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s rate set mismatch, rate/MCS %d",
+ reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE);
+ ieee80211_node_leave(ni);
+}
+
+static void
+capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s mismatch 0x%x",
+ reassoc ? "reassoc" : "assoc", tag, capinfo);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO);
+ ieee80211_node_leave(ni);
+ vap->iv_stats.is_rx_assoc_capmismatch++;
+}
+
+static void
+htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
+ /* XXX no better code */
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_OTHER);
+ ieee80211_node_leave(ni);
+}
+
+static void
+authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int algo, int seq, int status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH,
+ seq | (status << 16));
+}
+
+static __inline int
+ishtmixed(const uint8_t *ie)
+{
+ const struct ieee80211_ie_htinfo *ht =
+ (const struct ieee80211_ie_htinfo *) ie;
+ return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) ==
+ IEEE80211_HTINFO_OPMODE_MIXED;
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
+ int reassoc, resp;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames when scanning;
+ * otherwise we check beacon frames for overlapping non-ERP
+ * BSS in 11g and/or overlapping legacy BSS when in HT.
+ */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* NB: accept off-channel frames */
+ if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (scan.status == 0 && /* NB: on channel */
+ (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN)) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ /*
+ * Check beacon for overlapping bss w/ non ERP stations.
+ * If we detect one and protection is configured but not
+ * enabled, enable it and start a timer that'll bring us
+ * out if we stop seeing the bss.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ scan.status == 0 && /* NB: on-channel */
+ ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/
+ (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) {
+ ic->ic_lastnonerp = ticks;
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
+ if (ic->ic_protmode != IEEE80211_PROT_NONE &&
+ (ic->ic_flags & IEEE80211_F_USEPROT) == 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_ASSOC, wh,
+ "non-ERP present on channel %d "
+ "(saw erp 0x%x from channel %d), "
+ "enable use of protection",
+ ic->ic_curchan->ic_ieee,
+ scan.erp, scan.chan);
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ ieee80211_notify_erp(ic);
+ }
+ }
+ /*
+ * Check beacon for non-HT station on HT channel
+ * and update HT BSS occupancy as appropriate.
+ */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
+ if (scan.status & IEEE80211_BPARSE_OFFCHAN) {
+ /*
+ * Off control channel; only check frames
+ * that come in the extension channel when
+ * operating w/ HT40.
+ */
+ if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ break;
+ if (scan.chan != ic->ic_curchan->ic_extieee)
+ break;
+ }
+ if (scan.htinfo == NULL) {
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_PROTOPT |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ } else if (ishtmixed(scan.htinfo)) {
+ /* XXX? take NONHT_PRESENT from beacon? */
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_MIXED |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/
+ return;
+ }
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+ /*
+ * Consult the ACL policy module if setup.
+ */
+ if (vap->iv_acl != NULL &&
+ !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
+ return;
+ }
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, NULL, "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ hostap_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ hostap_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_LEAP) {
+ authalgreject(ni, wh, algo,
+ seq+1, IEEE80211_STATUS_ALG);
+ return;
+ } else {
+ /*
+ * We assume that an unknown algorithm is the result
+ * of a decryption failure on a shared key auth frame;
+ * return a status code appropriate for that instead
+ * of IEEE80211_STATUS_ALG.
+ *
+ * NB: a seq# of 4 is intentional; the decrypted
+ * frame likely has a bogus seq value.
+ */
+ authalgreject(ni, wh, algo,
+ 4, IEEE80211_STATUS_CHALLENGE);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
+ uint16_t capinfo, lintval;
+ struct ieee80211_rsnparms rsnparms;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_assoc_bss++;
+ return;
+ }
+ if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ reassoc = 1;
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else {
+ reassoc = 0;
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+ }
+ if (ni == vap->iv_bss) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, sta not authenticated",
+ reassoc ? "reassoc" : "assoc");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ vap->iv_stats.is_rx_assoc_notauth++;
+ return;
+ }
+
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
+ capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ lintval = le16toh(*(uint16_t *)frm); frm += 2;
+ if (reassoc)
+ frm += 6; /* ignore current AP info */
+ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ wpa = frm;
+ else if (iswmeinfo(frm))
+ wme = frm;
+ else if (isatherosoui(frm))
+ ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ if (ishtcapoui(frm) && htcap == NULL)
+ htcap = frm;
+ }
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if (htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(htcap[1],
+ htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ return); /* XXX just NULL out? */
+ }
+
+ if ((vap->iv_flags & IEEE80211_F_WPA) &&
+ !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo))
+ return;
+ /* discard challenge after association */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* NB: 802.11 spec says to ignore station's privacy bit */
+ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "capability", capinfo);
+ return;
+ }
+ /*
+ * Disallow re-associate w/ invalid slot time setting.
+ */
+ if (ni->ni_associd != 0 &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "slot time", capinfo);
+ return;
+ }
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * If constrained to 11g-only stations reject an
+ * 11b-only station. We cheat a bit here by looking
+ * at the max negotiated xmit rate and assuming anyone
+ * with a best rate <24Mb/s is an 11b station.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) {
+ ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * Do HT rate set handling and setup HT node state.
+ */
+ ni->ni_chan = vap->iv_bss->ni_chan;
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) {
+ rate = ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO |
+ IEEE80211_F_DOBRS);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp,
+ "HT", rate);
+ vap->iv_stats.is_ht_assoc_norate++;
+ return;
+ }
+ ieee80211_ht_node_init(ni, htcap);
+ } else if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
+ /*
+ * Allow AMPDU operation only with unencrypted traffic
+ * or AES-CCM; the 11n spec only specifies these ciphers
+ * so permitting any others is undefined and can lead
+ * to interoperability problems.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (((vap->iv_flags & IEEE80211_F_WPA) &&
+ rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) ||
+ (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "disallow HT use because WEP or TKIP requested, "
+ "capinfo 0x%x ucastcipher %d", capinfo,
+ rsnparms.rsn_ucastcipher);
+ ieee80211_ht_node_cleanup(ni);
+ vap->iv_stats.is_ht_assoc_downgrade++;
+ }
+ /*
+ * If constrained to 11n-only stations reject legacy stations.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_PUREN) &&
+ (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+ htcapmismatch(ni, wh, reassoc, resp);
+ vap->iv_stats.is_ht_assoc_nohtcap++;
+ return;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ ni->ni_intval = lintval;
+ ni->ni_capinfo = capinfo;
+ ni->ni_fhdwell = vap->iv_bss->ni_fhdwell;
+ ni->ni_fhindex = vap->iv_bss->ni_fhindex;
+ /*
+ * Store the IEs.
+ * XXX maybe better to just expand
+ */
+ if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) {
+#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off)
+ if (wpa != NULL)
+ setie(wpa_ie, wpa - sfrm);
+ if (rsn != NULL)
+ setie(rsn_ie, rsn - sfrm);
+ if (htcap != NULL)
+ setie(htcap_ie, htcap - sfrm);
+ if (wme != NULL) {
+ setie(wme_ie, wme - sfrm);
+ /*
+ * Mark node as capable of QoS.
+ */
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ if (ath != NULL) {
+ setie(ath_ie, ath - sfrm);
+ /*
+ * Parse ATH station parameters.
+ */
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ } else
+ ni->ni_ath_flags = 0;
+#undef setie
+ } else {
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ ni->ni_ath_flags = 0;
+ }
+ ieee80211_node_join(ni, resp);
+ ieee80211_deliver_l2uf(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ /* NB: can happen when in promiscuous mode */
+ !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /*
+ * deauth/disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+ if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) {
+ vap->iv_stats.is_rx_deauth++;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+ } else {
+ vap->iv_stats.is_rx_disassoc++;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT], reason);
+ if (ni != vap->iv_bss)
+ ieee80211_node_leave(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
+static void
+hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame_min *wh;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ uint16_t aid;
+ int qlen;
+
+ wh = mtod(m0, struct ieee80211_frame_min *);
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "%s", "unassociated station");
+ vap->iv_stats.is_ps_unassoc++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ aid = le16toh(*(uint16_t *)wh->i_dur);
+ if (aid != ni->ni_associd) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "aid mismatch: sta aid 0x%x poll aid 0x%x",
+ ni->ni_associd, aid);
+ vap->iv_stats.is_ps_badaid++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ /* Okay, take the first queued packet and put it out... */
+ IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+ if (m == NULL) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2,
+ "%s", "recv ps-poll, but queue empty");
+ ieee80211_send_nulldata(ieee80211_ref_node(ni));
+ vap->iv_stats.is_ps_qempty++; /* XXX node stat */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0); /* just in case */
+ return;
+ }
+ /*
+ * If there are more packets, set the more packets bit
+ * in the packet dispatched to the station; otherwise
+ * turn off the TIM bit.
+ */
+ if (qlen != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "recv ps-poll, send packet, %u still queued", qlen);
+ m->m_flags |= M_MORE_DATA;
+ } else {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "%s", "recv ps-poll, send packet, queue empty");
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ }
+ m->m_flags |= M_PWR_SAV; /* bypass PS handling */
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if_start(ifp);
+}
diff --git a/sys/net80211/ieee80211_hostap.h b/sys/net80211/ieee80211_hostap.h
new file mode 100644
index 0000000..87f858d
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_HOSTAP_H_
+#define _NET80211_IEEE80211_HOSTAP_H_
+
+/*
+ * Hostap implementation definitions.
+ */
+void ieee80211_hostap_attach(struct ieee80211com *);
+void ieee80211_hostap_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_HOSTAP_H_ */
diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c
index 63fb39a..d8b2d3d 100644
--- a/sys/net80211/ieee80211_ht.c
+++ b/sys/net80211/ieee80211_ht.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
@@ -46,37 +47,34 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
/* define here, used throughout file */
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
#define SM(_v, _f) (((_v) << _f##_S) & _f)
-/* XXX need max array size */
-/* NB: these are for HT20 w/ long GI */
-const int ieee80211_htrates[16] = {
- 13, /* IFM_IEEE80211_MCS0 */
- 26, /* IFM_IEEE80211_MCS1 */
- 39, /* IFM_IEEE80211_MCS2 */
- 52, /* IFM_IEEE80211_MCS3 */
- 78, /* IFM_IEEE80211_MCS4 */
- 104, /* IFM_IEEE80211_MCS5 */
- 117, /* IFM_IEEE80211_MCS6 */
- 130, /* IFM_IEEE80211_MCS7 */
- 26, /* IFM_IEEE80211_MCS8 */
- 52, /* IFM_IEEE80211_MCS9 */
- 78, /* IFM_IEEE80211_MCS10 */
- 104, /* IFM_IEEE80211_MCS11 */
- 156, /* IFM_IEEE80211_MCS12 */
- 208, /* IFM_IEEE80211_MCS13 */
- 234, /* IFM_IEEE80211_MCS14 */
- 260, /* IFM_IEEE80211_MCS15 */
+const struct ieee80211_mcs_rates ieee80211_htrates[16] = {
+ { 13, 14, 27, 30 }, /* MCS 0 */
+ { 26, 29, 54, 60 }, /* MCS 1 */
+ { 39, 43, 81, 90 }, /* MCS 2 */
+ { 52, 58, 108, 120 }, /* MCS 3 */
+ { 78, 87, 162, 180 }, /* MCS 4 */
+ { 104, 116, 216, 240 }, /* MCS 5 */
+ { 117, 130, 243, 270 }, /* MCS 6 */
+ { 130, 144, 270, 300 }, /* MCS 7 */
+ { 26, 29, 54, 60 }, /* MCS 8 */
+ { 52, 58, 108, 120 }, /* MCS 9 */
+ { 78, 87, 162, 180 }, /* MCS 10 */
+ { 104, 116, 216, 240 }, /* MCS 11 */
+ { 156, 173, 324, 360 }, /* MCS 12 */
+ { 208, 231, 432, 480 }, /* MCS 13 */
+ { 234, 260, 486, 540 }, /* MCS 14 */
+ { 260, 289, 540, 600 } /* MCS 15 */
};
static const struct ieee80211_htrateset ieee80211_rateset_11n =
{ 16, {
- /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- /* 39 52 78 104 117, 130 */
10, 11, 12, 13, 14, 15 }
};
@@ -85,11 +83,26 @@ static const struct ieee80211_htrateset ieee80211_rateset_11n =
int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */
#endif
int ieee80211_recv_bar_ena = 1;
+int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */
+int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */
+int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */
-#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250)
-#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000)
-#define IEEE80211_AGGR_MAXTRIES 3
+/*
+ * Setup HT parameters that depends on the clock frequency.
+ */
+static void
+ieee80211_ht_setup(void)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ ieee80211_ampdu_age = msecs_to_ticks(500);
+#endif
+ ieee80211_addba_timeout = msecs_to_ticks(250);
+ ieee80211_addba_backoff = msecs_to_ticks(10*1000);
+}
+SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
+static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
static int ieee80211_addba_request(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout);
@@ -104,56 +117,70 @@ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
void
ieee80211_ht_attach(struct ieee80211com *ic)
{
-#ifdef IEEE80211_AMPDU_AGE
- if (ieee80211_ampdu_age == -1)
- ieee80211_ampdu_age = msecs_to_ticks(500);
-#endif
-
/* setup default aggregation policy */
ic->ic_recv_action = ieee80211_aggr_recv_action;
ic->ic_send_action = ieee80211_send_action;
+ ic->ic_ampdu_enable = ieee80211_ampdu_enable;
ic->ic_addba_request = ieee80211_addba_request;
ic->ic_addba_response = ieee80211_addba_response;
ic->ic_addba_stop = ieee80211_addba_stop;
ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+}
+
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
- /* XXX get from driver */
- ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
- ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
- ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
- ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+void
+ieee80211_ht_vattach(struct ieee80211vap *vap)
+{
- if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ /* driver can override defaults */
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+ vap->iv_ampdu_limit = vap->iv_ampdu_rxmax;
+ vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU;
+ /* tx aggregation traffic thresholds */
+ vap->iv_ampdu_mintraffic[WME_AC_BK] = 128;
+ vap->iv_ampdu_mintraffic[WME_AC_BE] = 64;
+ vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
+ vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
+
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
/*
* Device is HT capable; enable all HT-related
* facilities by default.
* XXX these choices may be too aggressive.
*/
- ic->ic_flags_ext |= IEEE80211_FEXT_HT
- | IEEE80211_FEXT_HTCOMPAT
- ;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HT
+ | IEEE80211_FEXT_HTCOMPAT
+ ;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
/* XXX infer from channel list? */
- if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ vap->iv_flags_ext |= IEEE80211_FEXT_USEHT40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
}
/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMPDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
}
+ /* NB: disable default legacy WDS, too many issues right now */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HT;
}
void
-ieee80211_ht_detach(struct ieee80211com *ic)
+ieee80211_ht_vdetach(struct ieee80211vap *vap)
{
}
@@ -170,7 +197,7 @@ ht_announce(struct ieee80211com *ic, int mode,
rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
continue;
- rate = ieee80211_htrates[rs->rs_rates[i]];
+ rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns;
printf("%s%d%sMbps", (i != 0 ? " " : ""),
rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
}
@@ -205,14 +232,14 @@ ieee80211_get_suphtrates(struct ieee80211com *ic,
struct mbuf *
ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int framelen;
struct mbuf *n;
/* discard 802.3 header inserted by ieee80211_decap */
m_adj(m, sizeof(struct ether_header));
- ic->ic_stats.is_amsdu_decap++;
+ vap->iv_stats.is_amsdu_decap++;
for (;;) {
/*
@@ -223,23 +250,23 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu", "%s", "decap failed");
- ic->ic_stats.is_amsdu_tooshort++;
+ vap->iv_stats.is_amsdu_tooshort++;
return NULL;
}
if (m->m_pkthdr.len == framelen)
break;
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_amsdu_split++;
+ vap->iv_stats.is_amsdu_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m);
+ vap->iv_deliver_data(vap, ni, m);
/*
* Remove frame contents; each intermediate frame
@@ -252,19 +279,6 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
}
/*
- * Start A-MPDU rx/re-order processing for the specified TID.
- */
-static void
-ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
-{
- memset(rap, 0, sizeof(*rap));
- rap->rxa_wnd = (bufsiz == 0) ?
- IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
- rap->rxa_start = start;
- rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
-}
-
-/*
* Purge all frames in the A-MPDU re-order queue.
*/
static void
@@ -289,13 +303,33 @@ ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
}
/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+ if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
+ /*
+ * AMPDU previously setup and not terminated with a DELBA,
+ * flush the reorder q's in case anything remains.
+ */
+ ampdu_rx_purge(rap);
+ }
+ memset(rap, 0, sizeof(*rap));
+ rap->rxa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ rap->rxa_start = start;
+ rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
* Stop A-MPDU rx processing for the specified TID.
*/
static void
ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
{
- rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
ampdu_rx_purge(rap);
+ rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND);
}
/*
@@ -309,7 +343,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
{
m->m_flags |= M_AMPDU; /* bypass normal processing */
/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
- (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
+ (void) ieee80211_input(ni, m, 0, 0, 0);
}
/*
@@ -323,7 +357,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
static void
ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -353,14 +387,14 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
}
}
KASSERT(n == 0, ("lost %d frames", n));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Adjust the start of the BA window to
* reflect the frames just dispatched.
*/
rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
- ic->ic_stats.is_ampdu_rx_oor += i;
+ vap->iv_stats.is_ampdu_rx_oor += i;
}
#ifdef IEEE80211_AMPDU_AGE
@@ -370,7 +404,7 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
static void
ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -381,7 +415,7 @@ ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
if (rap->rxa_qframes == 0)
@@ -399,7 +433,7 @@ static void
ampdu_rx_flush_upto(struct ieee80211_node *ni,
struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
ieee80211_seq seqno;
int i;
@@ -418,7 +452,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
} else {
@@ -433,6 +467,10 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
*/
if (rap->rxa_qframes != 0) {
int n = rap->rxa_qframes, j;
+
+ /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
+ KASSERT(rap->rxa_m[0] == NULL,
+ ("%s: BA window slot 0 occupied", __func__));
for (j = i+1; j < rap->rxa_wnd; j++) {
if (rap->rxa_m[j] != NULL) {
rap->rxa_m[j-i] = rap->rxa_m[j];
@@ -446,7 +484,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
__func__, n, rap->rxa_qframes, i, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
winstart));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Move the start of the BA window; we use the
@@ -472,7 +510,7 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
#define PROCESS 0 /* caller should process frame */
#define CONSUMED 1 /* frame consumed, caller does nothing */
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_qosframe *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -557,7 +595,7 @@ again:
* frame; flush the reorder buffer.
*/
if (rap->rxa_qframes != 0) {
- ic->ic_stats.is_ampdu_rx_age +=
+ vap->iv_stats.is_ampdu_rx_age +=
rap->rxa_qframes;
ampdu_rx_flush(ni, rap);
}
@@ -576,15 +614,15 @@ again:
rap->rxa_m[off] = m;
rap->rxa_qframes++;
rap->rxa_qbytes += m->m_pkthdr.len;
- ic->ic_stats.is_ampdu_rx_reorder++;
+ vap->iv_stats.is_ampdu_rx_reorder++;
} else {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "a-mpdu duplicate",
"seqno %u tid %u BA win <%u:%u>",
rxseq, tid, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
- ic->ic_stats.is_rx_dup++;
+ vap->iv_stats.is_rx_dup++;
IEEE80211_NODE_STAT(ni, rx_dup);
m_freem(m);
}
@@ -596,12 +634,12 @@ again:
* flush the reorder q and move the window.
* Sec 9.10.7.6 b) (D2.04 p.118 line 60)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"move BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_rx_move++;
+ vap->iv_stats.is_ampdu_rx_move++;
/*
* The spec says to flush frames up to but not including:
@@ -620,14 +658,14 @@ again:
* Outside the BA window and out of range; toss.
* Sec 9.10.7.6 c) (D2.04 p.119 line 16)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
- "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+ "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_rx_drop++;
+ vap->iv_stats.is_ampdu_rx_drop++;
IEEE80211_NODE_STAT(ni, rx_drop);
m_freem(m);
return CONSUMED;
@@ -645,7 +683,7 @@ again:
void
ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame_bar *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -653,10 +691,10 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
if (!ieee80211_recv_bar_ena) {
#if 0
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "%s", "processing disabled");
#endif
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
wh = mtod(m0, struct ieee80211_frame_bar *);
@@ -667,13 +705,13 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
/*
* No ADDBA request yet, don't touch.
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
- ic->ic_stats.is_ampdu_bar_rx++;
+ vap->iv_stats.is_ampdu_bar_rx++;
rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
if (rxseq == rap->rxa_start)
return;
@@ -684,12 +722,12 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Flush the reorder q up to rxseq and move the window.
* Sec 9.10.7.6 a) (D2.04 p.119 line 22)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_bar_move++;
+ vap->iv_stats.is_ampdu_bar_move++;
ampdu_rx_flush_upto(ni, rap, rxseq);
if (off >= rap->rxa_wnd) {
@@ -705,14 +743,14 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Out of range; toss.
* Sec 9.10.7.6 b) (D2.04 p.119 line 41)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
"BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_bar_oow++;
+ vap->iv_stats.is_ampdu_bar_oow++;
IEEE80211_NODE_STAT(ni, rx_drop);
}
}
@@ -767,6 +805,8 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
*/
ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
IEEE80211_TAPQ_DESTROY(tap);
+ tap->txa_lastsample = 0;
+ tap->txa_avgpps = 0;
/* NB: clearing NAK means we may re-send ADDBA */
tap->txa_flags &=
~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
@@ -780,6 +820,45 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
IEEE80211_NODE_AMPDU);
}
+/*
+ * Age out HT resources for a station.
+ */
+void
+ieee80211_ht_node_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t tid;
+#endif
+
+ KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
+
+#ifdef IEEE80211_AMPDU_AGE
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ struct ieee80211_rx_ampdu *rap;
+
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
+ continue;
+ if (rap->rxa_qframes == 0)
+ continue;
+ /*
+ * Check for frames sitting too long in the reorder queue.
+ * See above for more details on what's happening here.
+ */
+ /* XXX honor batimeout? */
+ if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
+ /*
+ * Too long since we received the first
+ * frame; flush the reorder buffer.
+ */
+ vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
+ ampdu_rx_flush(ni, rap);
+ }
+ }
+#endif /* IEEE80211_AMPDU_AGE */
+}
+
static struct ieee80211_channel *
findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
{
@@ -832,11 +911,11 @@ ieee80211_ht_adjust_channel(struct ieee80211com *ic,
void
ieee80211_ht_wds_init(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tx_ampdu *tap;
int ac;
- KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
+ KASSERT(vap->iv_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
/* XXX check scan cache in case peer has an ap and we have info */
/*
@@ -845,11 +924,11 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
* AP) is suitable use it so we use the same location
* for the extension channel).
*/
- ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan,
- ic->ic_flags_ext);
+ ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic,
+ ni->ni_chan, ieee80211_htchanflags(ni->ni_chan));
ni->ni_htcap = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
@@ -858,7 +937,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
} else {
ni->ni_chw = 20;
@@ -883,20 +962,30 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
static void
htinfo_notify(struct ieee80211com *ic)
{
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return;
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
- ic->ic_bss,
- "HT bss occupancy change: %d sta, %d ht, "
- "%d ht40%s, HT protmode now 0x%x"
- , ic->ic_sta_assoc
- , ic->ic_ht_sta_assoc
- , ic->ic_ht40_sta_assoc
- , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
- ", non-HT sta present" : ""
- , ic->ic_curhtprotmode);
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO);
+ struct ieee80211vap *vap;
+ int first = 1;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ if (first) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+ vap->iv_bss,
+ "HT bss occupancy change: %d sta, %d ht, "
+ "%d ht40%s, HT protmode now 0x%x"
+ , ic->ic_sta_assoc
+ , ic->ic_ht_sta_assoc
+ , ic->ic_ht40_sta_assoc
+ , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+ ", non-HT sta present" : ""
+ , ic->ic_curhtprotmode);
+ first = 0;
+ }
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
+ }
}
/*
@@ -908,13 +997,14 @@ htinfo_update(struct ieee80211com *ic)
{
uint8_t protmode;
- if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
- protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+ if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_MIXED
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+ protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
} else {
@@ -964,15 +1054,36 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni)
/*
* Public version of htinfo_update; used for processing
- * beacon frames from overlapping bss in hostap_recv_mgmt.
+ * beacon frames from overlapping bss.
+ *
+ * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
+ * (on receipt of a beacon that advertises MIXED) or
+ * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
+ * from an overlapping legacy bss). We treat MIXED with
+ * a higher precedence than PROTOPT (i.e. we will not change
+ * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This
+ * corresponds to how we handle things in htinfo_update.
*/
void
-ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
{
- if (protmode != ic->ic_curhtprotmode) {
- ic->ic_curhtprotmode = protmode;
- htinfo_notify(ic);
- }
+#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE)
+ if (protmode == ic->ic_curhtprotmode)
+ return;
+ if (OPMODE(ic->ic_curhtprotmode) == IEEE80211_HTINFO_OPMODE_MIXED &&
+ OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)
+ return;
+
+ /* track non-HT station presence */
+ KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
+ ("missing NONHT_PRESENT"));
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONHT_PR;
+ ic->ic_lastnonht = ticks;
+
+ /* push beacon update */
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+#undef OPMODE
}
/*
@@ -991,7 +1102,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) &&
time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
#if 0
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"%s", "time out non-HT STA present on channel");
#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
@@ -1011,7 +1122,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
void
ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
if (ie[0] == IEEE80211_ELEMID_VENDOR) {
/*
@@ -1029,7 +1140,7 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
/* XXX needed or will ieee80211_parse_htinfo always be called? */
ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
}
/*
@@ -1044,6 +1155,7 @@ void
ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htinfo *htinfo;
struct ieee80211_channel *c;
uint16_t w;
@@ -1063,11 +1175,11 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
* identify the right channel to use. If we cannot locate it
* in the channel table then fallback to legacy operation.
*/
- htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
- IEEE80211_CHAN_HT20 : 0;
/* NB: honor operating mode constraint */
+ htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ?
+ IEEE80211_CHAN_HT20 : 0;
if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) {
if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
htflags = IEEE80211_CHAN_HT40U;
else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
@@ -1076,20 +1188,20 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
if (chanflags != ni->ni_chan->ic_flags) {
c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
- if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+ if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
/*
* No HT40 channel entry in our table; fall back
* to HT20 operation. This should not happen.
*/
c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"no HT40 channel (freq %u), falling back to HT20",
ni->ni_chan->ic_freq);
/* XXX stat */
}
if (c != NULL && c != ni->ni_chan) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"switch station to HT%d channel %u/0x%x",
IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
@@ -1108,7 +1220,7 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
int
ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htcap *htcap;
struct ieee80211_htrateset *rs;
int i;
@@ -1123,11 +1235,11 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
if (isclr(htcap->hc_mcsset, i))
continue;
if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"WARNING, HT rate set too large; only "
"using %u rates", IEEE80211_HTRATE_MAXSIZE);
- ic->ic_stats.is_rx_rstoobig++;
+ vap->iv_stats.is_rx_rstoobig++;
break;
}
rs->rs_rates[rs->rs_nrates++] = i;
@@ -1152,7 +1264,7 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
htinfo = (const struct ieee80211_ie_htinfo *) ie;
rs = &ni->ni_htrates;
if (rs->rs_nrates == 0) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(ni->ni_vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"%s", "WARNING, empty HT rate set");
return;
@@ -1180,10 +1292,10 @@ static void
addba_start_timeout(struct ieee80211_tx_ampdu *tap)
{
/* XXX use CALLOUT_PENDING instead? */
- callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
+ callout_reset(&tap->txa_timer, ieee80211_addba_timeout,
addba_timeout, tap);
tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
- tap->txa_lastrequest = ticks;
+ tap->txa_nextrequest = ticks + ieee80211_addba_timeout;
}
static void
@@ -1274,6 +1386,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
struct ieee80211_rx_ampdu *rap;
struct ieee80211_tx_ampdu *tap;
@@ -1295,7 +1408,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_BAPS_TID);
bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA request: dialogtoken %u "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
@@ -1314,19 +1427,19 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
* violates the 11n spec and is mostly for testing).
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
ampdu_rx_start(rap, bufsiz,
MS(baseqctl, IEEE80211_BASEQ_START));
args[1] = IEEE80211_STATUS_SUCCESS;
} else {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "reject ADDBA request: %s",
ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
"administratively disabled" :
"not negotiated for station");
- ic->ic_stats.is_addba_reject++;
+ vap->iv_stats.is_addba_reject++;
args[1] = IEEE80211_STATUS_UNSPECIFIED;
}
/* XXX honor rap flags? */
@@ -1350,26 +1463,26 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
ac = TID_TO_WME_AC(tid);
tap = &ni->ni_tx_ampdu[ac];
if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"no pending ADDBA, tid %d dialogtoken %u "
"code %d", tid, dialogtoken, code);
- ic->ic_stats.is_addba_norequest++;
+ vap->iv_stats.is_addba_norequest++;
return;
}
if (dialogtoken != tap->txa_token) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"dialogtoken mismatch: waiting for %d, "
"received %d, tid %d code %d",
tap->txa_token, dialogtoken, tid, code);
- ic->ic_stats.is_addba_badtoken++;
+ vap->iv_stats.is_addba_badtoken++;
return;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA response: dialogtoken %u code %d "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
@@ -1385,7 +1498,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_DELBAPS_TID);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv DELBA: baparamset 0x%x (tid %d initiator %d) "
"code %d", baparamset, tid,
@@ -1416,18 +1529,18 @@ void
ieee80211_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
int chw;
ia = (const struct ieee80211_action *) frm;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: BA action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
case IEEE80211_ACTION_CAT_HT:
switch (ia->ia_action) {
@@ -1437,7 +1550,7 @@ ieee80211_recv_action(struct ieee80211_node *ni,
ni->ni_chw = chw;
ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT txchwidth, width %d (%s)",
__func__, chw,
@@ -1445,25 +1558,25 @@ ieee80211_recv_action(struct ieee80211_node *ni,
"new" : "no change");
break;
case IEEE80211_ACTION_HT_MIMOPWRSAVE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT MIMO PS", __func__);
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: category %d not implemented", __func__,
ia->ia_category);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
}
@@ -1473,6 +1586,39 @@ ieee80211_recv_action(struct ieee80211_node *ni,
*/
/*
+ * Check if A-MPDU should be requested/enabled for a stream.
+ * We require a traffic rate above a per-AC threshold and we
+ * also handle backoff from previous failed attempts.
+ *
+ * Drivers may override this method to bring in information
+ * such as link state conditions in making the decision.
+ */
+static int
+ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
+ return 0;
+ /* XXX check rssi? */
+ if (tap->txa_attempts >= ieee80211_addba_maxtries &&
+ ticks < tap->txa_nextrequest) {
+ /*
+ * Don't retry too often; txa_nextrequest is set
+ * to the minimum interval we'll retry after
+ * ieee80211_addba_maxtries failed attempts are made.
+ */
+ return 0;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
+ "%s: enable AMPDU on %s, avgpps %d pkts %d",
+ __func__, ieee80211_wme_acnames[tap->txa_ac],
+ tap->txa_avgpps, tap->txa_pkts);
+ return 1;
+}
+
+/*
* Request A-MPDU tx aggregation. Setup local state and
* issue an ADDBA request. BA use will only happen after
* the other end replies with ADDBA response.
@@ -1493,16 +1639,6 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
tap->txa_flags |= IEEE80211_AGGR_SETUP;
}
- if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
- (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
- /*
- * Don't retry too often; IEEE80211_AGGR_MINRETRY
- * defines the minimum interval we'll retry after
- * IEEE80211_AGGR_MAXTRIES failed attempts to
- * negotiate use.
- */
- return 0;
- }
/* XXX hack for not doing proper locking */
tap->txa_flags &= ~IEEE80211_AGGR_NAK;
@@ -1521,12 +1657,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
/* NB: do first so there's no race against reply */
if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
/* unable to setup state, don't make request */
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
ni, "%s: could not setup BA stream for AC %d",
__func__, tap->txa_ac);
/* defer next try so we don't slam the driver with requests */
- tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
- tap->txa_lastrequest = ticks;
+ tap->txa_attempts = ieee80211_addba_maxtries;
+ /* NB: check in case driver wants to override */
+ if (tap->txa_nextrequest <= ticks)
+ tap->txa_nextrequest = ticks + ieee80211_addba_backoff;
return 0;
}
tokens = dialogtoken; /* allocate token */
@@ -1542,13 +1680,14 @@ void
ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t args[4];
/* XXX locking */
if (IEEE80211_AMPDU_RUNNING(tap)) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop++;
+ vap->iv_stats.is_ampdu_stop++;
ic->ic_addba_stop(ni, tap);
args[0] = WME_AC_TO_TID(tap->txa_ac);
@@ -1557,10 +1696,10 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
IEEE80211_ACTION_BA_DELBA, args);
} else {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: BA stream for AC %d not running",
__func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop_failed++;
+ vap->iv_stats.is_ampdu_stop_failed++;
}
}
@@ -1573,14 +1712,14 @@ int
ieee80211_send_bar(struct ieee80211_node *ni,
const struct ieee80211_tx_ampdu *tap)
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame_min *wh;
struct mbuf *m;
uint8_t *frm;
@@ -1601,7 +1740,7 @@ ieee80211_send_bar(struct ieee80211_node *ni,
IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
wh->i_fc[1] = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
tid = WME_AC_TO_TID(tap->txa_ac);
barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
@@ -1617,17 +1756,15 @@ ieee80211_send_bar(struct ieee80211_node *ni,
ADDSHORT(frm, barseqctl);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ M_WME_SETAC(m, WME_AC_VO);
+
IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */
- IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
ni, "send bar frame (tid %u start %u) on channel %u",
tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
- m->m_pkthdr.rcvif = (void *)ni;
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1644,12 +1781,13 @@ int
ieee80211_send_action(struct ieee80211_node *ni,
int category, int action, uint16_t args[4])
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m;
uint8_t *frm;
@@ -1663,7 +1801,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1685,7 +1823,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_BA:
switch (action) {
case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA request: dialogtoken %d "
"baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
@@ -1698,7 +1836,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, args[3]); /* baseqctl */
break;
case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA response: dialogtoken %d status %d "
"baparamset 0x%x (tid %d) batimeout %d",
@@ -1718,7 +1856,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, baparamset);
ADDSHORT(frm, args[2]); /* reason code */
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send DELBA action: tid %d, initiator %d reason %d",
args[0], args[1], args[2]);
@@ -1730,12 +1868,12 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_HT:
switch (action) {
case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "send HT txchwidth: width %d",
- IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20
);
- *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?
+ *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
IEEE80211_A_HT_TXCHWIDTH_2040 :
IEEE80211_A_HT_TXCHWIDTH_20;
break;
@@ -1745,7 +1883,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
break;
default:
badaction:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: unsupported category %d action %d", __func__,
category, action);
@@ -1754,12 +1892,11 @@ ieee80211_send_action(struct ieee80211_node *ni,
}
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
bad:
ieee80211_free_node(ni);
+ if (m != NULL)
+ m_freem(m);
return ret;
#undef ADDSHORT
#undef senderr
@@ -1794,12 +1931,12 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t caps;
int rxmax, density;
/* HT capabilities */
- caps = ic->ic_htcaps & 0xffff;
+ caps = vap->iv_htcaps & 0xffff;
/*
* Note channel width depends on whether we are operating as
* a sta or not. When operating as a sta we are generating
@@ -1808,9 +1945,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
* how we've been setup (which might be different if a fixed
* channel is specified).
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* override 20/40 use based on config */
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
@@ -1819,17 +1956,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
} else {
/* override 20/40 use based on current channel */
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
- rxmax = ic->ic_ampdu_rxmax;
- density = ic->ic_ampdu_density;
+ rxmax = vap->iv_ampdu_rxmax;
+ density = vap->iv_ampdu_density;
}
/* adjust short GI based on channel and config */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI20;
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
(caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI40;
ADDSHORT(frm, caps);
@@ -1909,23 +2046,25 @@ ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
* Update the HTINFO ie for a beacon frame.
*/
void
-ieee80211_ht_update_beacon(struct ieee80211com *ic,
+ieee80211_ht_update_beacon(struct ieee80211vap *vap,
struct ieee80211_beacon_offsets *bo)
{
#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+ const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_htinfo *ht =
(struct ieee80211_ie_htinfo *) bo->bo_htinfo;
/* XXX only update on channel change */
- ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
/* protection mode */
@@ -1951,16 +2090,16 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
/* primary/control channel center */
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
frm[1] = ic->ic_curhtprotmode;
diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h
index d1d6699..80052cf 100644
--- a/sys/net80211/ieee80211_ht.h
+++ b/sys/net80211/ieee80211_ht.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,13 +46,16 @@ struct ieee80211_tx_ampdu {
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
uint8_t txa_ac;
uint8_t txa_token; /* dialog token */
+ int txa_lastsample; /* ticks @ last traffic sample */
+ int txa_pkts; /* packets over last sample interval */
+ int txa_avgpps; /* filtered traffic over window */
int txa_qbytes; /* data queued (bytes) */
short txa_qframes; /* data queued (frames) */
ieee80211_seq txa_seqstart;
ieee80211_seq txa_start;
uint16_t txa_wnd; /* BA window size */
- uint8_t txa_attempts; /* # setup attempts */
- int txa_lastrequest;/* time of last ADDBA request */
+ uint8_t txa_attempts; /* # ADDBA requests w/o a response */
+ int txa_nextrequest;/* soonest to make next ADDBA request */
struct ifqueue txa_q; /* packet queue */
struct callout txa_timer;
void *txa_private; /* driver-private storage */
@@ -67,6 +70,65 @@ struct ieee80211_tx_ampdu {
(((tap)->txa_flags & \
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
+/*
+ * Traffic estimator support. We estimate packets/sec for
+ * each AC that is setup for AMPDU or will potentially be
+ * setup for AMPDU. The traffic rate can be used to decide
+ * when AMPDU should be setup (according to a threshold)
+ * and is available for drivers to do things like cache
+ * eviction when only a limited number of BA streams are
+ * available and more streams are requested than available.
+ */
+
+static void __inline
+ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap)
+{
+ /* NB: scale factor of 2 was picked heuristically */
+ tap->txa_avgpps = ((tap->txa_avgpps << 2) -
+ tap->txa_avgpps + tap->txa_pkts) >> 2;
+}
+
+/*
+ * Count a packet towards the pps estimate.
+ */
+static void __inline
+ieee80211_txampdu_count_packet(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ /* reset to start new sample interval */
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ tap->txa_pkts++;
+}
+
+/*
+ * Get the current pps estimate. If the average is out of
+ * date due to lack of traffic then we decay the estimate
+ * to account for the idle time.
+ */
+static int __inline
+ieee80211_txampdu_getpps(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ return tap->txa_avgpps;
+}
+
struct ieee80211_rx_ampdu {
int rxa_flags;
int rxa_qbytes; /* data queued (bytes) */
@@ -81,10 +143,18 @@ struct ieee80211_rx_ampdu {
void ieee80211_ht_attach(struct ieee80211com *);
void ieee80211_ht_detach(struct ieee80211com *);
+void ieee80211_ht_vattach(struct ieee80211vap *);
+void ieee80211_ht_vdetach(struct ieee80211vap *);
void ieee80211_ht_announce(struct ieee80211com *);
-extern const int ieee80211_htrates[16];
+struct ieee80211_mcs_rates {
+ uint16_t ht20_rate_800ns;
+ uint16_t ht20_rate_400ns;
+ uint16_t ht40_rate_800ns;
+ uint16_t ht40_rate_400ns;
+};
+extern const struct ieee80211_mcs_rates ieee80211_htrates[16];
const struct ieee80211_htrateset *ieee80211_get_suphtrates(
struct ieee80211com *, const struct ieee80211_channel *);
@@ -98,12 +168,14 @@ int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
void ieee80211_ht_node_cleanup(struct ieee80211_node *);
+void ieee80211_ht_node_age(struct ieee80211_node *);
+
struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
struct ieee80211_channel *, int);
void ieee80211_ht_wds_init(struct ieee80211_node *);
void ieee80211_ht_node_join(struct ieee80211_node *);
void ieee80211_ht_node_leave(struct ieee80211_node *);
-void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+void ieee80211_htprot_update(struct ieee80211com *, int protmode);
void ieee80211_ht_timeout(struct ieee80211com *);
void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
@@ -122,6 +194,6 @@ uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
struct ieee80211_beacon_offsets;
-void ieee80211_ht_update_beacon(struct ieee80211com *,
+void ieee80211_ht_update_beacon(struct ieee80211vap *,
struct ieee80211_beacon_offsets *);
#endif /* _NET80211_IEEE80211_HT_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 9238bdf..9ae653f 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -35,591 +37,75 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
+
#include <net/ethernet.h>
+#include <net/if.h>
#include <net/if_llc.h>
+#include <net/if_media.h>
#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
#include <net/bpf.h>
-#ifdef IEEE80211_DEBUG
-#include <machine/stdarg.h>
-
-/*
- * Decide if a received management frame should be
- * printed when debugging is enabled. This filters some
- * of the less interesting frames that come frequently
- * (e.g. beacons).
- */
-static __inline int
-doprint(struct ieee80211com *ic, int subtype)
-{
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_BEACON:
- return (ic->ic_flags & IEEE80211_F_SCAN);
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
- }
- return 1;
-}
-
-static const uint8_t *ieee80211_getbssid(struct ieee80211com *,
- const struct ieee80211_frame *);
-#endif /* IEEE80211_DEBUG */
-
-static struct mbuf *ieee80211_defrag(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
-static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
-static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
- const uint8_t *mac, int subtype, int arg);
-static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-static void ieee80211_recv_pspoll(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
+#ifdef INET
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#endif
-/*
- * Process a received frame. The node associated with the sender
- * should be supplied. If nothing was found in the node table then
- * the caller is assumed to supply a reference to ic_bss instead.
- * The RSSI and a timestamp are also supplied. The RSSI data is used
- * during AP scanning to select a AP to associate with; it can have
- * any units so long as values have consistent units and higher values
- * mean ``better signal''. The receive timestamp is currently not used
- * by the 802.11 layer.
- */
int
-ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp)
+ieee80211_input_all(struct ieee80211com *ic,
+ struct mbuf *m, int rssi, int noise, u_int32_t rstamp)
{
-#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
-#define HAS_SEQ(type) ((type & 0x4) == 0)
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct ieee80211_key *key;
- struct ether_header *eh;
- int hdrspace, need_tap;
- uint8_t dir, type, subtype, qos;
- uint8_t *bssid;
- uint16_t rxseq;
-
- if (m->m_flags & M_AMPDU) {
- /*
- * Fastpath for A-MPDU reorder q resubmission. Frames
- * w/ M_AMPDU marked have already passed through here
- * but were received out of order and been held on the
- * reorder queue. When resubmitted they are marked
- * with the M_AMPDU flag and we can bypass most of the
- * normal processing.
- */
- wh = mtod(m, struct ieee80211_frame *);
- type = IEEE80211_FC0_TYPE_DATA;
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- subtype = IEEE80211_FC0_SUBTYPE_QOS;
- hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
- need_tap = 0;
- goto resubmit_ampdu;
- }
-
- KASSERT(ni != NULL, ("null node"));
- ni->ni_inact = ni->ni_inact_reload;
-
- need_tap = 1; /* mbuf need to be tapped. */
- type = -1; /* undefined */
- /*
- * In monitor mode, send everything directly to bpf.
- * XXX may want to include the CRC
- */
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- goto out;
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "too short (1): len %u", m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- /*
- * Bit of a cheat here, we use a pointer for a 3-address
- * frame format but don't reference fields past outside
- * ieee80211_frame_min w/o first validating the data is
- * present.
- */
- wh = mtod(m, struct ieee80211_frame *);
-
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
- ic->ic_stats.is_rx_badversion++;
- goto err;
- }
-
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- bssid = wh->i_addr2;
- if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_NODS)
- bssid = wh->i_addr1;
- else if (type == IEEE80211_FC0_TYPE_CTL)
- bssid = wh->i_addr1;
- else {
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic,
- IEEE80211_MSG_ANY, ni->ni_macaddr,
- NULL, "too short (2): len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- bssid = wh->i_addr3;
- }
- if (type != IEEE80211_FC0_TYPE_DATA)
- break;
- /*
- * Data frame, validate the bssid.
- */
- if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
- !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- /*
- * For adhoc mode we cons up a node when it doesn't
- * exist. This should probably done after an ACL check.
- */
- if (ni == ic->ic_bss &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Fake up a node for this newly
- * discovered member of the IBSS.
- */
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- if (ni == NULL) {
- /* NB: stat kept for alloc failure */
- goto err;
- }
- }
- break;
- default:
- goto out;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (HAS_SEQ(type)) {
- uint8_t tid;
- if (IEEE80211_QOS_HAS_SEQ(wh)) {
- tid = ((struct ieee80211_qosframe *)wh)->
- i_qos[0] & IEEE80211_QOS_TID;
- if (TID_TO_WME_AC(tid) >= WME_AC_VI)
- ic->ic_wme.wme_hipri_traffic++;
- tid++;
- } else
- tid = IEEE80211_NONQOS_TID;
- rxseq = le16toh(*(uint16_t *)wh->i_seq);
- if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
- (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
- SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
- /* duplicate, discard */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, "duplicate",
- "seqno <%u,%u> fragno <%u,%u> tid %u",
- rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
- ni->ni_rxseqs[tid] >>
- IEEE80211_SEQ_SEQ_SHIFT,
- rxseq & IEEE80211_SEQ_FRAG_MASK,
- ni->ni_rxseqs[tid] &
- IEEE80211_SEQ_FRAG_MASK,
- tid);
- ic->ic_stats.is_rx_dup++;
- IEEE80211_NODE_STAT(ni, rx_dup);
- goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
- }
- }
-
- switch (type) {
- case IEEE80211_FC0_TYPE_DATA:
- hdrspace = ieee80211_hdrspace(ic, wh);
- if (m->m_len < hdrspace &&
- (m = m_pullup(m, hdrspace)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "data too short: expecting %u", hdrspace);
- ic->ic_stats.is_rx_tooshort++;
- goto out; /* XXX */
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- if (dir != IEEE80211_FC1_DIR_FROMDS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- if ((ifp->if_flags & IFF_SIMPLEX) &&
- IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
- /*
- * In IEEE802.11 network, multicast packet
- * sent from me is broadcasted from AP.
- * It should be silently discarded for
- * SIMPLEX interface.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, NULL, "%s", "multicast echo");
- ic->ic_stats.is_rx_mcastecho++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* XXX no power-save support */
- break;
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_TODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* check if source STA is associated */
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown src");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_AUTHED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unassoc src");
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_NOT_ASSOCED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
-
- /*
- * Check for power save state change.
- * XXX out-of-order A-MPDU frames?
- */
- if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
- (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
- ieee80211_node_pwrsave(ni,
- wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
- break;
- default:
- /* XXX here to keep compiler happy */
- goto out;
- }
-
- /*
- * Handle A-MPDU re-ordering. The station must be
- * associated and negotiated HT. The frame must be
- * a QoS frame (not QoS null data) and not previously
- * processed for A-MPDU re-ordering. If the frame is
- * to be processed directly then ieee80211_ampdu_reorder
- * will return 0; otherwise it has consumed the mbuf
- * and we should do nothing more with it.
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- subtype == IEEE80211_FC0_SUBTYPE_QOS &&
- ieee80211_ampdu_reorder(ni, m) != 0) {
- m = NULL;
- goto out;
- }
- resubmit_ampdu:
-
- /*
- * Handle privacy requirements. Note that we
- * must not be preempted from here until after
- * we (potentially) call ieee80211_crypto_demic;
- * otherwise we may violate assumptions in the
- * crypto cipher modules used to do delayed update
- * of replay sequence numbers.
- */
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "WEP", "%s", "PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- IEEE80211_NODE_STAT(ni, rx_noprivacy);
- goto out;
- }
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- IEEE80211_NODE_STAT(ni, rx_wepfail);
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- } else {
- /* XXX M_WEP and IEEE80211_F_PRIVACY */
- key = NULL;
- }
-
- /*
- * Save QoS bits for use below--before we strip the header.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
- qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
- ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
- ((struct ieee80211_qosframe *)wh)->i_qos[0];
- } else
- qos = 0;
+ struct ieee80211vap *vap;
+ int type = -1;
- /*
- * Next up, any fragmentation.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- m = ieee80211_defrag(ic, ni, m, hdrspace);
- if (m == NULL) {
- /* Fragment dropped or frame not complete yet */
- goto out;
- }
- }
- wh = NULL; /* no longer valid, catch any uses */
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ieee80211_node *ni;
+ struct mbuf *mcopy;
/*
- * Next strip any MSDU crypto bits.
+ * WDS vap's only receive directed traffic from the
+ * station at the ``far end''. That traffic should
+ * be passed through the AP vap the station is associated
+ * to--so don't spam them with mcast frames.
*/
- if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "demic error");
- ic->ic_stats.is_rx_demicfail++;
- IEEE80211_NODE_STAT(ni, rx_demicfail);
- goto out;
- }
-
- /* copy to listener after decrypt */
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- need_tap = 0;
-
- /*
- * Finally, strip the 802.11 header.
- */
- m = ieee80211_decap(ic, m, hdrspace);
- if (m == NULL) {
- /* don't count Null data frames as errors */
- if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
- subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
- goto out;
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "decap error");
- ic->ic_stats.is_rx_decap++;
- IEEE80211_NODE_STAT(ni, rx_decap);
- goto err;
- }
- eh = mtod(m, struct ether_header *);
- if (!ieee80211_node_is_authorized(ni)) {
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ continue;
+ if (TAILQ_NEXT(vap, iv_next) != NULL) {
/*
- * Deny any non-PAE frames received prior to
- * authorization. For open/shared-key
- * authentication the port is mark authorized
- * after authentication completes. For 802.1x
- * the port is not marked authorized by the
- * authenticator until the handshake has completed.
+ * Packet contents are changed by ieee80211_decap
+ * so do a deep copy of the packet.
*/
- if (eh->ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- eh->ether_shost, "data",
- "unauthorized port: ether type 0x%x len %u",
- eh->ether_type, m->m_pkthdr.len);
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(ni, rx_unauth);
- goto err;
+ mcopy = m_dup(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ /* XXX stat+msg */
+ continue;
}
} else {
- /*
- * When denying unencrypted frames, discard
- * any non-PAE frames received without encryption.
- */
- if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
- eh->ether_type != htons(ETHERTYPE_PAE)) {
- /*
- * Drop unencrypted frames.
- */
- ic->ic_stats.is_rx_unencrypted++;
- IEEE80211_NODE_STAT(ni, rx_unencrypted);
- goto out;
- }
- }
- /* XXX require HT? */
- if (qos & IEEE80211_QOS_AMSDU) {
- m = ieee80211_decap_amsdu(ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
-#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
- m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
- struct llc *llc;
-
- /*
- * Check for fast-frame tunnel encapsulation.
- */
- if (m->m_len < FF_LLC_SIZE &&
- (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "fast-frame",
- "%s", "m_pullup(llc) failed");
- ic->ic_stats.is_rx_tooshort++;
- return IEEE80211_FC0_TYPE_DATA;
- }
- llc = (struct llc *)(mtod(m, uint8_t *) +
- sizeof(struct ether_header));
- if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
- m_adj(m, FF_LLC_SIZE);
- m = ieee80211_decap_fastframe(ic, ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- }
- }
-#undef FF_LLC_SIZE
- ieee80211_deliver_data(ic, ni, m);
- return IEEE80211_FC0_TYPE_DATA;
-
- case IEEE80211_FC0_TYPE_MGT:
- ic->ic_stats.is_rx_mgmt++;
- IEEE80211_NODE_STAT(ni, rx_mgmt);
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto err;
- }
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "mgt", "too short: len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
-#ifdef IEEE80211_DEBUG
- if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
- ieee80211_msg_dumppkts(ic)) {
- if_printf(ic->ic_ifp, "received %s from %s rssi %d\n",
- ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(wh->i_addr2), rssi);
- }
-#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
- /*
- * Only shared key auth frames with a challenge
- * should be encrypted, discard all others.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "WEP set but not permitted");
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
- goto out;
- }
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "mgt", "%s", "WEP set but PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- goto out;
- }
- hdrspace = ieee80211_hdrspace(ic, wh);
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp);
- m_freem(m);
- return IEEE80211_FC0_TYPE_MGT;
-
- case IEEE80211_FC0_TYPE_CTL:
- ic->ic_stats.is_rx_ctl++;
- IEEE80211_NODE_STAT(ni, rx_ctrl);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PS_POLL:
- ieee80211_recv_pspoll(ic, ni, m);
- break;
- case IEEE80211_FC0_SUBTYPE_BAR:
- ieee80211_recv_bar(ni, m);
- break;
- }
+ mcopy = m;
+ m = NULL;
}
- goto out;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, NULL, "bad frame type 0x%x", type);
- /* should not come here */
- break;
+ ni = ieee80211_ref_node(vap->iv_bss);
+ type = ieee80211_input(ni, mcopy, rssi, noise, rstamp);
+ ieee80211_free_node(ni);
}
-err:
- ifp->if_ierrors++;
-out:
- if (m != NULL) {
- if (bpf_peers_present(ic->ic_rawbpf) && need_tap)
- bpf_mtap(ic->ic_rawbpf, m);
+ if (m != NULL) /* no vaps, reclaim mbuf */
m_freem(m);
- }
return type;
-#undef SEQ_LEQ
}
/*
* This function reassemble fragments.
+ *
+ * XXX should handle 3 concurrent reassemblies per-spec.
*/
-static struct mbuf *
-ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int hdrspace)
+struct mbuf *
+ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
struct ieee80211_frame *lwh;
uint16_t rxseq;
@@ -681,7 +167,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
if (mfrag == NULL) {
if (fragno != 0) { /* !first fragment, discard */
- ic->ic_stats.is_rx_defrag++;
+ vap->iv_stats.is_rx_defrag++;
IEEE80211_NODE_STAT(ni, rx_defrag);
m_freem(m);
return NULL;
@@ -705,12 +191,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
}
void
-ieee80211_deliver_data(struct ieee80211com *ic,
+ieee80211_deliver_data(struct ieee80211vap *vap,
struct ieee80211_node *ni, struct mbuf *m)
{
struct ether_header *eh = mtod(m, struct ether_header *);
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
+ /* NB: see hostap_deliver_data, this path doesn't handle hostap */
+ KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
/*
* Do accounting.
*/
@@ -722,66 +210,21 @@ ieee80211_deliver_data(struct ieee80211com *ic,
IEEE80211_NODE_STAT(ni, rx_mcast);
} else
IEEE80211_NODE_STAT(ni, rx_ucast);
+ m->m_pkthdr.rcvif = ifp;
/* clear driver/net80211 flags before passing up */
m->m_flags &= ~M_80211_RX;
- /* perform as a bridge within the AP */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
- struct mbuf *m1 = NULL;
-
- if (m->m_flags & M_MCAST) {
- m1 = m_dup(m, M_DONTWAIT);
- if (m1 == NULL)
- ifp->if_oerrors++;
- else
- m1->m_flags |= M_MCAST;
- } else {
- /*
- * Check if the destination is known; if so
- * and the port is authorized dispatch directly.
- */
- struct ieee80211_node *sta =
- ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
- if (sta != NULL) {
- if (ieee80211_node_is_authorized(sta)) {
- /*
- * Beware of sending to ourself; this
- * needs to happen via the normal
- * input path.
- */
- if (sta != ic->ic_bss) {
- m1 = m;
- m = NULL;
- }
- } else {
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(sta, rx_unauth);
- }
- ieee80211_free_node(sta);
- }
- }
- if (m1 != NULL) {
- int error;
-
- /* XXX does not work well with WME */
- IFQ_HANDOFF(ifp, m1, error);
- }
- }
- if (m != NULL) {
- m->m_pkthdr.rcvif = ifp;
- if (ni->ni_vlan != 0) {
- /* attach vlan tag */
- m->m_pkthdr.ether_vtag = ni->ni_vlan;
- m->m_flags |= M_VLANTAG;
- }
- (*ifp->if_input)(ifp, m);
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
}
+ ifp->if_input(ifp, m);
}
-static struct mbuf *
-ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen)
+struct mbuf *
+ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
{
struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
struct ether_header *eh;
@@ -916,28 +359,28 @@ ieee80211_decap1(struct mbuf *m, int *framelen)
* for delivery. The second frame is returned for delivery
* via the normal path.
*/
-static struct mbuf *
-ieee80211_decap_fastframe(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+struct mbuf *
+ieee80211_decap_fastframe(struct ieee80211_node *ni, struct mbuf *m)
{
#define MS(x,f) (((x) & f) >> f##_S)
+ struct ieee80211vap *vap = ni->ni_vap;
uint32_t ath;
struct mbuf *n;
int framelen;
m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"unsupport tunnel protocol, header 0x%x", ath);
- ic->ic_stats.is_ff_badhdr++;
+ vap->iv_stats.is_ff_badhdr++;
m_freem(m);
return NULL;
}
/* NB: skip header and alignment padding */
m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
- ic->ic_stats.is_ff_decap++;
+ vap->iv_stats.is_ff_decap++;
/*
* Decap the first frame, bust it apart from the
@@ -946,21 +389,22 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
return NULL;
}
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_ff_split++;
+ vap->iv_stats.is_ff_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m); /* 1st of pair */
+ /* XXX not right for WDS */
+ vap->iv_deliver_data(vap, ni, m); /* 1st of pair */
/*
* Decap second frame.
@@ -968,9 +412,9 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
m_adj(n, roundup2(framelen, 4) - framelen); /* padding */
n = ieee80211_decap1(n, &framelen);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
}
/* XXX verify framelen against mbuf contents */
return n; /* 2nd delivered by caller */
@@ -984,7 +428,7 @@ int
ieee80211_setup_rates(struct ieee80211_node *ni,
const uint8_t *rates, const uint8_t *xrates, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
@@ -998,11 +442,10 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
nxrates = xrates[1];
if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
- "[%s] extended rate set too large;"
- " only using %u of %u rates\n",
- ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]);
- ic->ic_stats.is_rx_rstoobig++;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni,
+ "extended rate set too large; only using "
+ "%u of %u rates", nxrates, xrates[1]);
+ vap->iv_stats.is_rx_rstoobig++;
}
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
rs->rs_nrates += nxrates;
@@ -1010,101 +453,6 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
return ieee80211_fix_rate(ni, rs, flags);
}
-static void
-ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp,
- uint16_t seq, uint16_t status)
-{
-
- if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /*
- * Clear any challenge text that may be there if
- * a previous shared key auth failed and then an
- * open auth is attempted.
- */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- /* should not come here */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad operating mode %u", ic->ic_opmode);
- break;
-
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN ||
- seq != IEEE80211_AUTH_OPEN_REQUEST) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- /* always accept open authentication requests */
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL)
- return;
- } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
-
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (open)\n",
- ether_sprintf(ni->ni_macaddr));
- /*
- * When 802.1x is not in use mark the port
- * authorized at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH ||
- seq != IEEE80211_AUTH_OPEN_RESPONSE) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] open auth failed (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- } else
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- }
-}
-
/*
* Send a management frame error response to the specified
* station. If ni is associated with the station then use
@@ -1112,14 +460,26 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
* transmitting the frame and then free the reference so
* it will go away as soon as the frame has been transmitted.
*/
-static void
-ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
- const uint8_t *mac, int subtype, int arg)
+void
+ieee80211_send_error(struct ieee80211_node *ni,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg)
{
+ struct ieee80211vap *vap = ni->ni_vap;
int istmp;
- if (ni == ic->ic_bss) {
- ni = ieee80211_tmp_node(ic, mac);
+ if (ni == vap->iv_bss) {
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ /*
+ * XXX hack until we get rid of this routine.
+ * We can be called prior to the vap reaching
+ * run state under certain conditions in which
+ * case iv_bss->ni_chan will not be setup.
+ * Check for this explicitly and and just ignore
+ * the request.
+ */
+ return;
+ }
+ ni = ieee80211_tmp_node(vap, mac);
if (ni == NULL) {
/* XXX msg */
return;
@@ -1127,2204 +487,342 @@ ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
istmp = 1;
} else
istmp = 0;
- IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
+ IEEE80211_SEND_MGMT(ni, subtype, arg);
if (istmp)
ieee80211_free_node(ni);
}
-static int
-alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+int
+ieee80211_alloc_challenge(struct ieee80211_node *ni)
{
if (ni->ni_challenge == NULL)
MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN,
M_80211_NODE, M_NOWAIT);
if (ni->ni_challenge == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key challenge alloc failed\n",
- ether_sprintf(ni->ni_macaddr));
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
+ "%s", "shared key challenge alloc failed");
/* XXX statistic */
}
return (ni->ni_challenge != NULL);
}
-/* XXX TODO: add statistics */
-static void
-ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
- uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni,
- int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
-{
- uint8_t *challenge;
- int allocbs, estatus;
-
- /*
- * NB: this can happen as we allow pre-shared key
- * authentication to be enabled w/o wep being turned
- * on so that configuration of these can be done
- * in any order. It may be better to enforce the
- * ordering in which case this check would just be
- * for sanity/consistency.
- */
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", " PRIVACY is disabled");
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
- /*
- * Pre-shared key authentication is evil; accept
- * it only if explicitly configured (it is supported
- * mainly for compatibility with clients like OS X).
- */
- if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
- ni->ni_authmode != IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
-
- challenge = NULL;
- if (frm + 1 < efrm) {
- if ((frm[1] + 2) > (efrm - frm)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "ie %d/%d too long",
- frm[0], (frm[1] + 2) - (efrm - frm));
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (*frm == IEEE80211_ELEMID_CHALLENGE)
- challenge = frm;
- frm += frm[1] + 2;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", "no challenge");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad challenge len %d", challenge[1]);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- default:
- break;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_IBSS:
- case IEEE80211_M_WDS:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad operating mode %u", ic->ic_opmode);
- return;
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad state %u", ic->ic_state);
- estatus = IEEE80211_STATUS_ALG; /* XXX */
- goto bad;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_REQUEST:
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL) {
- /* NB: no way to return an error */
- return;
- }
- allocbs = 1;
- } else {
- if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- allocbs = 0;
- }
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (!alloc_challenge(ic, ni)) {
- /* NB: don't return error so they rexmit */
- return;
- }
- get_random_bytes(ni->ni_challenge,
- IEEE80211_CHALLENGE_LEN);
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key %sauth request\n",
- ether_sprintf(ni->ni_macaddr),
- allocbs ? "" : "re");
- break;
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "unknown station");
- /* NB: don't send a response */
- return;
- }
- if (ni->ni_challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "no challenge recorded");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (memcmp(ni->ni_challenge, &challenge[2],
- challenge[1]) != 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "challenge mismatch");
- ic->ic_stats.is_rx_auth_fail++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (shared key)\n",
- ether_sprintf(ni->ni_macaddr));
- ieee80211_node_authorize(ni);
- break;
- default:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_SEQUENCE;
- goto bad;
- }
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH)
- return;
- switch (seq) {
- case IEEE80211_AUTH_SHARED_PASS:
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key auth failed (reason %d)\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)),
- status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- return;
- }
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- if (!alloc_challenge(ic, ni))
- return;
- /* XXX could optimize by passing recvd challenge */
- memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
- wh, "shared key auth", "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- break;
- }
- return;
-bad:
- /*
- * Send an error response; but only when operating as an AP.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (estatus<<16));
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
- /*
- * Kick the state machine. This short-circuits
- * using the mgt frame timeout to trigger the
- * state transition.
- */
- if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- }
-}
-
-/* Verify the existence and length of __elem or get out. */
-#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
- if ((__elem) == NULL) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "%s", "no " #__elem ); \
- ic->ic_stats.is_rx_elem_missing++; \
- return; \
- } \
- if ((__elem)[1] > (__maxlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "bad " #__elem " len %d", (__elem)[1]); \
- ic->ic_stats.is_rx_elem_toobig++; \
- return; \
- } \
-} while (0)
-
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
- if ((_len) < (_minlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "ie too short, got %d, expected %d", \
- (_len), (_minlen)); \
- ic->ic_stats.is_rx_elem_toosmall++; \
- _action; \
- } \
-} while (0)
-
-#ifdef IEEE80211_DEBUG
-static void
-ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
- uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
-{
- printf("[%s] discard %s frame, ssid mismatch: ",
- ether_sprintf(mac), tag);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf("\n");
-}
-
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- if (ieee80211_msg_input(ic)) \
- ieee80211_ssid_mismatch(ic, \
- ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- wh->i_addr2, _ssid); \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#else /* !IEEE80211_DEBUG */
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#endif /* !IEEE80211_DEBUG */
-
-/* unalligned little endian access */
-#define LE_READ_2(p) \
- ((uint16_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((uint32_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8) | \
- (((const uint8_t *)(p))[2] << 16) | \
- (((const uint8_t *)(p))[3] << 24)))
-
-static __inline int
-iswpaoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
-}
-
-static __inline int
-iswmeoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
-}
-
-static __inline int
-iswmeparam(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_PARAM_OUI_SUBTYPE;
-}
-
-static __inline int
-iswmeinfo(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_INFO_OUI_SUBTYPE;
-}
-
-static __inline int
-isatherosoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
-}
-
-static __inline int
-ishtcapoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
-}
-
-static __inline int
-ishtinfooui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
-}
-
-/*
- * Convert a WPA cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-wpa_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case WPA_SEL(WPA_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case WPA_SEL(WPA_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert a WPA key management/authentication algorithm
- * to an internal code.
- */
-static int
-wpa_keymgmt(uint8_t *sel)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_ASE_8021X_UNSPEC):
- return WPA_ASE_8021X_UNSPEC;
- case WPA_SEL(WPA_ASE_8021X_PSK):
- return WPA_ASE_8021X_PSK;
- case WPA_SEL(WPA_ASE_NONE):
- return WPA_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Parse a WPA information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts: OUI, type,
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 14) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 6, len -= 4; /* NB: len is payload only */
- /* NB: iswapoui already validated the OUI and type */
- w = LE_READ_2(frm);
- if (w != WPA_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= wpa_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & WPA_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
-
- if (len > 2) /* optional capabilities */
- rsn->rsn_caps = LE_READ_2(frm);
-
- return 0;
-}
-
-/*
- * Convert an RSN cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-rsn_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case RSN_SEL(RSN_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case RSN_SEL(RSN_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- case RSN_SEL(RSN_CSE_WRAP):
- return IEEE80211_CIPHER_AES_OCB;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert an RSN key management/authentication algorithm
- * to an internal code.
- */
-static int
-rsn_keymgmt(uint8_t *sel)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_ASE_8021X_UNSPEC):
- return RSN_ASE_8021X_UNSPEC;
- case RSN_SEL(RSN_ASE_8021X_PSK):
- return RSN_ASE_8021X_PSK;
- case RSN_SEL(RSN_ASE_NONE):
- return RSN_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef RSN_SEL
-}
-
-/*
- * Parse a WPA/RSN information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts:
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 10) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2;
- w = LE_READ_2(frm);
- if (w != RSN_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= rsn_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & RSN_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
-
- /* optional RSN capabilities */
- if (len > 2)
- rsn->rsn_caps = LE_READ_2(frm);
- /* XXXPMKID */
-
- return 0;
-}
-
-static int
-ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
-#define MS(_v, _f) (((_v) & _f) >> _f##_S)
- struct ieee80211_wme_state *wme = &ic->ic_wme;
- u_int len = frm[1], qosinfo;
- int i;
-
- if (len < sizeof(struct ieee80211_wme_param)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
- wh, "WME", "too short, len %u", len);
- return -1;
- }
- qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
- qosinfo &= WME_QOSINFO_COUNT;
- /* XXX do proper check for wraparound */
- if (qosinfo == wme->wme_wmeChanParams.cap_info)
- return 0;
- frm += __offsetof(struct ieee80211_wme_param, params_acParams);
- for (i = 0; i < WME_NUM_AC; i++) {
- struct wmeParams *wmep =
- &wme->wme_wmeChanParams.cap_wmeParams[i];
- /* NB: ACI not used */
- wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
- wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
- wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
- wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
- wmep->wmep_txopLimit = LE_READ_2(frm+2);
- frm += 4;
- }
- wme->wme_wmeChanParams.cap_info = qosinfo;
- return 1;
-#undef MS
-}
-
-static int
-ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
- struct ieee80211com *ic = ni->ni_ic;
- const struct ieee80211_ath_ie *ath;
- u_int len = frm[1];
- int capschanged;
- uint16_t defkeyix;
-
- if (len < sizeof(struct ieee80211_ath_ie)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
- wh, "Atheros", "too short, len %u", len);
- return -1;
- }
- ath = (const struct ieee80211_ath_ie *)frm;
- capschanged = (ni->ni_ath_flags != ath->ath_capability);
- defkeyix = LE_READ_2(ath->ath_defkeyix);
- if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
- ni->ni_ath_flags = ath->ath_capability;
- ni->ni_ath_defkeyix = defkeyix;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
- "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_ath_flags, ni->ni_ath_defkeyix);
- }
- if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) {
- uint16_t curflags, newflags;
-
- /*
- * Check for turbo mode switch. Calculate flags
- * for the new mode and effect the switch.
- */
- newflags = curflags = ic->ic_bsschan->ic_flags;
- /* NB: BOOST is not in ic_flags, so get it from the ie */
- if (ath->ath_capability & ATHEROS_CAP_BOOST)
- newflags |= IEEE80211_CHAN_TURBO;
- else
- newflags &= ~IEEE80211_CHAN_TURBO;
- if (newflags != curflags)
- ieee80211_dturbo_switch(ic, newflags);
- }
- return capschanged;
-}
-
void
-ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie)
+ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie)
{
const struct ieee80211_ath_ie *ath =
(const struct ieee80211_ath_ie *) ie;
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
- ieee80211_saveie(&ni->ni_ath_ie, ie);
-}
-
-void
-ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
-{
- u_int ielen = ie[1]+2;
- /*
- * Record information element for later use.
- */
- if (*iep == NULL || (*iep)[1] != ie[1]) {
- if (*iep != NULL)
- FREE(*iep, M_80211_NODE);
- MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT);
- }
- if (*iep != NULL)
- memcpy(*iep, ie, ielen);
- /* XXX note failure */
}
-/* XXX find a better place for definition */
-struct l2_update_frame {
- struct ether_header eh;
- uint8_t dsap;
- uint8_t ssap;
- uint8_t control;
- uint8_t xid[3];
-} __packed;
-
/*
- * Deliver a TGf L2UF frame on behalf of a station.
- * This primes any bridge when the station is roaming
- * between ap's on the same wired network.
+ * Parse a Beacon or ProbeResponse frame and return the
+ * useful information in an ieee80211_scanparams structure.
+ * Status is set to 0 if no problems were found; otherwise
+ * a bitmask of IEEE80211_BPARSE_* items is returned that
+ * describes the problems detected.
*/
-static void
-ieee80211_deliver_l2uf(struct ieee80211_node *ni)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct mbuf *m;
- struct l2_update_frame *l2uf;
- struct ether_header *eh;
-
- m = m_gethdr(M_NOWAIT, MT_DATA);
- if (m == NULL) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
- "%s", "no mbuf for l2uf frame");
- ic->ic_stats.is_rx_nobuf++; /* XXX not right */
- return;
- }
- l2uf = mtod(m, struct l2_update_frame *);
- eh = &l2uf->eh;
- /* dst: Broadcast address */
- IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
- /* src: associated STA */
- IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
- eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
-
- l2uf->dsap = 0;
- l2uf->ssap = 0;
- l2uf->control = 0xf5;
- l2uf->xid[0] = 0x81;
- l2uf->xid[1] = 0x80;
- l2uf->xid[2] = 0x00;
-
- m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
- ieee80211_deliver_data(ic, ni, m);
-}
-
-static __inline int
-contbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static __inline int
-startbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
- !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static void
-ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int rate)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, rate);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_norate++;
-}
-
-static void
-capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int capinfo)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s mismatch 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, capinfo);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_capmismatch++;
-}
-
-static void
-htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp)
+int
+ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
+ struct ieee80211_scanparams *scan)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
- "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
- /* XXX no better code */
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
- ieee80211_node_leave(ic, ni);
-}
-
-void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni,
- int subtype, int rssi, int noise, uint32_t rstamp)
-{
-#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
- uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
- int reassoc, resp, allocbs;
- uint8_t rate;
- wh = mtod(m0, struct ieee80211_frame *);
+ wh = mtod(m, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
- efrm = mtod(m0, uint8_t *) + m0->m_len;
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- case IEEE80211_FC0_SUBTYPE_BEACON: {
- struct ieee80211_scanparams scan;
-
- /*
- * We process beacon/probe response frames:
- * o when scanning, or
- * o station mode when associated (to collect state
- * updates such as 802.11g slot time), or
- * o adhoc mode (to discover neighbors)
- * Frames otherwise received are discarded.
- */
- if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
- (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
- ic->ic_opmode == IEEE80211_M_IBSS)) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * beacon/probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] capability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] country information
- * [tlv] parameter set (FH/DS)
- * [tlv] erp information
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
- memset(&scan, 0, sizeof(scan));
- scan.tstamp = frm; frm += 8;
- scan.bintval = le16toh(*(uint16_t *)frm); frm += 2;
- scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan);
- scan.curchan = ic->ic_curchan;
-
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- scan.ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- scan.rates = frm;
- break;
- case IEEE80211_ELEMID_COUNTRY:
- scan.country = frm;
- break;
- case IEEE80211_ELEMID_FHPARMS:
- if (ic->ic_phytype == IEEE80211_T_FH) {
- scan.fhdwell = LE_READ_2(&frm[2]);
- scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]);
- scan.fhindex = frm[6];
- }
- break;
- case IEEE80211_ELEMID_DSPARMS:
- /*
- * XXX hack this since depending on phytype
- * is problematic for multi-mode devices.
- */
- if (ic->ic_phytype != IEEE80211_T_FH)
- scan.bchan = frm[2];
- break;
- case IEEE80211_ELEMID_TIM:
- /* XXX ATIM? */
- scan.tim = frm;
- scan.timoff = frm - mtod(m0, uint8_t *);
- break;
- case IEEE80211_ELEMID_IBSSPARMS:
- break;
- case IEEE80211_ELEMID_XRATES:
- scan.xrates = frm;
- break;
- case IEEE80211_ELEMID_ERP:
- if (frm[1] != 1) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID, wh, "ERP",
- "bad len %u", frm[1]);
- ic->ic_stats.is_rx_elem_toobig++;
- break;
- }
- scan.erp = frm[2];
- break;
- case IEEE80211_ELEMID_HTCAP:
- scan.htcap = frm;
- break;
- case IEEE80211_ELEMID_RSN:
- scan.rsn = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- scan.htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- scan.wpa = frm;
- else if (iswmeparam(frm) || iswmeinfo(frm))
- scan.wme = frm;
- else if (isatherosoui(frm))
- scan.ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (scan.htcap == NULL)
- scan.htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (scan.htinfo == NULL)
- scan.htcap = frm;
- }
- }
- break;
- default:
- IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
- wh, "unhandled",
- "id %u, len %u", *frm, frm[1]);
- ic->ic_stats.is_rx_elem_unknown++;
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
- if (scan.xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(scan.xrates,
- IEEE80211_RATE_MAXSIZE - scan.rates[1]);
- IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
-#if IEEE80211_CHAN_MAX < 255
- if (scan.chan > IEEE80211_CHAN_MAX) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "invalid channel %u", scan.chan);
- ic->ic_stats.is_rx_badchan++;
- return;
- }
-#endif
- if (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan &&
- ic->ic_phytype != IEEE80211_T_FH) {
- /*
- * Frame was received on a channel different from the
- * one indicated in the DS params element id;
- * silently discard it.
- *
- * NB: this can happen due to signal leakage.
- * But we should take it for FH phy because
- * the rssi value should be correct even for
- * different hop pattern in FH.
- */
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "for off-channel %u",
- IEEE80211_CHAN2IEEE(scan.curchan));
- ic->ic_stats.is_rx_chanmismatch++;
- return;
- }
- if (!(IEEE80211_BINTVAL_MIN <= scan.bintval &&
- scan.bintval <= IEEE80211_BINTVAL_MAX)) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "bogus beacon interval", scan.bintval);
- ic->ic_stats.is_rx_badbintval++;
- return;
- }
- /*
- * Process HT ie's. This is complicated by our
- * accepting both the standard ie's and the pre-draft
- * vendor OUI ie's that some vendors still use/require.
- */
- if (scan.htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htcap[1],
- scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- scan.htcap = NULL);
- }
- if (scan.htinfo != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
- scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
- sizeof(struct ieee80211_ie_htinfo)-2,
- scan.htinfo = NULL);
- }
-
- /*
- * Count frame now that we know it's to be processed.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
- ic->ic_stats.is_rx_beacon++; /* XXX remove */
- IEEE80211_NODE_STAT(ni, rx_beacons);
- } else
- IEEE80211_NODE_STAT(ni, rx_proberesp);
-
- /*
- * When operating in station mode, check for state updates.
- * Be careful to ignore beacons received while doing a
- * background scan. We consider only 11g/WMM stuff right now.
- */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ni->ni_associd != 0 &&
- ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
- IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
- /* record tsf of last beacon */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- /* count beacon frame for s/w bmiss handling */
- ic->ic_swbmiss_count++;
- ic->ic_bmiss_count = 0;
- if (ni->ni_erp != scan.erp) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] erp change: was 0x%x, now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_erp, scan.erp);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- ni->ni_erp = scan.erp;
- /* XXX statistic */
- }
- if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] capabilities change: before 0x%x,"
- " now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_capinfo, scan.capinfo);
- /*
- * NB: we assume short preamble doesn't
- * change dynamically
- */
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
- (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
- ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
- | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
- /* XXX statistic */
- }
- if (scan.wme != NULL &&
- (ni->ni_flags & IEEE80211_NODE_QOS) &&
- ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
- ieee80211_wme_updateparams(ic);
- if (scan.ath != NULL)
- ieee80211_parse_athparams(ni, scan.ath, wh);
- if (scan.htcap != NULL)
- ieee80211_parse_htcap(ni, scan.htcap);
- if (scan.htinfo != NULL) {
- ieee80211_parse_htinfo(ni, scan.htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- if (scan.tim != NULL) {
- struct ieee80211_tim_ie *tim =
- (struct ieee80211_tim_ie *) scan.tim;
-#if 0
- int aid = IEEE80211_AID(ni->ni_associd);
- int ix = aid / NBBY;
- int min = tim->tim_bitctl &~ 1;
- int max = tim->tim_len + min - 4;
- if ((tim->tim_bitctl&1) ||
- (min <= ix && ix <= max &&
- isset(tim->tim_bitmap - min, aid))) {
- /*
- * XXX Do not let bg scan kick off
- * we are expecting data.
- */
- ic->ic_lastdata = ticks;
- ieee80211_sta_pwrsave(ic, 0);
- }
-#endif
- ni->ni_dtim_count = tim->tim_count;
- ni->ni_dtim_period = tim->tim_period;
+ efrm = mtod(m, uint8_t *) + m->m_len;
+ scan->status = 0;
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ memset(scan, 0, sizeof(*scan));
+ scan->tstamp = frm; frm += 8;
+ scan->bintval = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+ scan->chan = scan->bchan;
+ scan->ies = frm;
+ scan->ies_len = efrm - frm;
+
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ scan->ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ scan->rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ scan->country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ scan->fhdwell = LE_READ_2(&frm[2]);
+ scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ scan->fhindex = frm[6];
}
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
/*
- * If scanning, pass the info to the scan module.
- * Otherwise, check if it's the right time to do
- * a background scan. Background scanning must
- * be enabled and we must not be operating in the
- * turbo phase of dynamic turbo mode. Then,
- * it's been a while since the last background
- * scan and if no data frames have come through
- * recently, kick off a scan. Note that this
- * is the mechanism by which a background scan
- * is started _and_ continued each time we
- * return on-channel to receive a beacon from
- * our ap.
+ * XXX hack this since depending on phytype
+ * is problematic for multi-mode devices.
*/
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- } else if (contbgscan(ic)) {
- ieee80211_bg_scan(ic);
- } else if (startbgscan(ic)) {
-#if 0
- /* wakeup if we are sleeing */
- ieee80211_set_pwrsave(ic, 0);
-#endif
- ieee80211_bg_scan(ic);
- }
- return;
- }
- /*
- * If scanning, just pass information to the scan module.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
- /*
- * Actively scanning a channel marked passive;
- * send a probe request now that we know there
- * is 802.11 traffic present.
- *
- * XXX check if the beacon we recv'd gives
- * us what we need and suppress the probe req
- */
- ieee80211_probe_curchan(ic, 1);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
- }
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- return;
- }
- if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Create a new entry in the neighbor table.
- */
- ni = ieee80211_add_neighbor(ic, wh, &scan);
- } else if (ni->ni_capinfo == 0) {
- /*
- * Update faked node created on transmit.
- * Note this also updates the tsf.
- */
- ieee80211_init_neighbor(ni, wh, &scan);
- } else {
- /*
- * Record tsf for potential resync.
- */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- }
- if (ni != NULL) {
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- }
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
- /* frame must be directed */
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */
- return;
- }
-
- /*
- * prreq frame format
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] Atheros capabilities
- */
- ssid = rates = xrates = ath = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (isatherosoui(frm))
- ath = frm;
+ if (ic->ic_phytype != IEEE80211_T_FH)
+ scan->chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ /* XXX ATIM? */
+ scan->tim = frm;
+ scan->timoff = frm - mtod(m, uint8_t *);
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ case IEEE80211_ELEMID_CFPARMS:
+ /* NB: avoid debugging complaints */
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ scan->xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID, wh, "ERP",
+ "bad len %u", frm[1]);
+ vap->iv_stats.is_rx_elem_toobig++;
break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "no ssid with ssid suppression enabled");
- ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
- return;
- }
-
- allocbs = 0;
- if (ni == ic->ic_bss) {
- if (ic->ic_opmode != IEEE80211_M_IBSS) {
- ni = ieee80211_tmp_node(ic, wh->i_addr2);
- allocbs = 1;
- } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ scan->erp = frm[2] | 0x100;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan->htcap = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ scan->rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan->htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ scan->wpa = frm;
+ else if (iswmeparam(frm) || iswmeinfo(frm))
+ scan->wme = frm;
+ else if (isatherosoui(frm))
+ scan->ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
/*
- * XXX Cannot tell if the sender is operating
- * in ibss mode. But we need a new node to
- * send the response so blindly add them to the
- * neighbor table.
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
*/
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- }
- if (ni == NULL)
- return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv probe req\n", ether_sprintf(wh->i_addr2));
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
- | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "recv'd rate set invalid");
- } else {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
- }
- if (allocbs) {
- /*
- * Temporary node created just to send a
- * response, reclaim immediately.
- */
- ieee80211_free_node(ni);
- } else if (ath != NULL)
- ieee80211_saveath(ni, ath);
- break;
-
- case IEEE80211_FC0_SUBTYPE_AUTH: {
- uint16_t algo, seq, status;
- /*
- * auth frame format
- * [2] algorithm
- * [2] sequence
- * [2] status
- * [tlv*] challenge
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- algo = le16toh(*(uint16_t *)frm);
- seq = le16toh(*(uint16_t *)(frm + 2));
- status = le16toh(*(uint16_t *)(frm + 4));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv auth frame with algorithm %d seq %d\n",
- ether_sprintf(wh->i_addr2), algo, seq);
- /*
- * Consult the ACL policy module if setup.
- */
- if (ic->ic_acl != NULL &&
- !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
- wh, "auth", "%s", "disallowed by ACL");
- ic->ic_stats.is_rx_acl++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
- }
- return;
- }
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
- wh, "auth", "%s", "TKIP countermeasures enabled");
- ic->ic_stats.is_rx_auth_countermeasures++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- IEEE80211_REASON_MIC_FAILURE);
- }
- return;
- }
- if (algo == IEEE80211_AUTH_ALG_SHARED)
- ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
- noise, rstamp, seq, status);
- else if (algo == IEEE80211_AUTH_ALG_OPEN)
- ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp,
- seq, status);
- else {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "auth", "unsupported alg %d", algo);
- ic->ic_stats.is_rx_auth_unsupported++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX not right */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
- case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
- uint16_t capinfo, lintval;
- struct ieee80211_rsnparms rsnparms;
- uint8_t reason;
- int badwparsn;
-
- if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- reassoc = 1;
- resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
- } else {
- reassoc = 0;
- resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- }
- /*
- * asreq frame format
- * [2] capability information
- * [2] listen interval
- * [6*] current AP address (reassoc only)
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
- if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "wrong bssid");
- ic->ic_stats.is_rx_assoc_bss++;
- return;
- }
- capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- lintval = le16toh(*(uint16_t *)frm); frm += 2;
- if (reassoc)
- frm += 6; /* ignore current AP info */
- ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- /* XXX verify only one of RSN and WPA ie's? */
- case IEEE80211_ELEMID_RSN:
- rsn = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- wpa = frm;
- else if (iswmeinfo(frm))
- wme = frm;
- else if (isatherosoui(frm))
- ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- if (ishtcapoui(frm) && htcap == NULL)
- htcap = frm;
+ if (ishtcapoui(frm)) {
+ if (scan->htcap == NULL)
+ scan->htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan->htinfo == NULL)
+ scan->htcap = frm;
}
- break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if (htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(htcap[1],
- htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- return); /* XXX just NULL out? */
- }
-
- if (ni == ic->ic_bss) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, sta not authenticated\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ic->ic_stats.is_rx_assoc_notauth++;
- return;
- }
- /* assert right association security credentials */
- badwparsn = 0;
- switch (ic->ic_flags & IEEE80211_F_WPA) {
- case IEEE80211_F_WPA1:
- if (wpa == NULL)
- badwparsn = 1;
- break;
- case IEEE80211_F_WPA2:
- if (rsn == NULL)
- badwparsn = 1;
break;
- case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
- if (wpa == NULL && rsn == NULL)
- badwparsn = 1;
+ default:
+ IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
+ wh, "unhandled",
+ "id %u, len %u", *frm, frm[1]);
+ vap->iv_stats.is_rx_elem_unknown++;
break;
}
- if (badwparsn) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] no WPA/RSN IE in association request\n",
- ether_sprintf(wh->i_addr2));
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_IE_INVALID);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- if (wpa != NULL || rsn != NULL) {
- /*
- * Parse WPA/RSN information element. Note that
- * we initialize the param block from the node
- * state so that information in the IE overrides
- * our defaults. The resulting parameters are
- * installed below after the association is assured.
- */
- rsnparms = ni->ni_rsn;
- if (wpa != NULL)
- reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh);
- else
- reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh);
- if (reason != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
- ieee80211_node_leave(ic, ni);
- /* XXX distinguish WPA/RSN? */
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
- ether_sprintf(wh->i_addr2),
- wpa != NULL ? "WPA" : "RSN",
- rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen,
- rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen,
- rsnparms.rsn_keymgmt, rsnparms.rsn_caps);
- }
- /* discard challenge after association */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* NB: 802.11 spec says to ignore station's privacy bit */
- if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
- capinfomismatch(ni, wh, reassoc, resp,
- "capability", capinfo);
- return;
- }
- /*
- * Disallow re-associate w/ invalid slot time setting.
- */
- if (ni->ni_associd != 0 &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
- ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
- capinfomismatch(ni, wh, reassoc, resp,
- "slot time", capinfo);
- return;
- }
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- ratesetmismatch(ni, wh, reassoc, resp, "basic", rate);
- return;
- }
- /*
- * If constrained to 11g-only stations reject an
- * 11b-only station. We cheat a bit here by looking
- * at the max negotiated xmit rate and assuming anyone
- * with a best rate <24Mb/s is an 11b station.
- */
- if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) {
- ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
- return;
- }
- /* XXX enforce PUREN */
- /* 802.11n-specific rateset handling */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
- rate = ieee80211_setup_htrates(ni, htcap,
- IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
- IEEE80211_F_DOBRS);
- if (rate & IEEE80211_RATE_BASIC) {
- /* XXX 11n-specific stat */
- ratesetmismatch(ni, wh, reassoc, resp,
- "HT", rate);
- return;
- }
- ieee80211_ht_node_init(ni, htcap);
- } else if (ni->ni_flags & IEEE80211_NODE_HT)
- ieee80211_ht_node_cleanup(ni);
- /*
- * Allow AMPDU operation only with unencrypted traffic
- * or AES-CCM; the 11n spec only specifies these ciphers
- * so permitting any others is undefined and can lead
- * to interoperability problems.
- *
- * NB: We check for AES by looking at the GTK cipher
- * since the WPA/11i specs say the PTK cipher has
- * to be "as good or better".
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- (((ic->ic_flags & IEEE80211_F_WPA) &&
- rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
- (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
- "disallow HT use because WEP or TKIP requested, "
- "capinfo 0x%x mcastcipher %d", capinfo,
- rsnparms.rsn_mcastcipher);
- ieee80211_ht_node_cleanup(ni);
- ic->ic_stats.is_ht_assoc_downgrade++;
- }
- /*
- * If constrained to 11n-only stations reject legacy stations.
- */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
- (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
- htcapmismatch(ni, wh, reassoc, resp);
- ic->ic_stats.is_ht_assoc_nohtcap++;
- return;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- ni->ni_intval = lintval;
- ni->ni_capinfo = capinfo;
- ni->ni_chan = ic->ic_bsschan;
- ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
- ni->ni_fhindex = ic->ic_bss->ni_fhindex;
- if (wpa != NULL) {
- /*
- * Record WPA parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_wpa_ie, wpa);
- } else if (ni->ni_wpa_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- ni->ni_wpa_ie = NULL;
- }
- if (rsn != NULL) {
- /*
- * Record RSN parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_rsn_ie, rsn);
- } else if (ni->ni_rsn_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- ni->ni_rsn_ie = NULL;
- }
- if (wme != NULL) {
- /*
- * Record WME parameters for station, mark node
- * as capable of QoS and record information
- * element for applications that require it.
- */
- ieee80211_saveie(&ni->ni_wme_ie, wme);
- ni->ni_flags |= IEEE80211_NODE_QOS;
- } else if (ni->ni_wme_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wme_ie, M_80211_NODE);
- ni->ni_wme_ie = NULL;
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
- }
- if (ath != NULL) {
- /*
- * Record ATH parameters for station, mark
- * node with appropriate capabilities, and
- * record the information element for
- * applications that require it.
- */
- ieee80211_saveath(ni, ath);
- } else if (ni->ni_ath_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_ath_ie, M_80211_NODE);
- ni->ni_ath_ie = NULL;
- ni->ni_ath_flags = 0;
- }
- ieee80211_node_join(ic, ni, resp);
- ieee80211_deliver_l2uf(ni);
- break;
+ frm += frm[1] + 2;
}
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
- case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
- uint16_t capinfo, associd;
- uint16_t status;
-
- if (ic->ic_opmode != IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_ASSOC) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- /*
- * asresp frame format
- * [2] capability information
- * [2] status
- * [2] association ID
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] HT capabilities
- * [tlv] HT info
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- ni = ic->ic_bss;
- capinfo = le16toh(*(uint16_t *)frm);
- frm += 2;
- status = le16toh(*(uint16_t *)frm);
- frm += 2;
- if (status != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (reason %d)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "", status);
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++; /* XXX */
- return;
- }
- associd = le16toh(*(uint16_t *)frm);
- frm += 2;
-
- rates = xrates = wme = htcap = htinfo = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswmeoui(frm))
- wme = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (htcap == NULL)
- htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (htinfo == NULL)
- htcap = frm;
- }
- }
- /* XXX Atheros OUI support */
- break;
- }
- frm += frm[1] + 2;
- }
-
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_JOIN |
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (rate set mismatch)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "");
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_assoc_norate++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- return;
- }
-
- ni->ni_capinfo = capinfo;
- ni->ni_associd = associd;
- if (wme != NULL &&
- ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
- ni->ni_flags |= IEEE80211_NODE_QOS;
- ieee80211_wme_updateparams(ic);
- } else
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE,
+ scan->status |= IEEE80211_BPARSE_RATES_INVALID);
+ if (scan->rates != NULL && scan->xrates != NULL) {
/*
- * Setup HT state according to the negotiation.
+ * NB: don't process XRATES if RATES is missing. This
+ * avoids a potential null ptr deref and should be ok
+ * as the return code will already note RATES is missing
+ * (so callers shouldn't otherwise process the frame).
*/
- if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
- htcap != NULL && htinfo != NULL) {
- ieee80211_ht_node_init(ni, htcap);
- ieee80211_parse_htinfo(ni, htinfo);
- ieee80211_setup_htrates(ni,
- htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
- ieee80211_setup_basic_htrates(ni, htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- /*
- * Configure state now that we are associated.
- *
- * XXX may need different/additional driver callbacks?
- */
- if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
- ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
- ic->ic_flags &= ~IEEE80211_F_USEBARKER;
- } else {
- ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
- ic->ic_flags |= IEEE80211_F_USEBARKER;
- }
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ IEEE80211_VERIFY_ELEMENT(scan->xrates,
+ IEEE80211_RATE_MAXSIZE - scan->rates[1],
+ scan->status |= IEEE80211_BPARSE_XRATES_INVALID);
+ }
+ IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN,
+ scan->status |= IEEE80211_BPARSE_SSID_INVALID);
+#if IEEE80211_CHAN_MAX < 255
+ if (scan->chan > IEEE80211_CHAN_MAX) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID,
+ wh, NULL, "invalid channel %u", scan->chan);
+ vap->iv_stats.is_rx_badchan++;
+ scan->status |= IEEE80211_BPARSE_CHAN_INVALID;
+ }
+#endif
+ if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) {
/*
- * Honor ERP protection.
+ * Frame was received on a channel different from the
+ * one indicated in the DS params element id;
+ * silently discard it.
*
- * NB: ni_erp should zero for non-11g operation.
- */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "",
- ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
- ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
- ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
- ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
- ni->ni_flags & IEEE80211_NODE_HT ?
- (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
- ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
- ", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
- ", turbo" : ""
- );
- ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
- break;
+ * NB: this can happen due to signal leakage.
+ * But we should take it for FH phy because
+ * the rssi value should be correct even for
+ * different hop pattern in FH.
+ */
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "for off-channel %u", scan->chan);
+ vap->iv_stats.is_rx_chanmismatch++;
+ scan->status |= IEEE80211_BPARSE_OFFCHAN;
+ }
+ if (!(IEEE80211_BINTVAL_MIN <= scan->bintval &&
+ scan->bintval <= IEEE80211_BINTVAL_MAX)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "bogus beacon interval", scan->bintval);
+ vap->iv_stats.is_rx_badbintval++;
+ scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
+ }
+ if (scan->country != NULL) {
+ /*
+ * Validate we have at least enough data to extract
+ * the country code. Not sure if we should return an
+ * error instead of discarding the IE; consider this
+ * being lenient as we don't depend on the data for
+ * correct operation.
+ */
+ IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t),
+ scan->country = NULL);
}
-
- case IEEE80211_FC0_SUBTYPE_DEAUTH: {
- uint16_t reason;
-
- if (ic->ic_state == IEEE80211_S_SCAN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * deauth frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_deauth++;
- IEEE80211_NODE_STAT(ni, rx_deauth);
-
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
- (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
- break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
- break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- break;
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan->htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htcap[1],
+ scan->htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ scan->htcap = NULL);
}
+ if (scan->htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htinfo[1],
+ scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+ sizeof(struct ieee80211_ie_htinfo)-2,
+ scan->htinfo = NULL);
+ }
+ return scan->status;
+}
- case IEEE80211_FC0_SUBTYPE_DISASSOC: {
- uint16_t reason;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * disassoc frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_disassoc++;
- IEEE80211_NODE_STAT(ni, rx_disassoc);
+/*
+ * Parse an Action frame. Return 0 on success, non-zero on failure.
+ */
+int
+ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_action *ia;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m, u_int8_t *) + m->m_len;
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return EINVAL);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return EINVAL);
break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return EINVAL);
break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return EINVAL);
break;
}
break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ACTION: {
- const struct ieee80211_action *ia;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * action frame format:
- * [1] category
- * [1] action
- * [tlv] parameters
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action), return);
- ia = (const struct ieee80211_action *) frm;
-
- ic->ic_stats.is_rx_action++;
- IEEE80211_NODE_STAT(ni, rx_action);
-
- /* verify frame payloads but defer processing */
- /* XXX maybe push this to method */
- switch (ia->ia_category) {
- case IEEE80211_ACTION_CAT_BA:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbarequest),
- return);
- break;
- case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbaresponse),
- return);
- break;
- case IEEE80211_ACTION_BA_DELBA:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_delba),
- return);
- break;
- }
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return EINVAL);
break;
- case IEEE80211_ACTION_CAT_HT:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ht_txchwidth),
- return);
- break;
- }
+ case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_mimopowersave),
+ return EINVAL);
break;
}
- ic->ic_recv_action(ni, frm, efrm);
break;
}
-
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "mgt", "subtype 0x%x not handled", subtype);
- ic->ic_stats.is_rx_badsubtype++;
- break;
- }
-#undef ISREASSOC
-#undef ISPROBE
-}
-#undef IEEE80211_VERIFY_LENGTH
-#undef IEEE80211_VERIFY_ELEMENT
-
-/*
- * Process a received ps-poll frame.
- */
-static void
-ieee80211_recv_pspoll(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m0)
-{
- struct ieee80211_frame_min *wh;
- struct mbuf *m;
- uint16_t aid;
- int qlen;
-
- wh = mtod(m0, struct ieee80211_frame_min *);
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "%s", "unassociated station");
- ic->ic_stats.is_ps_unassoc++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- aid = le16toh(*(uint16_t *)wh->i_dur);
- if (aid != ni->ni_associd) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "aid mismatch: sta aid 0x%x poll aid 0x%x",
- ni->ni_associd, aid);
- ic->ic_stats.is_ps_badaid++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- /* Okay, take the first queued packet and put it out... */
- IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, but queue empty\n",
- ether_sprintf(wh->i_addr2));
- ieee80211_send_nulldata(ieee80211_ref_node(ni));
- ic->ic_stats.is_ps_qempty++; /* XXX node stat */
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- /*
- * If there are more packets, set the more packets bit
- * in the packet dispatched to the station; otherwise
- * turn off the TIM bit.
- */
- if (qlen != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, %u still queued\n",
- ether_sprintf(ni->ni_macaddr), qlen);
- m->m_flags |= M_MORE_DATA;
- } else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, queue empty\n",
- ether_sprintf(ni->ni_macaddr));
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
- }
- m->m_flags |= M_PWR_SAV; /* bypass PS handling */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+ return 0;
}
#ifdef IEEE80211_DEBUG
/*
* Debugging support.
*/
+void
+ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
+{
+ printf("[%s] discard %s frame, ssid mismatch: ",
+ ether_sprintf(mac), tag);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf("\n");
+}
/*
* Return the bssid of a frame.
*/
static const uint8_t *
-ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
{
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
return wh->i_addr2;
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
return wh->i_addr1;
@@ -3333,8 +831,10 @@ ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
return wh->i_addr3;
}
+#include <machine/stdarg.h>
+
void
-ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
+ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
@@ -3343,11 +843,11 @@ ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */
+ if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */
}
void
-ieee80211_note_frame(struct ieee80211com *ic,
+ieee80211_note_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *fmt, ...)
{
@@ -3357,12 +857,12 @@ ieee80211_note_frame(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n",
+ ether_sprintf(ieee80211_getbssid(vap, wh)), buf);
}
void
-ieee80211_note_mac(struct ieee80211com *ic,
+ieee80211_note_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *fmt, ...)
{
@@ -3372,22 +872,24 @@ ieee80211_note_mac(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
}
void
-ieee80211_discard_frame(struct ieee80211com *ic,
+ieee80211_discard_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
- if (type != NULL)
+ if_printf(vap->iv_ifp, "[%s] discard ",
+ ether_sprintf(ieee80211_getbssid(vap, wh)));
+ if (type == NULL) {
+ printf("%s frame, ", ieee80211_mgt_subtype_name[
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT]);
+ } else
printf("%s frame, ", type);
- else
- printf("frame, ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
@@ -3395,14 +897,14 @@ ieee80211_discard_frame(struct ieee80211com *ic,
}
void
-ieee80211_discard_ie(struct ieee80211com *ic,
+ieee80211_discard_ie(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
+ if_printf(vap->iv_ifp, "[%s] discard ",
+ ether_sprintf(ieee80211_getbssid(vap, wh)));
if (type != NULL)
printf("%s information element, ", type);
else
@@ -3414,13 +916,13 @@ ieee80211_discard_ie(struct ieee80211com *ic,
}
void
-ieee80211_discard_mac(struct ieee80211com *ic,
+ieee80211_discard_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac));
+ if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac));
if (type != NULL)
printf("%s frame, ", type);
else
diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h
new file mode 100644
index 0000000..dd41d7c
--- /dev/null
+++ b/sys/net80211/ieee80211_input.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_INPUT_H_
+#define _NET80211_IEEE80211_INPUT_H_
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen, _action) do { \
+ if ((__elem) == NULL) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "%s", "no " #__elem ); \
+ vap->iv_stats.is_rx_elem_missing++; \
+ _action; \
+ } else if ((__elem)[1] > (__maxlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "bad " #__elem " len %d", (__elem)[1]); \
+ vap->iv_stats.is_rx_elem_toobig++; \
+ _action; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
+ if ((_len) < (_minlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "ie too short, got %d, expected %d", \
+ (_len), (_minlen)); \
+ vap->iv_stats.is_rx_elem_toosmall++; \
+ _action; \
+ } \
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid);
+
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ if (ieee80211_msg_input(vap)) \
+ ieee80211_ssid_mismatch(vap, \
+ ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ wh->i_addr2, _ssid); \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((uint32_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8) | \
+ (((const uint8_t *)(p))[2] << 16) | \
+ (((const uint8_t *)(p))[3] << 24)))
+
+static __inline int
+iswpaoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static __inline int
+iswmeoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static __inline int
+iswmeparam(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static __inline int
+iswmeinfo(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static __inline int
+isatherosoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static __inline int
+ishtcapoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
+}
+
+void ieee80211_deliver_data(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+struct mbuf *ieee80211_defrag(struct ieee80211_node *,
+ struct mbuf *, int);
+struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int);
+struct mbuf *ieee80211_decap1(struct mbuf *, int *);
+struct mbuf *ieee80211_decap_fastframe(struct ieee80211_node *,
+ struct mbuf *);
+int ieee80211_setup_rates(struct ieee80211_node *ni,
+ const uint8_t *rates, const uint8_t *xrates, int flags);
+void ieee80211_send_error(struct ieee80211_node *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg);
+int ieee80211_alloc_challenge(struct ieee80211_node *);
+void ieee80211_parse_ath(struct ieee80211_node *, uint8_t *);
+int ieee80211_parse_beacon(struct ieee80211_node *, struct mbuf *,
+ struct ieee80211_scanparams *);
+int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *);
+#endif /* _NET80211_IEEE80211_INPUT_H_ */
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 83627e8..3b088b1 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,14 +27,13 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_compat.h"
-
/*
* IEEE 802.11 ioctl support (FreeBSD-specific)
*/
#include "opt_inet.h"
#include "opt_ipx.h"
+#include "opt_wlan.h"
#include <sys/endian.h>
#include <sys/param.h>
@@ -61,33 +60,21 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_input.h>
-#define IS_UP(_ic) \
- (((_ic)->ic_ifp->if_flags & IFF_UP) && \
- ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
-#define IS_UP_AUTO(_ic) \
- (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
-#define RESCAN 1
+#define IS_UP_AUTO(_vap) \
+ (IFNET_IS_UP_RUNNING(vap->iv_ifp) && \
+ (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO)
+static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
static struct ieee80211_channel *findchannel(struct ieee80211com *,
int ieee, int mode);
-static int
-cap2cipher(int flag)
-{
- switch (flag) {
- case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP;
- case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB;
- case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM;
- case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP;
- case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP;
- }
- return -1;
-}
-
-static int
-ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
struct ieee80211req_key ik;
struct ieee80211_key *wk;
@@ -102,26 +89,26 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
kid = ik.ik_keyix;
if (kid == IEEE80211_KEYIX_NONE) {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
wk = &ni->ni_ucastkey;
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
- IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+ wk = &vap->iv_nw_keys[kid];
+ IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr);
ni = NULL;
}
cip = wk->wk_cipher;
ik.ik_type = cip->ic_cipher;
ik.ik_keylen = wk->wk_keylen;
ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
- if (wk->wk_keyix == ic->ic_def_txkey)
+ if (wk->wk_keyix == vap->iv_def_txkey)
ik.ik_flags |= IEEE80211_KEY_DEFAULT;
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
/* NB: only root can read key data */
- ik.ik_keyrsc = wk->wk_keyrsc;
+ ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID];
ik.ik_keytsc = wk->wk_keytsc;
memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
@@ -140,18 +127,20 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ik, ireq->i_data, sizeof(ik));
}
-static int
-ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (sizeof(ic->ic_chan_active) < ireq->i_len)
ireq->i_len = sizeof(ic->ic_chan_active);
return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
int space;
space = __offsetof(struct ieee80211req_chaninfo,
@@ -162,8 +151,9 @@ ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ic->ic_nchans, ireq->i_data, space);
}
-static int
-ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
+static __noinline int
+ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
+ struct ieee80211req *ireq, int req)
{
struct ieee80211_node *ni;
struct ieee80211req_wpaie2 wpaie;
@@ -174,34 +164,34 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie.wpa_macaddr);
if (ni == NULL)
- return ENOENT; /* XXX */
+ return ENOENT;
memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
- if (ni->ni_wpa_ie != NULL) {
- int ielen = ni->ni_wpa_ie[1] + 2;
+ if (ni->ni_ies.wpa_ie != NULL) {
+ int ielen = ni->ni_ies.wpa_ie[1] + 2;
if (ielen > sizeof(wpaie.wpa_ie))
ielen = sizeof(wpaie.wpa_ie);
- memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+ memcpy(wpaie.wpa_ie, ni->ni_ies.wpa_ie, ielen);
}
if (req == IEEE80211_IOC_WPAIE2) {
memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
- if (ni->ni_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ni->ni_ies.rsn_ie != NULL) {
+ int ielen = ni->ni_ies.rsn_ie[1] + 2;
if (ielen > sizeof(wpaie.rsn_ie))
ielen = sizeof(wpaie.rsn_ie);
- memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
+ memcpy(wpaie.rsn_ie, ni->ni_ies.rsn_ie, ielen);
}
if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
ireq->i_len = sizeof(struct ieee80211req_wpaie2);
} else {
/* compatibility op, may overwrite wpa ie */
/* XXX check ic_flags? */
- if (ni->ni_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ni->ni_ies.rsn_ie != NULL) {
+ int ielen = ni->ni_ies.rsn_ie[1] + 2;
if (ielen > sizeof(wpaie.wpa_ie))
ielen = sizeof(wpaie.wpa_ie);
- memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
+ memcpy(wpaie.wpa_ie, ni->ni_ies.rsn_ie, ielen);
}
if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
ireq->i_len = sizeof(struct ieee80211req_wpaie);
@@ -210,8 +200,8 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
return copyout(&wpaie, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -223,9 +213,9 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
ireq->i_len = sizeof(struct ieee80211req_sta_stats);
/* NB: copy out only the statistics */
@@ -235,149 +225,6 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
}
-static __inline uint8_t *
-copyie(uint8_t *cp, const uint8_t *ie)
-{
- if (ie != NULL) {
- memcpy(cp, ie, 2+ie[1]);
- cp += 2+ie[1];
- }
- return cp;
-}
-
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_SCAN_RESULTS_OLD 24
-
-struct scan_result_old {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_freq; /* MHz */
- uint16_t isr_flags; /* channel flags */
- uint8_t isr_noise;
- uint8_t isr_rssi;
- uint8_t isr_intval; /* beacon interval */
- uint8_t isr_capinfo; /* capabilities */
- uint8_t isr_erp; /* ERP element */
- uint8_t isr_bssid[IEEE80211_ADDR_LEN];
- uint8_t isr_nrates;
- uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isr_ssid_len; /* SSID length */
- uint8_t isr_ie_len; /* IE length */
- uint8_t isr_pad[5];
- /* variable length SSID followed by IE data */
-};
-
-struct oscanreq {
- struct scan_result_old *sr;
- size_t space;
-};
-
-static size_t
-old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
-{
- size_t len;
-
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- /*
- * NB: ie's can be no more than 255 bytes and the max 802.11
- * packet is <3Kbytes so we are sure this doesn't overflow
- * 16-bits; if this is a concern we can drop the ie's.
- */
- len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
- return roundup(len, sizeof(uint32_t));
-}
-
-static void
-old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- int ielen;
-
- req->space += old_scan_space(se, &ielen);
-}
-
-static void
-old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- struct scan_result_old *sr;
- int ielen, len, nr, nxr;
- uint8_t *cp;
-
- len = old_scan_space(se, &ielen);
- if (len > req->space)
- return;
-
- sr = req->sr;
- memset(sr, 0, sizeof(*sr));
- sr->isr_ssid_len = se->se_ssid[1];
- /* NB: beware of overflow, isr_ie_len is 8 bits */
- sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
- sr->isr_len = len;
- sr->isr_freq = se->se_chan->ic_freq;
- sr->isr_flags = se->se_chan->ic_flags;
- sr->isr_rssi = se->se_rssi;
- sr->isr_noise = se->se_noise;
- sr->isr_intval = se->se_intval;
- sr->isr_capinfo = se->se_capinfo;
- sr->isr_erp = se->se_erp;
- IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
- nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
- memcpy(sr->isr_rates, se->se_rates+2, nr);
- nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
- memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
- sr->isr_nrates = nr + nxr;
-
- cp = (uint8_t *)(sr+1);
- memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
- cp += sr->isr_ssid_len;
- if (sr->isr_ie_len) {
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_wme_ie);
- }
-
- req->space -= len;
- req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
-}
-
-static int
-old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- struct oscanreq req;
- int error;
-
- if (ireq->i_len < sizeof(struct scan_result_old))
- return EFAULT;
-
- error = 0;
- req.space = 0;
- ieee80211_scan_iterate(ic, old_get_scan_space, &req);
- if (req.space > ireq->i_len)
- req.space = ireq->i_len;
- if (req.space > 0) {
- size_t space;
- void *p;
-
- space = req.space;
- /* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
- if (p == NULL)
- return ENOMEM;
- req.sr = p;
- ieee80211_scan_iterate(ic, old_get_scan_result, &req);
- ireq->i_len = space - req.space;
- error = copyout(p, ireq->i_data, ireq->i_len);
- FREE(p, M_TEMP);
- } else
- ireq->i_len = 0;
-
- return error;
-}
-#endif /* COMPAT_FREEBSD6 */
-
struct scanreq {
struct ieee80211req_scan_result *sr;
size_t space;
@@ -388,15 +235,7 @@ scan_space(const struct ieee80211_scan_entry *se, int *ielen)
{
size_t len;
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_rsn_ie != NULL)
- *ielen += 2+se->se_rsn_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- if (se->se_ath_ie != NULL)
- *ielen += 2+se->se_ath_ie[1];
+ *ielen = se->se_ies.len;
/*
* NB: ie's can be no more than 255 bytes and the max 802.11
* packet is <3Kbytes so we are sure this doesn't overflow
@@ -415,7 +254,7 @@ get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
req->space += scan_space(se, &ielen);
}
-static void
+static __noinline void
get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
{
struct scanreq *req = arg;
@@ -430,9 +269,9 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
sr = req->sr;
KASSERT(len <= 65535 && ielen <= 65535,
("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
+ sr->isr_len = len;
sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ielen;
- sr->isr_len = len;
sr->isr_freq = se->se_chan->ic_freq;
sr->isr_flags = se->se_chan->ic_flags;
sr->isr_rssi = se->se_rssi;
@@ -453,29 +292,26 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
if (ielen) {
cp += sr->isr_ssid_len;
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_rsn_ie);
- cp = copyie(cp, se->se_wme_ie);
- cp = copyie(cp, se->se_ath_ie);
- cp = copyie(cp, se->se_htcap_ie);
+ memcpy(cp, se->se_ies.data, ielen);
}
req->space -= len;
req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
}
-static int
-ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
+ struct ieee80211req *ireq)
{
struct scanreq req;
int error;
- if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
+ if (ireq->i_len < sizeof(struct scanreq))
return EFAULT;
error = 0;
req.space = 0;
- ieee80211_scan_iterate(ic, get_scan_space, &req);
+ ieee80211_scan_iterate(vap, get_scan_space, &req);
if (req.space > ireq->i_len)
req.space = ireq->i_len;
if (req.space > 0) {
@@ -488,7 +324,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
if (p == NULL)
return ENOMEM;
req.sr = p;
- ieee80211_scan_iterate(ic, get_scan_result, &req);
+ ieee80211_scan_iterate(vap, get_scan_result, &req);
ireq->i_len = space - req.space;
error = copyout(p, ireq->i_data, ireq->i_len);
FREE(p, M_TEMP);
@@ -499,7 +335,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
}
struct stainforeq {
- struct ieee80211com *ic;
+ struct ieee80211vap *vap;
struct ieee80211req_sta_info *si;
size_t space;
};
@@ -507,15 +343,7 @@ struct stainforeq {
static size_t
sta_space(const struct ieee80211_node *ni, size_t *ielen)
{
- *ielen = 0;
- if (ni->ni_wpa_ie != NULL)
- *ielen += 2+ni->ni_wpa_ie[1];
- if (ni->ni_rsn_ie != NULL)
- *ielen += 2+ni->ni_rsn_ie[1];
- if (ni->ni_wme_ie != NULL)
- *ielen += 2+ni->ni_wme_ie[1];
- if (ni->ni_ath_ie != NULL)
- *ielen += 2+ni->ni_ath_ie[1];
+ *ielen = ni->ni_ies.len;
return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
sizeof(uint32_t));
}
@@ -524,25 +352,28 @@ static void
get_sta_space(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
size_t ielen;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if (req->vap != ni->ni_vap)
+ return;
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
return;
req->space += sta_space(ni, &ielen);
}
-static void
+static __noinline void
get_sta_info(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211req_sta_info *si;
size_t ielen, len;
uint8_t *cp;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if (req->vap != ni->ni_vap)
+ return;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
return;
if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */
@@ -558,8 +389,8 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_flags = ni->ni_chan->ic_flags;
si->isi_state = ni->ni_flags;
si->isi_authmode = ni->ni_authmode;
- ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
- si->isi_noise = 0; /* XXX */
+ vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
+ vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo);
si->isi_capinfo = ni->ni_capinfo;
si->isi_erp = ni->ni_erp;
IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
@@ -568,7 +399,22 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_nrates = 15;
memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
si->isi_txrate = ni->ni_txrate;
- si->isi_ie_len = ielen;
+ if (si->isi_txrate & IEEE80211_RATE_MCS) {
+ const struct ieee80211_mcs_rates *mcs =
+ &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS];
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+ si->isi_txmbps = mcs->ht40_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht40_rate_400ns;
+ } else {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+ si->isi_txmbps = mcs->ht20_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht20_rate_400ns;
+ }
+ } else
+ si->isi_txmbps = si->isi_txrate;
si->isi_associd = ni->ni_associd;
si->isi_txpower = ni->ni_txpower;
si->isi_vlan = ni->ni_vlan;
@@ -581,29 +427,29 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
}
/* NB: leave all cases in case we relax ni_associd == 0 check */
if (ieee80211_node_is_authorized(ni))
- si->isi_inact = ic->ic_inact_run;
- else if (ni->ni_associd != 0)
- si->isi_inact = ic->ic_inact_auth;
+ si->isi_inact = vap->iv_inact_run;
+ else if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ si->isi_inact = vap->iv_inact_auth;
else
- si->isi_inact = ic->ic_inact_init;
+ si->isi_inact = vap->iv_inact_init;
si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
if (ielen) {
cp = ((uint8_t *)si) + si->isi_ie_off;
- cp = copyie(cp, ni->ni_wpa_ie);
- cp = copyie(cp, ni->ni_rsn_ie);
- cp = copyie(cp, ni->ni_wme_ie);
- cp = copyie(cp, ni->ni_ath_ie);
+ memcpy(cp, ni->ni_ies.data, ielen);
}
req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
req->space -= len;
}
-static int
-getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
+static __noinline int
+getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
struct ieee80211_node *ni, int off)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct stainforeq req;
size_t space;
void *p;
@@ -611,6 +457,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
error = 0;
req.space = 0;
+ req.vap = vap;
if (ni == NULL)
ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
else
@@ -620,7 +467,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
if (req.space > 0) {
space = req.space;
/* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
+ MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
if (p == NULL) {
error = ENOMEM;
goto bad;
@@ -641,8 +488,8 @@ bad:
return error;
}
-static int
-ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t macaddr[IEEE80211_ADDR_LEN];
const int off = __offsetof(struct ieee80211req_sta_req, info);
@@ -654,30 +501,18 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
+ if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) {
ni = NULL;
} else {
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
}
- return getstainfo_common(ic, ireq, ni, off);
+ return getstainfo_common(vap, ireq, ni, off);
}
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_STA_INFO_OLD 45
-
-static int
-old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
- return EFAULT;
- return getstainfo_common(ic, ireq, NULL, 0);
-}
-#endif /* COMPAT_FREEBSD6 */
-
-static int
-ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -688,18 +523,19 @@ ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
txpow.it_txpow = ni->ni_txpower;
error = copyout(&txpow, ireq->i_data, sizeof(txpow));
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep;
int ac;
@@ -739,12 +575,12 @@ ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0;
}
-static int
-ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
- return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
+ return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq));
}
/*
@@ -753,20 +589,151 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
* setting. Otherwise report the current setting.
*/
static int
-getathcap(struct ieee80211com *ic, int cap)
+getathcap(struct ieee80211vap *vap, int cap)
{
- if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
- return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN)
+ return IEEE80211_ATH_CAP(vap, vap->iv_bss, cap) != 0;
else
- return (ic->ic_flags & cap) != 0;
+ return (vap->iv_flags & cap) != 0;
}
-static int
-ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+
if (ireq->i_len != sizeof(struct ieee80211_channel))
return EINVAL;
- return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
+ /*
+ * vap's may have different operating channels when HT is
+ * in use. When in RUN state report the vap-specific channel.
+ * Otherwise return curchan.
+ */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ c = vap->iv_bss->ni_chan;
+ else
+ c = ic->ic_curchan;
+ return copyout(c, ireq->i_data, sizeof(*c));
+}
+
+static int
+getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq)
+{
+ if (aie == NULL)
+ return EINVAL;
+ /* NB: truncate, caller can check length */
+ if (ireq->i_len > aie->ie_len)
+ ireq->i_len = aie->ie_len;
+ return copyout(aie->ie_data, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return getappie(vap->iv_appie_beacon, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_proberesp, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ return getappie(vap->iv_appie_assocresp, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return getappie(vap->iv_appie_probereq, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ return getappie(vap->iv_appie_assocreq, ireq);
+ case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_wpa, ireq);
+ }
+ return EINVAL;
+}
+
+static __noinline int
+ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (ireq->i_len != sizeof(ic->ic_regdomain))
+ return EINVAL;
+ return copyout(&ic->ic_regdomain, ireq->i_data,
+ sizeof(ic->ic_regdomain));
+}
+
+static __noinline int
+ieee80211_ioctl_getroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ return copyout(vap->iv_roamparms, ireq->i_data,
+ sizeof(vap->iv_roamparms));
+}
+
+static __noinline int
+ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_txparms))
+ return EINVAL;
+ return copyout(vap->iv_txparms, ireq->i_data, sizeof(vap->iv_txparms));
+}
+
+static __noinline int
+ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_devcaps_req *dc;
+ struct ieee80211req_chaninfo *ci;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_devcaps_req))
+ return EINVAL;
+ MALLOC(dc, struct ieee80211_devcaps_req *,
+ sizeof(struct ieee80211_devcaps_req), M_TEMP, M_NOWAIT | M_ZERO);
+ if (dc == NULL)
+ return ENOMEM;
+ dc->dc_drivercaps = ic->ic_caps;
+ dc->dc_cryptocaps = ic->ic_cryptocaps;
+ dc->dc_htcaps = ic->ic_htcaps;
+ ci = &dc->dc_chaninfo;
+ ic->ic_getradiocaps(ic, &ci->ic_nchans, ci->ic_chans);
+ ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans);
+ error = copyout(dc, ireq->i_data, sizeof(*dc));
+ FREE(dc, M_TEMP);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ vlan.sv_vlan = ni->ni_vlan;
+ error = copyout(&vlan, ireq->i_data, sizeof(vlan));
+ ieee80211_free_node(ni);
+ return error;
}
/*
@@ -785,30 +752,28 @@ ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
* but special-casing the compilation of this one module in the
* build system would be awkward.
*/
-#ifdef __GNUC__
-__attribute__ ((noinline))
-#endif
-static int
-ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
+ struct ieee80211req *ireq)
{
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- int error = 0;
- u_int kid, len, m;
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211com *ic = vap->iv_ic;
+ u_int kid, len;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
+ int error = 0;
switch (ireq->i_type) {
case IEEE80211_IOC_SSID:
- switch (ic->ic_state) {
+ switch (vap->iv_state) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- ireq->i_len = ic->ic_des_ssid[0].len;
- memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
+ ireq->i_len = vap->iv_des_ssid[0].len;
+ memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len);
break;
default:
- ireq->i_len = ic->ic_bss->ni_esslen;
- memcpy(tmpssid, ic->ic_bss->ni_essid,
- ireq->i_len);
+ ireq->i_len = vap->iv_bss->ni_esslen;
+ memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len);
break;
}
error = copyout(tmpssid, ireq->i_data, ireq->i_len);
@@ -817,9 +782,9 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = 1;
break;
case IEEE80211_IOC_WEP:
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
ireq->i_val = IEEE80211_WEP_OFF;
- else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ else if (vap->iv_flags & IEEE80211_F_DROPUNENC)
ireq->i_val = IEEE80211_WEP_ON;
else
ireq->i_val = IEEE80211_WEP_MIXED;
@@ -828,10 +793,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+ len = (u_int) vap->iv_nw_keys[kid].wk_keylen;
/* NB: only root can read WEP keys */
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
- bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len);
} else {
bzero(tmpkey, len);
}
@@ -842,19 +807,19 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = IEEE80211_WEP_NKID;
break;
case IEEE80211_IOC_WEPTXKEY:
- ireq->i_val = ic->ic_def_txkey;
+ ireq->i_val = vap->iv_def_txkey;
break;
case IEEE80211_IOC_AUTHMODE:
- if (ic->ic_flags & IEEE80211_F_WPA)
+ if (vap->iv_flags & IEEE80211_F_WPA)
ireq->i_val = IEEE80211_AUTH_WPA;
else
- ireq->i_val = ic->ic_bss->ni_authmode;
+ ireq->i_val = vap->iv_bss->ni_authmode;
break;
case IEEE80211_IOC_CHANNEL:
ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
break;
case IEEE80211_IOC_POWERSAVE:
- if (ic->ic_flags & IEEE80211_F_PMGTON)
+ if (vap->iv_flags & IEEE80211_F_PMGTON)
ireq->i_val = IEEE80211_POWERSAVE_ON;
else
ireq->i_val = IEEE80211_POWERSAVE_OFF;
@@ -863,42 +828,25 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = ic->ic_lintval;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
- ireq->i_val = ic->ic_rtsthreshold;
+ ireq->i_val = vap->iv_rtsthreshold;
break;
case IEEE80211_IOC_PROTMODE:
ireq->i_val = ic->ic_protmode;
break;
case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
- ireq->i_val = ic->ic_txpowlimit;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- ireq->i_val = rsn->rsn_mcastcipher;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- ireq->i_val = rsn->rsn_mcastkeylen;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- ireq->i_val = 0;
- for (m = 0x1; m != 0; m <<= 1)
- if (rsn->rsn_ucastcipherset & m)
- ireq->i_val |= 1<<cap2cipher(m);
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- ireq->i_val = rsn->rsn_ucastcipher;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- ireq->i_val = rsn->rsn_ucastkeylen;
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- ireq->i_val = rsn->rsn_keymgmtset;
- break;
- case IEEE80211_IOC_RSNCAPS:
- ireq->i_val = rsn->rsn_caps;
+ /*
+ * Tx power limit is the min of max regulatory
+ * power, any user-set limit, and the max the
+ * radio can do.
+ */
+ ireq->i_val = 2*ic->ic_curchan->ic_maxregpower;
+ if (ireq->i_val > ic->ic_txpowlimit)
+ ireq->i_val = ic->ic_txpowlimit;
+ if (ireq->i_val > ic->ic_curchan->ic_maxpower)
+ ireq->i_val = ic->ic_curchan->ic_maxpower;
break;
case IEEE80211_IOC_WPA:
- switch (ic->ic_flags & IEEE80211_F_WPA) {
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
case IEEE80211_F_WPA1:
ireq->i_val = 1;
break;
@@ -914,85 +862,63 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_getchanlist(ic, ireq);
+ error = ieee80211_ioctl_getchanlist(vap, ireq);
break;
case IEEE80211_IOC_ROAMING:
- ireq->i_val = ic->ic_roaming;
+ ireq->i_val = vap->iv_roaming;
break;
case IEEE80211_IOC_PRIVACY:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0;
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0;
break;
case IEEE80211_IOC_COUNTERMEASURES:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- ireq->i_val = ic->ic_caps>>16;
- ireq->i_len = ic->ic_caps&0xffff;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0;
break;
case IEEE80211_IOC_WME:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0;
break;
case IEEE80211_IOC_HIDESSID:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0;
break;
case IEEE80211_IOC_APBRIDGE:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
- break;
- case IEEE80211_IOC_OPTIE:
- if (ic->ic_opt_ie == NULL)
- return EINVAL;
- /* NB: truncate, caller can check length */
- if (ireq->i_len > ic->ic_opt_ie_len)
- ireq->i_len = ic->ic_opt_ie_len;
- error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0;
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_getkey(ic, ireq);
+ error = ieee80211_ioctl_getkey(vap, ireq);
break;
case IEEE80211_IOC_CHANINFO:
- error = ieee80211_ioctl_getchaninfo(ic, ireq);
+ error = ieee80211_ioctl_getchaninfo(vap, ireq);
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != IEEE80211_ADDR_LEN)
return EINVAL;
- error = copyout(ic->ic_state == IEEE80211_S_RUN ?
- ic->ic_bss->ni_bssid :
- ic->ic_des_bssid,
+ error = copyout(vap->iv_state == IEEE80211_S_RUN ?
+ vap->iv_bss->ni_bssid :
+ vap->iv_des_bssid,
ireq->i_data, ireq->i_len);
break;
case IEEE80211_IOC_WPAIE:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
case IEEE80211_IOC_WPAIE2:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_SCAN_RESULTS_OLD:
- error = old_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
-#endif
case IEEE80211_IOC_SCAN_RESULTS:
- error = ieee80211_ioctl_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getscanresults(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_getstastats(ic, ireq);
+ error = ieee80211_ioctl_getstastats(vap, ireq);
break;
case IEEE80211_IOC_TXPOWMAX:
- ireq->i_val = ic->ic_bss->ni_txpower;
+ ireq->i_val = vap->iv_bss->ni_txpower;
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_getstatxpow(ic, ireq);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_STA_INFO_OLD:
- error = old_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstatxpow(vap, ireq);
break;
-#endif
case IEEE80211_IOC_STA_INFO:
- error = ieee80211_ioctl_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstainfo(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -1000,180 +926,163 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_getwmeparam(ic, ireq);
+ error = ieee80211_ioctl_getwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- ireq->i_val = ic->ic_dtim_period;
+ ireq->i_val = vap->iv_dtim_period;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
/* NB: get from ic_bss for station mode */
- ireq->i_val = ic->ic_bss->ni_intval;
+ ireq->i_val = vap->iv_bss->ni_intval;
break;
case IEEE80211_IOC_PUREG:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
break;
case IEEE80211_IOC_FF:
- ireq->i_val = getathcap(ic, IEEE80211_F_FF);
+ ireq->i_val = getathcap(vap, IEEE80211_F_FF);
break;
case IEEE80211_IOC_TURBOP:
- ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
+ ireq->i_val = getathcap(vap, IEEE80211_F_TURBOP);
break;
case IEEE80211_IOC_BGSCAN:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
- ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */
+ ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
- ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */
+ ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */
break;
case IEEE80211_IOC_SCANVALID:
- ireq->i_val = ic->ic_scanvalid/hz; /* seconds */
- break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ireq->i_val = ic->ic_roam.rssi11a;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ireq->i_val = ic->ic_roam.rssi11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ireq->i_val = ic->ic_roam.rssi11b;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ireq->i_val = ic->ic_roam.rate11a;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ireq->i_val = ic->ic_roam.rate11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ireq->i_val = ic->ic_roam.rate11b;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ireq->i_val = ic->ic_mcast_rate;
+ ireq->i_val = vap->iv_scanvalid/hz; /* seconds */
break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- ireq->i_val = ic->ic_fragthreshold;
+ ireq->i_val = vap->iv_fragthreshold;
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_getmaccmd(ic, ireq);
+ error = ieee80211_ioctl_getmaccmd(vap, ireq);
break;
case IEEE80211_IOC_BURST:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
- ireq->i_val = ic->ic_bmissthreshold;
+ ireq->i_val = vap->iv_bmissthreshold;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_getcurchan(ic, ireq);
+ error = ieee80211_ioctl_getcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
break;
case IEEE80211_IOC_AMPDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ireq->i_val = vap->iv_ampdu_rxmax;
+ else if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MAXRXAMPDU);
+ else
+ ireq->i_val = vap->iv_ampdu_limit;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- ireq->i_val = ic->ic_ampdu_density;
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MPDUDENSITY);
+ else
+ ireq->i_val = vap->iv_ampdu_density;
break;
case IEEE80211_IOC_AMSDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
- ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */
+ ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) != 0;
break;
case IEEE80211_IOC_DOTH:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_getregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_getroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_gettxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ break;
+ case IEEE80211_IOC_DWDS:
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0;
break;
case IEEE80211_IOC_INACTIVITY:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_getappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0;
+ break;
+ case IEEE80211_IOC_TSN:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0;
+ break;
+ case IEEE80211_IOC_DFS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0;
+ break;
+ case IEEE80211_IOC_DOTD:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0;
+ break;
+ case IEEE80211_IOC_DEVCAPS:
+ error = ieee80211_ioctl_getdevcaps(ic, ireq);
break;
case IEEE80211_IOC_HTPROTMODE:
ireq->i_val = ic->ic_htprotmode;
break;
case IEEE80211_IOC_HTCONF:
- if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_HT) {
ireq->i_val = 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
ireq->i_val |= 2;
} else
ireq->i_val = 0;
break;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_getstavlan(vap, ireq);
+ break;
default:
error = EINVAL;
break;
}
return error;
+#undef MS
}
-static int
-ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- int error;
- void *ie, *oie;
-
- /*
- * NB: Doing this for ap operation could be useful (e.g. for
- * WPA and/or WME) except that it typically is worthless
- * without being able to intervene when processing
- * association response frames--so disallow it for now.
- */
- if (ic->ic_opmode != IEEE80211_M_STA)
- return EINVAL;
- if (ireq->i_len > IEEE80211_MAX_OPT_IE)
- return EINVAL;
- /* NB: data.length is validated by the wireless extensions code */
- /* XXX M_WAITOK after driver lock released */
- if (ireq->i_len > 0) {
- MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
- if (ie == NULL)
- return ENOMEM;
- error = copyin(ireq->i_data, ie, ireq->i_len);
- if (error) {
- FREE(ie, M_DEVBUF);
- return error;
- }
- } else {
- ie = NULL;
- ireq->i_len = 0;
- }
- /* XXX sanity check data? */
- oie = ic->ic_opt_ie;
- ic->ic_opt_ie = ie;
- ic->ic_opt_ie_len = ireq->i_len;
- if (oie != NULL)
- FREE(oie, M_DEVBUF);
- return 0;
-}
-
-static int
-ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_key ik;
struct ieee80211_node *ni;
struct ieee80211_key *wk;
uint16_t kid;
- int error;
+ int error, i;
if (ireq->i_len != sizeof(ik))
return EINVAL;
@@ -1189,14 +1098,15 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
/* XXX unicast keys currently must be tx/rx */
if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
return EINVAL;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ ik.ik_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1204,7 +1114,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
+ wk = &vap->iv_nw_keys[kid];
/*
* Global slots start off w/o any assigned key index.
* Force one here for consistency with IEEE80211_IOC_WEPKEY.
@@ -1214,31 +1124,32 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
ni = NULL;
}
error = 0;
- ieee80211_key_update_begin(ic);
- if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
+ ieee80211_key_update_begin(vap);
+ if (ieee80211_crypto_newkey(vap, ik.ik_type, ik.ik_flags, wk)) {
wk->wk_keylen = ik.ik_keylen;
/* NB: MIC presence is implied by cipher type */
if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
- wk->wk_keyrsc = ik.ik_keyrsc;
+ for (i = 0; i < IEEE80211_TID_SIZE; i++)
+ wk->wk_keyrsc[i] = ik.ik_keyrsc;
wk->wk_keytsc = 0; /* new key, reset */
memset(wk->wk_key, 0, sizeof(wk->wk_key));
memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
- if (!ieee80211_crypto_setkey(ic, wk,
+ if (!ieee80211_crypto_setkey(vap, wk,
ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
error = EIO;
else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
} else
error = ENXIO;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
if (ni != NULL)
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_del_key dk;
int kid, error;
@@ -1253,14 +1164,15 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
struct ieee80211_node *ni;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ dk.idk_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1271,25 +1183,216 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
/* XXX error return */
- ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+ ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]);
}
return 0;
}
+struct mlmeop {
+ struct ieee80211vap *vap;
+ int op;
+ int reason;
+};
+
+static void
+mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int op, int reason)
+{
+#ifdef IEEE80211_DEBUG
+ static const struct {
+ int mask;
+ const char *opstr;
+ } ops[] = {
+ { 0, "op#0" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "assoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "disassoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "deauth" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "authorize" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "unauthorize" },
+ };
+
+ if (op == IEEE80211_MLME_AUTH) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL |
+ IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac,
+ "station authenticate %s via MLME (reason %d)",
+ reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT",
+ reason);
+ } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac,
+ "unknown MLME request %d (reason %d)", op, reason);
+ } else if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME", ops[op].opstr);
+ } else {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME (reason %d)", ops[op].opstr, reason);
+ }
+#endif /* IEEE80211_DEBUG */
+}
+
static void
domlme(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211req_mlme *mlme = arg;
+ struct mlmeop *mop = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap != mop->vap)
+ return;
+ /*
+ * NB: if ni_associd is zero then the node is already cleaned
+ * up and we don't need to do this (we're safely holding a
+ * reference but should otherwise not modify it's state).
+ */
+ if (ni->ni_associd == 0)
+ return;
+ mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason);
+ if (mop->op == IEEE80211_MLME_DEAUTH) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ mop->reason);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ mop->reason);
+ }
+ ieee80211_node_leave(ni);
+}
+
+static int
+setmlme_dropsta(struct ieee80211vap *vap,
+ const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ int error = 0;
+
+ /* NB: the broadcast address means do 'em all */
+ if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) {
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, mac);
+ if (ni != NULL) {
+ domlme(mlmeop, ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ } else {
+ ieee80211_iterate_nodes(nt, domlme, mlmeop);
+ }
+ return error;
+}
+
+static __noinline int
+setmlme_common(struct ieee80211vap *vap, int op,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int reason)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ struct mlmeop mlmeop;
+ int error;
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- mlme->im_op == IEEE80211_MLME_DEAUTH ?
- IEEE80211_FC0_SUBTYPE_DEAUTH :
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- mlme->im_reason);
+ error = 0;
+ switch (op) {
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ /* XXX not quite right */
+ ieee80211_new_state(vap, IEEE80211_S_INIT, reason);
+ break;
+ case IEEE80211_M_HOSTAP:
+ mlmeop.vap = vap;
+ mlmeop.op = op;
+ mlmeop.reason = reason;
+ error = setmlme_dropsta(vap, mac, &mlmeop);
+ break;
+ case IEEE80211_M_WDS:
+ /* XXX user app should send raw frame? */
+ if (op != IEEE80211_MLME_DEAUTH) {
+ error = EINVAL;
+ break;
+ }
+#if 0
+ /* XXX accept any address, simplifies user code */
+ if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) {
+ error = EINVAL;
+ break;
+ }
+#endif
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case IEEE80211_MLME_AUTHORIZE:
+ case IEEE80211_MLME_UNAUTHORIZE:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_WDS) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (op == IEEE80211_MLME_AUTHORIZE)
+ ieee80211_node_authorize(ni);
+ else
+ ieee80211_node_unauthorize(ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ case IEEE80211_MLME_AUTH:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ /*
+ * For shared key auth, just continue the
+ * exchange. Otherwise when 802.1x is not in
+ * use mark the port authorized at this point
+ * so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X &&
+ ni->ni_challenge == NULL)
+ ieee80211_node_authorize(ni);
+ } else {
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, ni->ni_macaddr,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16));
+ ieee80211_node_leave(ni);
+ }
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- ieee80211_node_leave(ic, ni);
+ return error;
}
struct scanlookup {
@@ -1318,11 +1421,34 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
look->se = se;
}
-static int
-ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int ssid_len, const uint8_t ssid[IEEE80211_NWID_LEN])
+{
+ struct scanlookup lookup;
+
+ /* XXX ibss/ahdemo */
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+
+ /* NB: this is racey if roaming is !manual */
+ lookup.se = NULL;
+ lookup.mac = mac;
+ lookup.esslen = ssid_len;
+ lookup.essid = ssid;
+ ieee80211_scan_iterate(vap, mlmelookup, &lookup);
+ if (lookup.se == NULL)
+ return ENOENT;
+ mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0);
+ if (!ieee80211_sta_join(vap, lookup.se))
+ return EIO; /* XXX unique but could be better */
+ return 0;
+}
+
+static __noinline int
+ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_mlme mlme;
- struct ieee80211_node *ni;
int error;
if (ireq->i_len != sizeof(mlme))
@@ -1330,72 +1456,19 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &mlme, sizeof(mlme));
if (error)
return error;
- switch (mlme.im_op) {
- case IEEE80211_MLME_ASSOC:
- /* XXX ibss/ahdemo */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- struct scanlookup lookup;
-
- lookup.se = NULL;
- lookup.mac = mlme.im_macaddr;
- /* XXX use revised api w/ explicit ssid */
- lookup.esslen = ic->ic_des_ssid[0].len;
- lookup.essid = ic->ic_des_ssid[0].ssid;
- ieee80211_scan_iterate(ic, mlmelookup, &lookup);
- if (lookup.se != NULL &&
- ieee80211_sta_join(ic, lookup.se))
- return 0;
- }
- return EINVAL;
- case IEEE80211_MLME_DISASSOC:
- case IEEE80211_MLME_DEAUTH:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- /* XXX not quite right */
- ieee80211_new_state(ic, IEEE80211_S_INIT,
- mlme.im_reason);
- break;
- case IEEE80211_M_HOSTAP:
- /* NB: the broadcast address means do 'em all */
- if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
- if ((ni = ieee80211_find_node(&ic->ic_sta,
- mlme.im_macaddr)) == NULL)
- return EINVAL;
- domlme(&mlme, ni);
- ieee80211_free_node(ni);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- domlme, &mlme);
- }
- break;
- default:
- return EINVAL;
- }
- break;
- case IEEE80211_MLME_AUTHORIZE:
- case IEEE80211_MLME_UNAUTHORIZE:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return EINVAL;
- ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
- if (ni == NULL)
- return EINVAL;
- if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
- ieee80211_node_authorize(ni);
- else
- ieee80211_node_unauthorize(ni);
- ieee80211_free_node(ni);
- break;
- default:
- return EINVAL;
- }
- return 0;
+ if (mlme.im_op == IEEE80211_MLME_ASSOC)
+ return setmlme_assoc(vap, mlme.im_macaddr,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ else
+ return setmlme_common(vap, mlme.im_op,
+ mlme.im_macaddr, mlme.im_reason);
}
-static int
-ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t mac[IEEE80211_ADDR_LEN];
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
int error;
if (ireq->i_len != sizeof(mac))
@@ -1405,57 +1478,59 @@ ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
if (ireq->i_type == IEEE80211_IOC_ADDMAC)
- acl->iac_add(ic, mac);
+ acl->iac_add(vap, mac);
else
- acl->iac_remove(ic, mac);
+ acl->iac_remove(vap, mac);
return 0;
}
-static int
-ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
switch (ireq->i_val) {
case IEEE80211_MACCMD_POLICY_OPEN:
case IEEE80211_MACCMD_POLICY_ALLOW:
case IEEE80211_MACCMD_POLICY_DENY:
+ case IEEE80211_MACCMD_POLICY_RADIUS:
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
- acl->iac_setpolicy(ic, ireq->i_val);
+ acl->iac_setpolicy(vap, ireq->i_val);
break;
case IEEE80211_MACCMD_FLUSH:
if (acl != NULL)
- acl->iac_flush(ic);
+ acl->iac_flush(vap);
/* NB: silently ignore when not in use */
break;
case IEEE80211_MACCMD_DETACH:
if (acl != NULL) {
- ic->ic_acl = NULL;
- acl->iac_detach(ic);
+ vap->iv_acl = NULL;
+ acl->iac_detach(vap);
}
break;
default:
if (acl == NULL)
return EINVAL;
else
- return acl->iac_setioctl(ic, ireq);
+ return acl->iac_setioctl(vap, ireq);
}
return 0;
}
-static int
-ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211req_chanlist list;
u_char chanlist[IEEE80211_CHAN_BYTES];
int i, j, nchan, error;
@@ -1491,11 +1566,12 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
isclr(chanlist, ic->ic_bsschan->ic_ieee))
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
- return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
+ ieee80211_scan_flush(vap);
+ return ENETRESET;
}
-static int
-ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -1511,16 +1587,17 @@ ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
+ /* XXX require ni_vap == vap? */
memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
ieee80211_free_node(ni);
return 0;
}
-static int
-ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -1531,23 +1608,24 @@ ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
ni->ni_txpower = txpow.it_txpow;
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep, *chanp;
int isbss, ac;
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
@@ -1610,20 +1688,7 @@ ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
(ireq->i_val) == 0;
break;
}
- ieee80211_wme_updateparams(ic);
- return 0;
-}
-
-static int
-cipher2cap(int cipher)
-{
- switch (cipher) {
- case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP;
- case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES;
- case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM;
- case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP;
- case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP;
- }
+ ieee80211_wme_updateparams(vap);
return 0;
}
@@ -1666,10 +1731,7 @@ findchannel(struct ieee80211com *ic, int ieee, int mode)
u_int modeflags;
int i;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
modeflags = chanflags[mode];
- KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
- ("no chanflags for mode %u", mode));
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -1735,44 +1797,56 @@ check_mode_consistency(const struct ieee80211_channel *c, int mode)
* change or a kick of the state machine.
*/
static int
-setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
if (c != IEEE80211_CHAN_ANYC) {
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- !check_mode_consistency(c, ic->ic_des_mode))
- return EINVAL;
- if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ return EBUSY; /* XXX better code? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (IEEE80211_IS_CHAN_NOHOSTAP(c))
+ return EINVAL;
+ if (!check_mode_consistency(c, vap->iv_des_mode))
+ return EINVAL;
+ } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ return EINVAL;
+ }
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bss->ni_chan == c)
return 0; /* NB: nothing to do */
}
- ic->ic_des_chan = c;
+ vap->iv_des_chan = c;
error = 0;
- if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (vap->iv_opmode == IEEE80211_M_MONITOR &&
+ vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
- * Monitor and wds modes can switch directly.
+ * Monitor mode can switch directly.
*/
- ic->ic_curchan = ic->ic_des_chan;
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_set_channel(ic);
+ if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) {
+ /* XXX need state machine for other vap's to follow */
+ ieee80211_setcurchan(ic, vap->iv_des_chan);
+ vap->iv_bss->ni_chan = ic->ic_curchan;
+ } else
+ ic->ic_curchan = vap->iv_des_chan;
} else {
/*
* Need to go through the state machine in case we
* need to reassociate or the like. The state machine
* will pickup the desired channel and avoid scanning.
*/
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
- else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (IS_UP_AUTO(vap))
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
* When not up+running and a real channel has
* been specified fix the current channel so
* there is immediate feedback; e.g. via ifconfig.
*/
- ic->ic_curchan = ic->ic_des_chan;
+ ic->ic_curchan = vap->iv_des_chan;
}
}
return error;
@@ -1782,10 +1856,11 @@ setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
* Old api for setting the current channel; this is
* deprecated because channel numbers are ambiguous.
*/
-static int
-ieee80211_ioctl_setchannel(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
/* XXX 0xffff overflows 16-bit signed */
@@ -1797,7 +1872,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
} else {
struct ieee80211_channel *c2;
- c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
+ c = findchannel(ic, ireq->i_val, vap->iv_des_mode);
if (c == NULL) {
c = findchannel(ic, ireq->i_val,
IEEE80211_MODE_AUTO);
@@ -1816,7 +1891,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* 11g channel returned,
* otherwise we should be ok with what we've got.
*/
- switch (ic->ic_des_mode) {
+ switch (vap->iv_des_mode) {
case IEEE80211_MODE_11B:
if (IEEE80211_IS_CHAN_ANYG(c)) {
c2 = findchannel(ic, ireq->i_val,
@@ -1854,7 +1929,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
break;
}
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
}
/*
@@ -1862,10 +1937,11 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* channel description is provide so there is no ambiguity in
* identifying the channel.
*/
-static int
-ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel chan, *c;
int error;
@@ -1882,21 +1958,494 @@ ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
if (c == NULL)
return EINVAL;
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
+}
+
+static __noinline int
+ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_regdomain_req *reg;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_regdomain_req))
+ return EINVAL;
+ MALLOC(reg, struct ieee80211_regdomain_req *,
+ sizeof(struct ieee80211_regdomain_req), M_TEMP, M_NOWAIT);
+ if (reg == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, reg, sizeof(*reg));
+ if (error == 0)
+ error = ieee80211_setregdomain(vap, reg);
+ FREE(reg, M_TEMP);
+
+ return (error == 0 ? ENETRESET : error);
+}
+
+static int
+ieee80211_ioctl_setroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ /* XXX validate params */
+ /* XXX? ENETRESET to push to device? */
+ return copyin(ireq->i_data, vap->iv_roamparms,
+ sizeof(vap->iv_roamparms));
+}
+
+static int
+checkrate(const struct ieee80211_rateset *rs, int rate)
+{
+ int i;
+
+ if (rate == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ for (i = 0; i < rs->rs_nrates; i++)
+ if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate)
+ return 1;
+ return 0;
+}
+
+static int
+checkmcs(int mcs)
+{
+ if (mcs == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */
+ return 0;
+ return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
+}
+
+static __noinline int
+ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_txparams_req parms; /* XXX stack use? */
+ struct ieee80211_txparam *src, *dst;
+ const struct ieee80211_rateset *rs;
+ int error, i, changed;
+
+ if (ireq->i_len != sizeof(parms))
+ return EINVAL;
+ error = copyin(ireq->i_data, &parms, sizeof(parms));
+ if (error != 0)
+ return error;
+ changed = 0;
+ /* validate parameters and check if anything changed */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ /* 11n parameters are handled differently */
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i == IEEE80211_MODE_11NA ?
+ IEEE80211_MODE_11A : IEEE80211_MODE_11G];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkmcs(src->ucastrate) &&
+ !checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkmcs(src->mcastrate) &&
+ !checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkmcs(src->mgmtrate) &&
+ !checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ if (changed) {
+ /*
+ * Copy new parameters in place and notify the
+ * driver so it can push state to the device.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
+ if (isset(ic->ic_modecaps, i))
+ vap->iv_txparms[i] = parms.params[i];
+ }
+ /* XXX could be more intelligent,
+ e.g. don't reset if setting not being used */
+ return ENETRESET;
+ }
+ return 0;
+}
+
+/*
+ * Application Information Element support.
+ */
+static int
+setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
+{
+ struct ieee80211_appie *app = *aie;
+ struct ieee80211_appie *napp;
+ int error;
+
+ if (ireq->i_len == 0) { /* delete any existing ie */
+ if (app != NULL) {
+ *aie = NULL; /* XXX racey */
+ FREE(app, M_80211_NODE_IE);
+ }
+ return 0;
+ }
+ if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE))
+ return EINVAL;
+ /*
+ * Allocate a new appie structure and copy in the user data.
+ * When done swap in the new structure. Note that we do not
+ * guard against users holding a ref to the old structure;
+ * this must be handled outside this code.
+ *
+ * XXX bad bad bad
+ */
+ MALLOC(napp, struct ieee80211_appie *,
+ sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT);
+ if (napp == NULL)
+ return ENOMEM;
+ /* XXX holding ic lock */
+ error = copyin(ireq->i_data, napp->ie_data, ireq->i_len);
+ if (error) {
+ FREE(napp, M_80211_NODE_IE);
+ return error;
+ }
+ napp->ie_len = ireq->i_len;
+ *aie = napp;
+ if (app != NULL)
+ FREE(app, M_80211_NODE_IE);
+ return 0;
+}
+
+static void
+setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space)
+{
+ /* validate data is present as best we can */
+ if (space == 0 || 2+ie[1] > space)
+ return;
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ vap->iv_wpa_ie = ie;
+ else if (ie[0] == IEEE80211_ELEMID_RSN)
+ vap->iv_rsn_ie = ie;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq, int fc0)
+{
+ int error;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS) {
+ error = EINVAL;
+ break;
+ }
+ error = setappie(&vap->iv_appie_beacon, ireq);
+ if (error == 0)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE);
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ error = setappie(&vap->iv_appie_proberesp, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ error = setappie(&vap->iv_appie_assocresp, ireq);
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ error = setappie(&vap->iv_appie_probereq, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = setappie(&vap->iv_appie_assocreq, ireq);
+ else
+ error = EINVAL;
+ break;
+ case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK):
+ error = setappie(&vap->iv_appie_wpa, ireq);
+ if (error == 0) {
+ /*
+ * Must split single blob of data into separate
+ * WPA and RSN ie's because they go in different
+ * locations in the mgt frames.
+ * XXX use IEEE80211_IOC_WPA2 so user code does split
+ */
+ vap->iv_wpa_ie = NULL;
+ vap->iv_rsn_ie = NULL;
+ if (vap->iv_appie_wpa != NULL) {
+ struct ieee80211_appie *appie =
+ vap->iv_appie_wpa;
+ uint8_t *data = appie->ie_data;
+
+ /* XXX ie length validate is painful, cheat */
+ setwparsnie(vap, data, appie->ie_len);
+ setwparsnie(vap, data + 2 + data[1],
+ appie->ie_len - (2 + data[1]));
+ }
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ /*
+ * Must rebuild beacon frame as the update
+ * mechanism doesn't handle WPA/RSN ie's.
+ * Could extend it but it doesn't normally
+ * change; this is just to deal with hostapd
+ * plumbing the ie after the interface is up.
+ */
+ error = ENETRESET;
+ }
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int error;
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ IEEE80211_LOCK(ic);
+ error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0);
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_chanswitch_req csr;
+ struct ieee80211_channel *c;
+ int error;
+
+ if (ireq->i_len != sizeof(csr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &csr, sizeof(csr));
+ if (error != 0)
+ return error;
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ c = ieee80211_find_channel(ic,
+ csr.csa_chan.ic_freq, csr.csa_chan.ic_flags);
+ if (c == NULL)
+ return ENOENT;
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0)
+ ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count);
+ else
+ error = EBUSY;
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+#define IEEE80211_IOC_SCAN_FLAGS \
+ (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \
+ IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \
+ IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \
+ IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \
+ IEEE80211_IOC_SCAN_CHECK)
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_req sr; /* XXX off stack? */
+ int error, i;
+
+ /* NB: parent must be running */
+ if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return ENXIO;
+
+ if (ireq->i_len != sizeof(sr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &sr, sizeof(sr));
+ if (error != 0)
+ return error;
+ /* convert duration */
+ if (sr.sr_duration == IEEE80211_IOC_SCAN_FOREVER)
+ sr.sr_duration = IEEE80211_SCAN_FOREVER;
+ else {
+ if (sr.sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN ||
+ sr.sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX)
+ return EINVAL;
+ sr.sr_duration = msecs_to_ticks(sr.sr_duration);
+ if (sr.sr_duration < 1)
+ sr.sr_duration = 1;
+ }
+ /* convert min/max channel dwell */
+ if (sr.sr_mindwell != 0) {
+ sr.sr_mindwell = msecs_to_ticks(sr.sr_mindwell);
+ if (sr.sr_mindwell < 1)
+ sr.sr_mindwell = 1;
+ }
+ if (sr.sr_maxdwell != 0) {
+ sr.sr_maxdwell = msecs_to_ticks(sr.sr_maxdwell);
+ if (sr.sr_maxdwell < 1)
+ sr.sr_maxdwell = 1;
+ }
+ /* NB: silently reduce ssid count to what is supported */
+ if (sr.sr_nssid > IEEE80211_SCAN_MAX_SSID)
+ sr.sr_nssid = IEEE80211_SCAN_MAX_SSID;
+ for (i = 0; i < sr.sr_nssid; i++)
+ if (sr.sr_ssid[i].len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ /* cleanse flags just in case, could reject if invalid flags */
+ sr.sr_flags &= IEEE80211_IOC_SCAN_FLAGS;
+ /*
+ * Add an implicit NOPICK if the vap is not marked UP. This
+ * allows applications to scan without joining a bss (or picking
+ * a channel and setting up a bss) and without forcing manual
+ * roaming mode--you just need to mark the parent device UP.
+ */
+ if ((vap->iv_ifp->if_flags & IFF_UP) == 0)
+ sr.sr_flags |= IEEE80211_IOC_SCAN_NOPICK;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n",
+ __func__, sr.sr_flags,
+ (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "",
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, sr.sr_nssid);
+ /*
+ * If we are in INIT state then the driver has never had a chance
+ * to setup hardware state to do a scan; we must use the state
+ * machine to get us up to the SCAN state but once we reach SCAN
+ * state we then want to use the supplied params. Stash the
+ * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the
+ * state machines will recognize this and use the stashed params
+ * to issue the scan request.
+ *
+ * Otherwise just invoke the scan machinery directly.
+ */
+ IEEE80211_LOCK(ic);
+ if (vap->iv_state == IEEE80211_S_INIT) {
+ /* NB: clobbers previous settings */
+ vap->iv_scanreq_flags = sr.sr_flags;
+ vap->iv_scanreq_duration = sr.sr_duration;
+ vap->iv_scanreq_nssid = sr.sr_nssid;
+ for (i = 0; i < sr.sr_nssid; i++) {
+ vap->iv_scanreq_ssid[i].len = sr.sr_ssid[i].len;
+ memcpy(vap->iv_scanreq_ssid[i].ssid, sr.sr_ssid[i].ssid,
+ sr.sr_ssid[i].len);
+ }
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ /* XXX neeed error return codes */
+ if (sr.sr_flags & IEEE80211_IOC_SCAN_CHECK) {
+ (void) ieee80211_check_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ } else {
+ (void) ieee80211_start_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ }
+ }
+ return error;
+#undef IEEE80211_IOC_SCAN_FLAGS
+}
+
+static __noinline int
+ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ ni->ni_vlan = vlan.sv_vlan;
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+isvap11g(const struct ieee80211vap *vap)
+{
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_ANYG(bss->ni_chan);
}
static int
-ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+isvapht(const struct ieee80211vap *vap)
{
- static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
- struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(bss->ni_chan);
+}
+
+static __noinline int
+ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
const struct ieee80211_authenticator *auth;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
uint8_t tmpbssid[IEEE80211_ADDR_LEN];
struct ieee80211_key *k;
- int j, caps;
u_int kid;
error = 0;
@@ -1908,39 +2457,37 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpssid, ireq->i_len);
if (error)
break;
- memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_ssid[0].len = ireq->i_len;
- memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
- ic->ic_des_nssid = (ireq->i_len > 0);
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
+ vap->iv_des_ssid[0].len = ireq->i_len;
+ memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len);
+ vap->iv_des_nssid = (ireq->i_len > 0);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEP:
switch (ireq->i_val) {
case IEEE80211_WEP_OFF:
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_ON:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_MIXED:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
}
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEPKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- k = &ic->ic_nw_keys[kid];
+ k = &vap->iv_nw_keys[kid];
if (ireq->i_len == 0) {
/* zero-len =>'s delete any existing key */
- (void) ieee80211_crypto_delkey(ic, k);
+ (void) ieee80211_crypto_delkey(vap, k);
break;
}
if (ireq->i_len > sizeof(tmpkey))
@@ -1949,24 +2496,24 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpkey, ireq->i_len);
if (error)
break;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
k->wk_keyix = kid; /* NB: force fixed key id */
- if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+ if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP,
IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
k->wk_keylen = ireq->i_len;
memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
- if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
+ if (!ieee80211_crypto_setkey(vap, k, vap->iv_myaddr))
error = EINVAL;
} else
error = EINVAL;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
break;
case IEEE80211_IOC_WEPTXKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID &&
(uint16_t) kid != IEEE80211_KEYIX_NONE)
return EINVAL;
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
break;
case IEEE80211_IOC_AUTHMODE:
switch (ireq->i_val) {
@@ -1984,74 +2531,66 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
switch (ireq->i_val) {
case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
ireq->i_val = IEEE80211_AUTH_8021X;
break;
case IEEE80211_AUTH_OPEN: /* open */
- ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+ vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
break;
case IEEE80211_AUTH_SHARED: /* shared-key */
case IEEE80211_AUTH_8021X: /* 802.1x */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* both require a key so mark the PRIVACY capability */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
break;
case IEEE80211_AUTH_AUTO: /* auto */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* XXX PRIVACY handling? */
/* XXX what's the right way to do this? */
break;
}
/* NB: authenticator attach/detach happens on state change */
- ic->ic_bss->ni_authmode = ireq->i_val;
+ vap->iv_bss->ni_authmode = ireq->i_val;
/* XXX mixed/mode/usage? */
- ic->ic_auth = auth;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_auth = auth;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANNEL:
- error = ieee80211_ioctl_setchannel(ic, ireq);
+ error = ieee80211_ioctl_setchannel(vap, ireq);
break;
case IEEE80211_IOC_POWERSAVE:
switch (ireq->i_val) {
case IEEE80211_POWERSAVE_OFF:
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
+ ieee80211_syncflag(vap, -IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
case IEEE80211_POWERSAVE_ON:
- if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
- error = EINVAL;
- else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if ((vap->iv_caps & IEEE80211_C_PMGT) == 0)
+ error = EOPNOTSUPP;
+ else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) {
+ ieee80211_syncflag(vap, IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET) {
- /*
- * Switching in+out of power save mode
- * should not require a state change.
- */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- }
break;
case IEEE80211_IOC_POWERSAVESLEEP:
if (ireq->i_val < 0)
return EINVAL;
ic->ic_lintval = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_RTS_MAX))
return EINVAL;
- ic->ic_rtsthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_rtsthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_PROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2060,158 +2599,96 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
/* NB: if not operating in 11g this can wait */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_TXPOWER:
if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_TXPOWER_MAX))
return EINVAL;
ic->ic_txpowlimit = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_ROAMING:
if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
ireq->i_val <= IEEE80211_ROAMING_MANUAL))
return EINVAL;
- ic->ic_roaming = ireq->i_val;
+ vap->iv_roaming = ireq->i_val;
/* XXXX reset? */
break;
case IEEE80211_IOC_PRIVACY:
if (ireq->i_val) {
/* XXX check for key state? */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
} else
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
else
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_setkey(ic, ireq);
+ error = ieee80211_ioctl_setkey(vap, ireq);
break;
case IEEE80211_IOC_DELKEY:
- error = ieee80211_ioctl_delkey(ic, ireq);
+ error = ieee80211_ioctl_delkey(vap, ireq);
break;
case IEEE80211_IOC_MLME:
- error = ieee80211_ioctl_setmlme(ic, ireq);
- break;
- case IEEE80211_IOC_OPTIE:
- error = ieee80211_ioctl_setoptie(ic, ireq);
+ error = ieee80211_ioctl_setmlme(vap, ireq);
break;
case IEEE80211_IOC_COUNTERMEASURES:
if (ireq->i_val) {
- if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_COUNTERM;
+ if ((vap->iv_flags & IEEE80211_F_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_COUNTERM;
} else
- ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+ vap->iv_flags &= ~IEEE80211_F_COUNTERM;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPA:
if (ireq->i_val > 3)
return EINVAL;
/* XXX verify ciphers available */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
switch (ireq->i_val) {
case 1:
- ic->ic_flags |= IEEE80211_F_WPA1;
+ vap->iv_flags |= IEEE80211_F_WPA1;
break;
case 2:
- ic->ic_flags |= IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA2;
break;
case 3:
- ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
}
- error = ENETRESET;
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_WME:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_WME;
+ if ((vap->iv_caps & IEEE80211_C_WME) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_WME);
} else
- ic->ic_flags &= ~IEEE80211_F_WME;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, 0);
+ ieee80211_syncflag(vap, -IEEE80211_F_WME);
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_HIDESSID:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_HIDESSID;
+ vap->iv_flags |= IEEE80211_F_HIDESSID;
else
- ic->ic_flags &= ~IEEE80211_F_HIDESSID;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_HIDESSID;
+ error = ERESTART; /* XXX ENETRESET? */
break;
case IEEE80211_IOC_APBRIDGE:
if (ireq->i_val == 0)
- ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+ vap->iv_flags |= IEEE80211_F_NOBRIDGE;
else
- ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
- !ieee80211_crypto_available(ireq->i_val))
- return EINVAL;
- rsn->rsn_mcastcipher = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_mcastkeylen = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- /*
- * Convert user-specified cipher set to the set
- * we can support (via hardware or software).
- * NB: this logic intentionally ignores unknown and
- * unsupported ciphers so folks can specify 0xff or
- * similar and get all available ciphers.
- */
- caps = 0;
- for (j = 1; j < 32; j++) /* NB: skip WEP */
- if ((ireq->i_val & (1<<j)) &&
- ((ic->ic_caps & cipher2cap(j)) ||
- ieee80211_crypto_available(j)))
- caps |= 1<<j;
- if (caps == 0) /* nothing available */
- return EINVAL;
- /* XXX verify ciphers ok for unicast use? */
- /* XXX disallow if running as it'll have no effect */
- rsn->rsn_ucastcipherset = caps;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
- return EINVAL;
- rsn->rsn_ucastcipher = ireq->i_val;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_ucastkeylen = ireq->i_val;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- /* NB: for testing */
- ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
- ((uint16_t) ireq->i_len);
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- /* XXX check */
- rsn->rsn_keymgmtset = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_RSNCAPS:
- /* XXX check */
- rsn->rsn_caps = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ vap->iv_flags &= ~IEEE80211_F_NOBRIDGE;
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != sizeof(tmpbssid))
@@ -2219,39 +2696,71 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
if (error)
break;
- IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
- if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
- ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid);
+ if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid))
+ vap->iv_flags &= ~IEEE80211_F_DESBSSID;
else
- ic->ic_flags |= IEEE80211_F_DESBSSID;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_setchanlist(ic, ireq);
+ error = ieee80211_ioctl_setchanlist(vap, ireq);
break;
+#define OLD_IEEE80211_IOC_SCAN_REQ 23
+#ifdef OLD_IEEE80211_IOC_SCAN_REQ
+ case OLD_IEEE80211_IOC_SCAN_REQ:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: active scan request\n", __func__);
+ /*
+ * If we are in INIT state then the driver has never
+ * had a chance to setup hardware state to do a scan;
+ * use the state machine to get us up the SCAN state.
+ * Otherwise just invoke the scan machinery to start
+ * a one-time scan.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else
+ (void) ieee80211_start_scan(vap,
+ IEEE80211_SCAN_ACTIVE |
+ IEEE80211_SCAN_NOPICK |
+ IEEE80211_SCAN_ONCE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ /* XXX use ioctl params */
+ vap->iv_des_nssid, vap->iv_des_ssid);
+ break;
+#endif /* OLD_IEEE80211_IOC_SCAN_REQ */
case IEEE80211_IOC_SCAN_REQ:
- if (!IS_UP(ic))
- return EINVAL;
- (void) ieee80211_start_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_NOPICK |
- IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
- /* XXX use ioctl params */
- ic->ic_des_nssid, ic->ic_des_ssid);
+ error = ieee80211_ioctl_scanreq(vap, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_CANCEL:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel scan\n", __func__);
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_IOC_HTCONF:
+ if (ireq->i_val & 1)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_HT);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_HT);
+ if (ireq->i_val & 2)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_USEHT40);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_USEHT40);
+ error = ENETRESET;
break;
case IEEE80211_IOC_ADDMAC:
case IEEE80211_IOC_DELMAC:
- error = ieee80211_ioctl_macmac(ic, ireq);
+ error = ieee80211_ioctl_macmac(vap, ireq);
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_setmaccmd(ic, ireq);
+ error = ieee80211_ioctl_setmaccmd(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_setstastats(ic, ireq);
+ error = ieee80211_ioctl_setstastats(vap, ireq);
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_setstatxpow(ic, ireq);
+ error = ieee80211_ioctl_setstatxpow(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -2259,22 +2768,22 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_setwmeparam(ic, ireq);
+ error = ieee80211_ioctl_setwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_DTIM_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_DTIM_MAX) {
- ic->ic_dtim_period = ireq->i_val;
+ vap->iv_dtim_period = ireq->i_val;
error = ENETRESET; /* requires restart */
} else
error = EINVAL;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_BINTVAL_MAX) {
@@ -2285,210 +2794,258 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
break;
case IEEE80211_IOC_PUREG:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_PUREG;
+ vap->iv_flags |= IEEE80211_F_PUREG;
else
- ic->ic_flags &= ~IEEE80211_F_PUREG;
+ vap->iv_flags &= ~IEEE80211_F_PUREG;
/* NB: reset only if we're operating on an 11g channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
+ if (isvap11g(vap))
error = ENETRESET;
break;
case IEEE80211_IOC_FF:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_FF) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_FF;
+ if ((vap->iv_caps & IEEE80211_C_FF) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_FF;
} else
- ic->ic_flags &= ~IEEE80211_F_FF;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_FF;
+ error = ERESTART;
break;
case IEEE80211_IOC_TURBOP:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_TURBOP;
+ if ((vap->iv_caps & IEEE80211_C_TURBOP) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_TURBOP;
} else
- ic->ic_flags &= ~IEEE80211_F_TURBOP;
+ vap->iv_flags &= ~IEEE80211_F_TURBOP;
error = ENETRESET;
break;
case IEEE80211_IOC_BGSCAN:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BGSCAN;
+ if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
} else
- ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ vap->iv_flags &= ~IEEE80211_F_BGSCAN;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
- ic->ic_bgscanidle = ireq->i_val*hz/1000;
+ vap->iv_bgscanidle = ireq->i_val*hz/1000;
else
error = EINVAL;
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
- ic->ic_bgscanintvl = ireq->i_val*hz;
+ vap->iv_bgscanintvl = ireq->i_val*hz;
else
error = EINVAL;
break;
case IEEE80211_IOC_SCANVALID:
if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
- ic->ic_scanvalid = ireq->i_val*hz;
+ vap->iv_scanvalid = ireq->i_val*hz;
else
error = EINVAL;
break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ic->ic_roam.rssi11a = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ic->ic_roam.rssi11bOnly = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ic->ic_roam.rssi11b = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
- break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
+ if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 &&
ireq->i_val != IEEE80211_FRAG_MAX)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_FRAG_MAX))
return EINVAL;
- ic->ic_fragthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_fragthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_BURST:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BURST;
+ if ((vap->iv_caps & IEEE80211_C_BURST) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_BURST);
} else
- ic->ic_flags &= ~IEEE80211_F_BURST;
- error = ENETRESET; /* XXX maybe not for station? */
+ ieee80211_syncflag(vap, -IEEE80211_F_BURST);
+ error = ERESTART;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_HWBMISS_MAX))
return EINVAL;
- ic->ic_bmissthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_bmissthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_setcurchan(ic, ireq);
+ error = ieee80211_ioctl_setcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
if (ireq->i_val) {
#define IEEE80211_HTCAP_SHORTGI \
(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
- if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
+ if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
return EINVAL;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
#undef IEEE80211_HTCAP_SHORTGI
} else
- ic->ic_flags_ext &=
+ vap->iv_flags_ext &=
~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
- /* XXX kick state machine? */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- /* XXX validate */
- ic->ic_ampdu_limit = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K))
+ return EINVAL;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_ampdu_rxmax = ireq->i_val;
+ else
+ vap->iv_ampdu_limit = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- /* XXX validate */
- ic->ic_ampdu_density = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16))
+ return EINVAL;
+ vap->iv_ampdu_density = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
/* XXX validate */
- ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */
+ vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext |= IEEE80211_FEXT_PUREN;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_PUREN;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_DOTH:
if (ireq->i_val) {
#if 0
/* XXX no capability */
- if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
- return EINVAL;
+ if ((vap->iv_caps & IEEE80211_C_DOTH) == 0)
+ return EOPNOTSUPP;
#endif
- ic->ic_flags |= IEEE80211_F_DOTH;
+ vap->iv_flags |= IEEE80211_F_DOTH;
} else
- ic->ic_flags &= ~IEEE80211_F_DOTH;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_flags &= ~IEEE80211_F_DOTH;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_setregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_setroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_settxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
- return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
+ break;
+ case IEEE80211_IOC_DWDS:
+ if (ireq->i_val) {
+ /* NB: DWDS only makes sense for WDS-capable devices */
+ if ((ic->ic_caps & IEEE80211_C_WDS) == 0)
+ return EOPNOTSUPP;
+ /* NB: DWDS is used only with ap+sta vaps */
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ vap->iv_flags |= IEEE80211_F_DWDS;
+ } else
+ vap->iv_flags &= ~IEEE80211_F_DWDS;
break;
case IEEE80211_IOC_INACTIVITY:
if (ireq->i_val)
- ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext |= IEEE80211_FEXT_INACT;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_setappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_WPS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS;
+ break;
+ case IEEE80211_IOC_TSN:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_TSN;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN;
+ break;
+ case IEEE80211_IOC_CHANSWITCH:
+ error = ieee80211_ioctl_chanswitch(vap, ireq);
+ break;
+ case IEEE80211_IOC_DFS:
+ if (ireq->i_val) {
+#if 0
+ /* XXX no capability */
+ if ((vap->iv_caps & IEEE80211_C_DFS) == 0)
+ return EOPNOTSUPP;
+#endif
+ /* NB: DFS requires 11h support */
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS;
+ break;
+ case IEEE80211_IOC_DOTD:
+ if (ireq->i_val)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DOTD;
else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = ENETRESET;
break;
case IEEE80211_IOC_HTPROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2496,57 +3053,143 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ic->ic_htprotmode = ireq->i_val ?
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
/* NB: if not operating in 11n this can wait */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ if (isvapht(vap))
error = ERESTART;
break;
- case IEEE80211_IOC_HTCONF:
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_HT;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
- error = ENETRESET;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_setstavlan(vap, ireq);
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET)
- error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
+ /*
+ * The convention is that ENETRESET means an operation
+ * requires a complete re-initialization of the device (e.g.
+ * changing something that affects the association state).
+ * ERESTART means the request may be handled with only a
+ * reload of the hardware state. We hand ERESTART requests
+ * to the iv_reset callback so the driver can decide. If
+ * a device does not fillin iv_reset then it defaults to one
+ * that returns ENETRESET. Otherwise a driver may return
+ * ENETRESET (in which case a full reset will be done) or
+ * 0 to mean there's no need to do anything (e.g. when the
+ * change has no effect on the driver/device).
+ */
+ if (error == ERESTART)
+ error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ?
+ vap->iv_reset(vap, ireq->i_type) : 0;
+ if (error == ENETRESET) {
+ /* XXX need to re-think AUTO handling */
+ if (IS_UP_AUTO(vap))
+ ieee80211_init(vap);
+ error = 0;
+ }
return error;
}
+/*
+ * Rebuild the parent's multicast address list after an add/del
+ * of a multicast address for a vap. We have no way to tell
+ * what happened above to optimize the work so we purge the entire
+ * list and rebuild from scratch. This is way expensive.
+ * Note also the half-baked workaround for if_addmulti calling
+ * back to the parent device; there's no way to insert mcast
+ * entries quietly and/or cheaply.
+ */
+static void
+ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
+{
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ void *ioctl;
+
+ IEEE80211_LOCK(ic);
+ if_purgemaddrs(parent);
+ ioctl = parent->if_ioctl; /* XXX WAR if_allmulti */
+ parent->if_ioctl = NULL;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifmultiaddr *ifma;
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ (void) if_addmulti(parent, ifma->ifma_addr, NULL);
+ }
+ parent->if_ioctl = ioctl;
+
+ ic->ic_update_mcast(ic->ic_ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
int
-ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
+ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
int error = 0;
struct ifreq *ifr;
struct ifaddr *ifa; /* XXX */
+ vap = ifp->if_softc;
+ if (vap == NULL) {
+ /*
+ * During detach we clear the backpointer in the softc
+ * so any ioctl request through the ifnet that arrives
+ * before teardown is ignored/rejected. In particular
+ * this hack handles destroying a vap used by an app
+ * like wpa_supplicant that will respond to the vap
+ * being forced into INIT state by immediately trying
+ * to force it back up. We can yank this hack if/when
+ * we can destroy the ifnet before cleaning up vap state.
+ */
+ return ENXIO;
+ }
switch (cmd) {
+ case SIOCSIFFLAGS:
+ ic = vap->iv_ic;
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ if (ifp->if_flags & IFF_UP) {
+ /*
+ * Bring ourself up unless we're already operational.
+ * If we're the first vap and the parent is not up
+ * then it will automatically be brought up as a
+ * side-effect of bringing ourself up.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_start_locked(vap);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Stop ourself. If we are the last vap to be
+ * marked down the parent will also be taken down.
+ */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ieee80211_ioctl_updatemulti(vap->iv_ic);
+ break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, (struct ifreq *) data,
- &ic->ic_media, cmd);
+ ifr = (struct ifreq *)data;
+ error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd);
break;
case SIOCG80211:
- error = ieee80211_ioctl_get80211(ic, cmd,
+ error = ieee80211_ioctl_get80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCS80211:
error = priv_check(curthread, PRIV_NET80211_MANAGE);
if (error == 0)
- error = ieee80211_ioctl_set80211(ic, cmd,
+ error = ieee80211_ioctl_set80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;
- copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
+ copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats));
break;
case SIOCSIFMTU:
ifr = (struct ifreq *)data;
@@ -2601,6 +3244,14 @@ ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
break;
}
break;
+ /* Pass NDIS ioctls up to the driver */
+ case SIOCGDRVSPEC:
+ case SIOCSDRVSPEC:
+ case SIOCGPRIVATE_0: {
+ struct ifnet *parent = vap->iv_ic->ic_ifp;
+ error = parent->if_ioctl(parent, cmd, data);
+ break;
+ }
default:
error = ether_ioctl(ifp, cmd, data);
break;
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index e96ab52..c40b929 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -196,6 +196,8 @@ struct ieee80211_stats {
uint32_t is_tx_badstate; /* tx discard state != RUN */
uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
uint32_t is_tx_classify; /* tx classification failed */
+ uint32_t is_dwds_mcast; /* discard mcast over dwds */
+ uint32_t is_dwds_qdrop; /* dwds pending frame q full */
uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
@@ -208,7 +210,12 @@ struct ieee80211_stats {
uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
- uint32_t is_spare[16];
+ uint32_t is_scan_bg; /* background scans started */
+ uint8_t is_rx_deauth_code; /* last rx'd deauth reason */
+ uint8_t is_rx_disassoc_code; /* last rx'd disassoc reason */
+ uint8_t is_rx_authfail_code; /* last rx'd auth fail reason */
+ uint32_t is_beacon_miss; /* beacon miss notification */
+ uint32_t is_spare[14];
};
/*
@@ -264,6 +271,7 @@ struct ieee80211req_mlme {
#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+#define IEEE80211_MLME_AUTH 6 /* authenticate station */
uint8_t im_ssid_len; /* length of optional ssid */
uint16_t im_reason; /* 802.11 reason code */
uint8_t im_macaddr[IEEE80211_ADDR_LEN];
@@ -281,6 +289,7 @@ enum {
IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */
IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */
IEEE80211_MACCMD_LIST = 6, /* get ACL database */
+ IEEE80211_MACCMD_POLICY_RADIUS = 7, /* set policy: RADIUS managed */
};
struct ieee80211req_maclist {
@@ -335,7 +344,7 @@ struct ieee80211req_sta_stats {
* to retrieve other data like stats, unicast key, etc.
*/
struct ieee80211req_sta_info {
- uint16_t isi_len; /* length (mult of 4) */
+ uint16_t isi_len; /* total length (mult of 4) */
uint16_t isi_ie_off; /* offset to IE data */
uint16_t isi_ie_len; /* IE length */
uint16_t isi_freq; /* MHz */
@@ -350,7 +359,7 @@ struct ieee80211req_sta_info {
uint8_t isi_nrates;
/* negotiated rates */
uint8_t isi_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isi_txrate; /* index to isi_rates[] */
+ uint8_t isi_txrate; /* legacy/IEEE rate or MCS */
uint16_t isi_associd; /* assoc response */
uint16_t isi_txpower; /* current tx power */
uint16_t isi_vlan; /* vlan tag */
@@ -358,6 +367,9 @@ struct ieee80211req_sta_info {
uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
uint16_t isi_inact; /* inactivity timer */
+ uint16_t isi_txmbps; /* current tx rate in .5 Mb/s */
+ uint32_t isi_jointime; /* time of assoc/join */
+ struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */
/* XXX frag state? */
/* variable length IE data */
};
@@ -384,15 +396,98 @@ struct ieee80211req_sta_txpow {
};
/*
- * WME parameters are set and return using i_val and i_len.
- * i_val holds the value itself. i_len specifies the AC
- * and, as appropriate, then high bit specifies whether the
- * operation is to be applied to the BSS or ourself.
+ * WME parameters manipulated with IEEE80211_IOC_WME_CWMIN
+ * through IEEE80211_IOC_WME_ACKPOLICY are set and return
+ * using i_val and i_len. i_val holds the value itself.
+ * i_len specifies the AC and, as appropriate, then high bit
+ * specifies whether the operation is to be applied to the
+ * BSS or ourself.
*/
#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */
#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */
#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */
+/*
+ * Application Information Elements can be appended to a variety
+ * of frames with the IEE80211_IOC_APPIE request. This request
+ * piggybacks on a normal ieee80211req; the frame type is passed
+ * in i_val as the 802.11 FC0 bytes and the length of the IE data
+ * is passed in i_len. The data is referenced in i_data. If i_len
+ * is zero then any previously configured IE data is removed. At
+ * most IEEE80211_MAX_APPIE data be appened. Note that multiple
+ * IE's can be supplied; the data is treated opaquely.
+ */
+#define IEEE80211_MAX_APPIE 1024 /* max app IE data */
+/*
+ * Hack: the WPA authenticator uses this mechanism to specify WPA
+ * ie's that are used instead of the ones normally constructed using
+ * the cipher state setup with separate ioctls. This avoids issues
+ * like the authenticator ordering ie data differently than the
+ * net80211 layer and needing to keep separate state for WPA and RSN.
+ */
+#define IEEE80211_APPIE_WPA \
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+
+/*
+ * Station mode roaming parameters. These are maintained
+ * per band/mode and control the roaming algorithm.
+ */
+struct ieee80211_roamparams_req {
+ struct ieee80211_roamparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Transmit parameters. These can be used to set fixed transmit
+ * rate for each operating mode when operating as client or on a
+ * per-client basis according to the capabilities of the client
+ * (e.g. an 11b client associated to an 11g ap) when operating as
+ * an ap.
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparams_req {
+ struct ieee80211_txparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Set regulatory domain state with IEEE80211_IOC_REGDOMAIN.
+ * Note this is both the regulatory description and the channel
+ * list. The get request for IEEE80211_IOC_REGDOMAIN returns
+ * only the regdomain info; the channel list is obtained
+ * separately with IEEE80211_IOC_CHANINFO.
+ */
+struct ieee80211_regdomain_req {
+ struct ieee80211_regdomain rd;
+ struct ieee80211req_chaninfo chaninfo;
+};
+
+/*
+ * Get driver capabilities. Driver, hardware crypto, and
+ * HT/802.11n capabilities, and a table that describes what
+ * the radio can do.
+ */
+struct ieee80211_devcaps_req {
+ uint32_t dc_drivercaps; /* general driver caps */
+ uint32_t dc_cryptocaps; /* hardware crypto support */
+ uint32_t dc_htcaps; /* HT/802.11n support */
+ struct ieee80211req_chaninfo dc_chaninfo;
+};
+
+struct ieee80211_chanswitch_req {
+ struct ieee80211_channel csa_chan; /* new channel */
+ int csa_mode; /* CSA mode */
+ int csa_count; /* beacon count to switch */
+};
+
+/*
+ * Get/set per-station vlan tag.
+ */
+struct ieee80211req_sta_vlan {
+ uint8_t sv_macaddr[IEEE80211_ADDR_LEN];
+ uint16_t sv_vlan;
+};
+
#ifdef __FreeBSD__
/*
* FreeBSD-style ioctls.
@@ -443,8 +538,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WPAKEY 19
#define IEEE80211_IOC_DELKEY 20
#define IEEE80211_IOC_MLME 21
-#define IEEE80211_IOC_OPTIE 22 /* optional info. element */
-#define IEEE80211_IOC_SCAN_REQ 23
+/* 22 was IEEE80211_IOC_OPTIE, replaced by IEEE80211_IOC_APPIE */
+/* 23 was IEEE80211_IOC_SCAN_REQ */
/* 24 was IEEE80211_IOC_SCAN_RESULTS */
#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */
#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */
@@ -452,14 +547,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */
#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */
#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */
-#define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */
-#define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */
-#define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */
-#define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */
-#define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */
-#define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */
-#define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */
-#define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */
+/* 31-35,37-38 were for WPA authenticator settings */
+/* 36 was IEEE80211_IOC_DRIVER_CAPS */
#define IEEE80211_IOC_WPAIE 39 /* WPA information element */
#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */
#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */
@@ -484,13 +573,7 @@ struct ieee80211req {
#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */
#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */
#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */
-#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */
-#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */
-#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */
-#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */
-#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */
-#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */
-#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */
+/* 66-72 were IEEE80211_IOC_ROAM_* and IEEE80211_IOC_MCAST_RATE */
#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */
#define IEEE80211_IOC_BURST 75 /* packet bursting */
#define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */
@@ -506,13 +589,84 @@ struct ieee80211req {
#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */
#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */
#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */
-#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */
-#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */
-#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */
+/* 89-91 were regulatory items */
#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
+#define IEEE80211_IOC_DWDS 93 /* DWDS/4-address handling */
#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
+#define IEEE80211_IOC_APPIE 95 /* application IE's */
+#define IEEE80211_IOC_WPS 96 /* WPS operation */
+#define IEEE80211_IOC_TSN 97 /* TSN operation */
+#define IEEE80211_IOC_DEVCAPS 98 /* driver+device capabilities */
+#define IEEE80211_IOC_CHANSWITCH 99 /* start 11h channel switch */
+#define IEEE80211_IOC_DFS 100 /* DFS (on, off) */
+#define IEEE80211_IOC_DOTD 101 /* 802.11d (on, off) */
#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
+#define IEEE80211_IOC_SCAN_REQ 103 /* scan w/ specified params */
+#define IEEE80211_IOC_SCAN_CANCEL 104 /* cancel ongoing scan */
#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
+#define IEEE80211_IOC_REGDOMAIN 106 /* regulatory domain info */
+#define IEEE80211_IOC_ROAM 107 /* roaming params en masse */
+#define IEEE80211_IOC_TXPARAMS 108 /* tx parameters */
+#define IEEE80211_IOC_STA_VLAN 109 /* per-station vlan tag */
+
+/*
+ * Parameters for controlling a scan requested with
+ * IEEE80211_IOC_SCAN_REQ.
+ *
+ * Active scans cause ProbeRequest frames to be issued for each
+ * specified ssid and, by default, a broadcast ProbeRequest frame.
+ * The set of ssid's is specified in the request.
+ *
+ * By default the scan will cause a BSS to be joined (in station/adhoc
+ * mode) or a channel to be selected for operation (hostap mode).
+ * To disable that specify IEEE80211_IOC_SCAN_NOPICK and if the
+ *
+ * If the station is currently associated to an AP then a scan request
+ * will cause the station to leave the current channel and potentially
+ * miss frames from the AP. Alternatively the station may notify the
+ * AP that it is going into power save mode before it leaves the channel.
+ * This ensures frames for the station are buffered by the AP. This is
+ * termed a ``bg scan'' and is requested with the IEEE80211_IOC_SCAN_BGSCAN
+ * flag. Background scans may take longer than foreground scans and may
+ * be preempted by traffic. If a station is not associated to an AP
+ * then a request for a background scan is automatically done in the
+ * foreground.
+ *
+ * The results of the scan request are cached by the system. This
+ * information is aged out and/or invalidated based on events like not
+ * being able to associated to an AP. To flush the current cache
+ * contents before doing a scan the IEEE80211_IOC_SCAN_FLUSH flag may
+ * be specified.
+ *
+ * By default the scan will be done until a suitable AP is located
+ * or a channel is found for use. A scan can also be constrained
+ * to be done once (IEEE80211_IOC_SCAN_ONCE) or to last for no more
+ * than a specified duration.
+ */
+struct ieee80211_scan_req {
+ int sr_flags;
+#define IEEE80211_IOC_SCAN_NOPICK 0x00001 /* scan only, no selection */
+#define IEEE80211_IOC_SCAN_ACTIVE 0x00002 /* active scan (probe req) */
+#define IEEE80211_IOC_SCAN_PICK1ST 0x00004 /* ``hey sailor'' mode */
+#define IEEE80211_IOC_SCAN_BGSCAN 0x00008 /* bg scan, exit ps at end */
+#define IEEE80211_IOC_SCAN_ONCE 0x00010 /* do one complete pass */
+#define IEEE80211_IOC_SCAN_NOBCAST 0x00020 /* don't send bcast probe req */
+#define IEEE80211_IOC_SCAN_NOJOIN 0x00040 /* no auto-sequencing */
+#define IEEE80211_IOC_SCAN_FLUSH 0x10000 /* flush scan cache first */
+#define IEEE80211_IOC_SCAN_CHECK 0x20000 /* check scan cache first */
+ u_int sr_duration; /* duration (ms) */
+#define IEEE80211_IOC_SCAN_DURATION_MIN 1
+#define IEEE80211_IOC_SCAN_DURATION_MAX 0x7fffffff
+#define IEEE80211_IOC_SCAN_FOREVER IEEE80211_IOC_SCAN_DURATION_MAX
+ u_int sr_mindwell; /* min channel dwelltime (ms) */
+ u_int sr_maxdwell; /* max channel dwelltime (ms) */
+ int sr_nssid;
+#define IEEE80211_IOC_SCAN_MAX_SSID 3
+ struct {
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ } sr_ssid[IEEE80211_IOC_SCAN_MAX_SSID];
+};
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
@@ -523,8 +677,8 @@ struct ieee80211req {
* in isr_len. Result records are rounded to a multiple of 4 bytes.
*/
struct ieee80211req_scan_result {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_ie_off; /* offset to IE data */
+ uint16_t isr_len; /* total length (mult of 4) */
+ uint16_t isr_ie_off; /* offset to SSID+IE data */
uint16_t isr_ie_len; /* IE length */
uint16_t isr_freq; /* MHz */
uint16_t isr_flags; /* channel flags */
@@ -540,10 +694,47 @@ struct ieee80211req_scan_result {
/* variable length SSID followed by IE data */
};
+/*
+ * Virtual AP cloning parameters. The parent device must
+ * be a vap-capable device. All parameters specified with
+ * the clone request are fixed for the lifetime of the vap.
+ *
+ * There are two flavors of WDS vaps: legacy and dynamic.
+ * Legacy WDS operation implements a static binding between
+ * two stations encapsulating traffic in 4-address frames.
+ * Dynamic WDS vaps are created when a station associates to
+ * an AP and sends a 4-address frame. If the AP vap is
+ * configured to support WDS then this will generate an
+ * event to user programs listening on the routing socket
+ * and a Dynamic WDS vap will be created to handle traffic
+ * to/from that station. In both cases the bssid of the
+ * peer must be specified when creating the vap.
+ *
+ * By default a vap will inherit the mac address/bssid of
+ * the underlying device. To request a unique address the
+ * IEEE80211_CLONE_BSSID flag should be supplied. This is
+ * meaningless for WDS vaps as they share the bssid of an
+ * AP vap that must otherwise exist. Note that some devices
+ * may not be able to support multiple addresses.
+ *
+ * Station mode vap's normally depend on the device to notice
+ * when the AP stops sending beacon frames. If IEEE80211_CLONE_NOBEACONS
+ * is specified the net80211 layer will do this in s/w. This
+ * is mostly useful when setting up a WDS repeater/extender where
+ * an AP vap is combined with a sta vap and the device isn't able
+ * to track beacon frames in hardware.
+ */
struct ieee80211_clone_params {
char icp_parent[IFNAMSIZ]; /* parent device */
- int icp_opmode; /* operating mode */
+ uint16_t icp_opmode; /* operating mode */
+ uint16_t icp_flags; /* see below */
+ uint8_t icp_bssid[IEEE80211_ADDR_LEN]; /* for WDS links */
+ uint8_t icp_macaddr[IEEE80211_ADDR_LEN];/* local address */
};
+#define IEEE80211_CLONE_BSSID 0x0001 /* allocate unique mac/bssid */
+#define IEEE80211_CLONE_NOBEACONS 0x0002 /* don't setup beacon timers */
+#define IEEE80211_CLONE_WDSLEGACY 0x0004 /* legacy WDS processing */
+#define IEEE80211_CLONE_MACADDR 0x0008 /* use specified mac addr */
#endif /* __FreeBSD__ */
#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff --git a/sys/net80211/ieee80211_monitor.c b/sys/net80211/ieee80211_monitor.c
new file mode 100644
index 0000000..b1e98eb
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Monitor mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_monitor.h>
+
+static void monitor_vattach(struct ieee80211vap *);
+static int monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_monitor_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_MONITOR] = monitor_vattach;
+}
+
+void
+ieee80211_monitor_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+monitor_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+monitor_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = monitor_newstate;
+ vap->iv_input = monitor_input;
+ vap->iv_opdetach = monitor_vdetach;
+}
+
+/*
+ * IEEE80211_M_MONITOR vap state machine handler.
+ */
+static int
+monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (nstate == IEEE80211_S_RUN) {
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_create_ibss(vap, ic->ic_curchan);
+ break;
+ default:
+ break;
+ }
+ /*
+ * NB: this shouldn't be here but many people use
+ * monitor mode for raw packets; once we switch
+ * them over to adhoc demo mode remove this.
+ */
+ ieee80211_node_authorize(vap->iv_bss);
+ }
+ return 0;
+}
+
+/*
+ * Process a received frame in monitor mode.
+ */
+static int
+monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ return -1;
+}
diff --git a/sys/net80211/ieee80211_monitor.h b/sys/net80211/ieee80211_monitor.h
new file mode 100644
index 0000000..d7dd8e9
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_MONITOR_H_
+#define _NET80211_IEEE80211_MONITOR_H_
+
+/*
+ * Monitor implementation definitions.
+ */
+void ieee80211_monitor_attach(struct ieee80211com *);
+void ieee80211_monitor_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_MONITOR_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 0ed8b79..58cb33f 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -40,18 +42,22 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
#include <net/bpf.h>
/*
* Association id's are managed with a bit vector.
*/
-#define IEEE80211_AID_SET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_CLR(b, w) \
- ((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_ISSET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_SET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \
+ (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_CLR(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \
+ ~(1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_ISSET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
#ifdef IEEE80211_DEBUG_REFCNT
#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line
@@ -64,117 +70,112 @@ static int ieee80211_sta_join1(struct ieee80211_node *);
static struct ieee80211_node *node_alloc(struct ieee80211_node_table *);
static void node_cleanup(struct ieee80211_node *);
static void node_free(struct ieee80211_node *);
+static void node_age(struct ieee80211_node *);
static int8_t node_getrssi(const struct ieee80211_node *);
static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *);
+static void node_getmimoinfo(const struct ieee80211_node *,
+ struct ieee80211_mimo_info *);
-static void ieee80211_setup_node(struct ieee80211_node_table *,
- struct ieee80211_node *, const uint8_t *);
static void _ieee80211_free_node(struct ieee80211_node *);
static void ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt, const char *name,
int inact, int keymaxix);
-static void ieee80211_node_table_reset(struct ieee80211_node_table *);
+static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ struct ieee80211vap *);
+static void ieee80211_node_reclaim(struct ieee80211_node *);
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
static void ieee80211_erp_timeout(struct ieee80211com *);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
void
ieee80211_node_attach(struct ieee80211com *ic)
{
+ ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+ IEEE80211_INACT_INIT, ic->ic_max_keyix);
+ callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+ callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+ ieee80211_node_timeout, ic);
ic->ic_node_alloc = node_alloc;
ic->ic_node_free = node_free;
ic->ic_node_cleanup = node_cleanup;
+ ic->ic_node_age = node_age;
+ ic->ic_node_drain = node_age; /* NB: same as age */
ic->ic_node_getrssi = node_getrssi;
ic->ic_node_getsignal = node_getsignal;
+ ic->ic_node_getmimoinfo = node_getmimoinfo;
- /* default station inactivity timer setings */
- ic->ic_inact_init = IEEE80211_INACT_INIT;
- ic->ic_inact_auth = IEEE80211_INACT_AUTH;
- ic->ic_inact_run = IEEE80211_INACT_RUN;
- ic->ic_inact_probe = IEEE80211_INACT_PROBE;
-
- callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
-
- /* NB: driver should override */
- ic->ic_max_aid = IEEE80211_AID_DEF;
-
+ /*
+ * Set flags to be propagated to all vap's;
+ * these define default behaviour/configuration.
+ */
ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */
}
void
-ieee80211_node_lateattach(struct ieee80211com *ic)
+ieee80211_node_detach(struct ieee80211com *ic)
{
- struct ieee80211_rsnparms *rsn;
- if (ic->ic_max_aid > IEEE80211_AID_MAX)
- ic->ic_max_aid = IEEE80211_AID_MAX;
- MALLOC(ic->ic_aid_bitmap, uint32_t *,
- howmany(ic->ic_max_aid, 32) * sizeof(uint32_t),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (ic->ic_aid_bitmap == NULL) {
- /* XXX no way to recover */
- printf("%s: no memory for AID bitmap!\n", __func__);
- ic->ic_max_aid = 0;
- }
+ callout_drain(&ic->ic_inact);
+ ieee80211_node_table_cleanup(&ic->ic_sta);
+}
- ieee80211_node_table_init(ic, &ic->ic_sta, "station",
- IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix);
+void
+ieee80211_node_vattach(struct ieee80211vap *vap)
+{
+ /* NB: driver can override */
+ vap->iv_max_aid = IEEE80211_AID_DEF;
- ieee80211_reset_bss(ic);
- /*
- * Setup "global settings" in the bss node so that
- * each new station automatically inherits them.
- */
- rsn = &ic->ic_bss->ni_rsn;
- /* WEP, TKIP, and AES-CCM are always supported */
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
- if (ic->ic_caps & IEEE80211_C_AES)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
- if (ic->ic_caps & IEEE80211_C_CKIP)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
- /*
- * Default unicast cipher to WEP for 802.1x use. If
- * WPA is enabled the management code will set these
- * values to reflect.
- */
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastkeylen = 104 / NBBY;
- /*
- * WPA says the multicast cipher is the lowest unicast
- * cipher supported. But we skip WEP which would
- * otherwise be used based on this criteria.
- */
- rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
- rsn->rsn_mcastkeylen = 128 / NBBY;
+ /* default station inactivity timer setings */
+ vap->iv_inact_init = IEEE80211_INACT_INIT;
+ vap->iv_inact_auth = IEEE80211_INACT_AUTH;
+ vap->iv_inact_run = IEEE80211_INACT_RUN;
+ vap->iv_inact_probe = IEEE80211_INACT_PROBE;
+}
- /*
- * We support both WPA-PSK and 802.1x; the one used
- * is determined by the authentication mode and the
- * setting of the PSK state.
- */
- rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+void
+ieee80211_node_latevattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /* XXX should we allow max aid to be zero? */
+ if (vap->iv_max_aid < IEEE80211_AID_MIN) {
+ vap->iv_max_aid = IEEE80211_AID_MIN;
+ if_printf(vap->iv_ifp,
+ "WARNING: max aid too small, changed to %d\n",
+ vap->iv_max_aid);
+ }
+ MALLOC(vap->iv_aid_bitmap, uint32_t *,
+ howmany(vap->iv_max_aid, 32) * sizeof(uint32_t),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (vap->iv_aid_bitmap == NULL) {
+ /* XXX no way to recover */
+ printf("%s: no memory for AID bitmap, max aid %d!\n",
+ __func__, vap->iv_max_aid);
+ vap->iv_max_aid = 0;
+ }
+ }
+
+ ieee80211_reset_bss(vap);
- ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode);
+ vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode);
}
void
-ieee80211_node_detach(struct ieee80211com *ic)
+ieee80211_node_vdetach(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
- if (ic->ic_bss != NULL) {
- ieee80211_free_node(ic->ic_bss);
- ic->ic_bss = NULL;
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ if (vap->iv_bss != NULL) {
+ ieee80211_free_node(vap->iv_bss);
+ vap->iv_bss = NULL;
}
- ieee80211_node_table_cleanup(&ic->ic_sta);
- if (ic->ic_aid_bitmap != NULL) {
- FREE(ic->ic_aid_bitmap, M_80211_NODE);
- ic->ic_aid_bitmap = NULL;
+ if (vap->iv_aid_bitmap != NULL) {
+ FREE(vap->iv_aid_bitmap, M_80211_NODE);
+ vap->iv_aid_bitmap = NULL;
}
}
@@ -185,20 +186,16 @@ ieee80211_node_detach(struct ieee80211com *ic)
void
ieee80211_node_authorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags |= IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_run;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_run;
ni->ni_inact = ni->ni_inact_reload;
}
void
ieee80211_node_unauthorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags &= ~IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_auth;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_auth;
if (ni->ni_inact > ni->ni_inact_reload)
ni->ni_inact = ni->ni_inact_reload;
}
@@ -206,18 +203,16 @@ ieee80211_node_unauthorize(struct ieee80211_node *ni)
/*
* Set/change the channel. The rate set is also updated as
* to insure a consistent view by drivers.
+ * XXX should be private but hostap needs it to deal with CSA
*/
-static void
-ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
+void
+ieee80211_node_set_chan(struct ieee80211_node *ni,
+ struct ieee80211_channel *chan)
{
- struct ieee80211_channel *chan = ic->ic_bsschan;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel"));
-#if 0
- KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup"));
-#else
- if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */
- chan = ic->ic_curchan;
-#endif
ni->ni_chan = chan;
if (IEEE80211_IS_CHAN_HT(chan)) {
/*
@@ -232,31 +227,6 @@ ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
ni->ni_rates = *ieee80211_get_suprates(ic, chan);
}
-/*
- * Probe the curent channel, if allowed, while scanning.
- * If the channel is not marked passive-only then send
- * a probe request immediately. Otherwise mark state and
- * listen for beacons on the channel; if we receive something
- * then we'll transmit a probe request.
- */
-void
-ieee80211_probe_curchan(struct ieee80211com *ic, int force)
-{
- struct ifnet *ifp = ic->ic_ifp;
-
- if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 || force) {
- /*
- * XXX send both broadcast+directed probe request
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- } else
- ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
-}
-
static __inline void
copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
{
@@ -264,90 +234,87 @@ copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
nbss->ni_authmode = obss->ni_authmode;
nbss->ni_txpower = obss->ni_txpower;
nbss->ni_vlan = obss->ni_vlan;
- nbss->ni_rsn = obss->ni_rsn;
/* XXX statistics? */
+ /* XXX legacy WDS bssid? */
}
void
-ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
+ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
{
- struct ieee80211_node_table *nt;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: creating ibss\n", __func__);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: creating ibss on channel %u\n", __func__,
+ ieee80211_chan2ieee(ic, chan));
- /*
- * Create the station/neighbor table. Note that for adhoc
- * mode we make the initial inactivity timer longer since
- * we create nodes only through discovery and they typically
- * are long-lived associations.
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- nt->nt_name = "station";
- nt->nt_inact_init = ic->ic_inact_init;
- } else {
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- }
- IEEE80211_NODE_UNLOCK(nt);
-
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
if (ni == NULL) {
/* XXX recovery? */
return;
}
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- ni->ni_esslen = ic->ic_des_ssid[0].len;
- memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen);
- if (ic->ic_bss != NULL)
- copy_bss(ni, ic->ic_bss);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ if (vap->iv_bss != NULL)
+ copy_bss(ni, vap->iv_bss);
ni->ni_intval = ic->ic_bintval;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
if (ic->ic_phytype == IEEE80211_T_FH) {
ni->ni_fhdwell = 200; /* XXX */
ni->ni_fhindex = 1;
}
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ic->ic_flags |= IEEE80211_F_SIBSS;
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ vap->iv_flags |= IEEE80211_F_SIBSS;
ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else {
get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN);
/* clear group bit, add local bit */
ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02;
}
- } else if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else
memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN);
}
/*
* Fix the channel and related attributes.
*/
+ /* clear DFS CAC state on previous channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bsschan->ic_freq != chan->ic_freq &&
+ IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan))
+ ieee80211_dfs_cac_clear(ic, ic->ic_bsschan);
ic->ic_bsschan = chan;
- ieee80211_node_set_chan(ic, ni);
+ ieee80211_node_set_chan(ni, chan);
ic->ic_curmode = ieee80211_chan2mode(chan);
/*
- * Do mode-specific rate setup.
+ * Do mode-specific setup.
*/
if (IEEE80211_IS_CHAN_FULL(chan)) {
if (IEEE80211_IS_CHAN_ANYG(chan)) {
/*
- * Use a mixed 11b/11g rate set.
+ * Use a mixed 11b/11g basic rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
- IEEE80211_MODE_11G);
+ ieee80211_setbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11G);
+ if (vap->iv_flags & IEEE80211_F_PUREG) {
+ /*
+ * Also mark OFDM rates basic so 11b
+ * stations do not join (WiFi compliance).
+ */
+ ieee80211_addbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11A);
+ }
} else if (IEEE80211_IS_CHAN_B(chan)) {
/*
* Force pure 11b rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
+ ieee80211_setbasicrates(&ni->ni_rates,
IEEE80211_MODE_11B);
}
}
@@ -362,23 +329,25 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
* etc. state).
*/
void
-ieee80211_reset_bss(struct ieee80211com *ic)
+ieee80211_reset_bss(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni, *obss;
- callout_drain(&ic->ic_inact);
- ieee80211_node_table_reset(&ic->ic_sta);
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ /* XXX multi-bss: wrong */
ieee80211_reset_erp(ic);
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
KASSERT(ni != NULL, ("unable to setup inital BSS node"));
- obss = ic->ic_bss;
- ic->ic_bss = ieee80211_ref_node(ni);
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
if (obss != NULL) {
copy_bss(ni, obss);
ni->ni_intval = ic->ic_bintval;
ieee80211_free_node(obss);
- }
+ } else
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
}
static int
@@ -399,20 +368,21 @@ match_ssid(const struct ieee80211_node *ni,
* Test a node for suitability/compatibility.
*/
static int
-check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
return 0;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
return 0;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
return 0;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
return 0;
} else {
@@ -424,11 +394,11 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
return 0;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
return 0;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
return 0;
return 1;
}
@@ -438,22 +408,23 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
* Display node suitability/compatibility.
*/
static void
-check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
fail |= 0x01;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= 0x02;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= 0x02;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= 0x04;
} else {
@@ -465,18 +436,17 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
fail |= 0x10;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
fail |= 0x20;
printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr));
printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' ');
printf(" %3d%c",
ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' ');
- printf(" %+4d", ni->ni_rssi);
printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
fail & 0x08 ? '!' : ' ');
printf(" %4s%c",
@@ -507,25 +477,28 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
int
ieee80211_ibss_merge(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+#ifdef IEEE80211_DEBUG
struct ieee80211com *ic = ni->ni_ic;
+#endif
- if (ni == ic->ic_bss ||
- IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+ if (ni == vap->iv_bss ||
+ IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
/* unchanged, nothing to do */
return 0;
}
- if (!check_bss(ic, ni)) {
+ if (!check_bss(vap, ni)) {
/* capabilities mismatch */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: merge failed, capabilities mismatch\n", __func__);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_assoc(ic))
- check_bss_debug(ic, ni);
+ if (ieee80211_msg_assoc(vap))
+ check_bss_debug(vap, ni);
#endif
- ic->ic_stats.is_ibss_capmismatch++;
+ vap->iv_stats.is_ibss_capmismatch++;
return 0;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: new bssid %s: %s preamble, %s slot time%s\n", __func__,
ether_sprintf(ni->ni_bssid),
ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -536,13 +509,72 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
}
/*
- * Change the bss channel.
+ * Calculate HT channel promotion flags for all vaps.
+ * This assumes ni_chan have been setup for each vap.
+ */
+static int
+gethtadjustflags(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+ int flags;
+
+ flags = 0;
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_state < IEEE80211_S_RUN)
+ continue;
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_WDS:
+ case IEEE80211_M_STA:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_IBSS:
+ flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan);
+ break;
+ default:
+ break;
+ }
+ }
+ return flags;
+}
+
+/*
+ * Check if the current channel needs to change based on whether
+ * any vap's are using HT20/HT40. This is used sync the state of
+ * ic_curchan after a channel width change on a running vap.
*/
void
-ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
+ieee80211_sync_curchan(struct ieee80211com *ic)
{
- ic->ic_bsschan = c;
- ic->ic_curchan = ic->ic_bsschan;
+ struct ieee80211_channel *c;
+
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic));
+ if (c != ic->ic_curchan) {
+ ic->ic_curchan = c;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+ ic->ic_set_channel(ic);
+ }
+}
+
+/*
+ * Change the current channel. The request channel may be
+ * promoted if other vap's are operating with HT20/HT40.
+ */
+void
+ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ int flags = gethtadjustflags(ic);
+ /*
+ * Check for channel promotion required to support the
+ * set of running vap's. This assumes we are called
+ * after ni_chan is setup for each vap.
+ */
+ /* NB: this assumes IEEE80211_FEXT_USEHT40 > IEEE80211_FEXT_HT */
+ if (flags > ieee80211_htchanflags(c))
+ c = ieee80211_ht_adjust_channel(ic, c, flags);
+ }
+ ic->ic_bsschan = ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_set_channel(ic);
}
@@ -554,61 +586,49 @@ ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
static int
ieee80211_sta_join1(struct ieee80211_node *selbs)
{
+ struct ieee80211vap *vap = selbs->ni_vap;
struct ieee80211com *ic = selbs->ni_ic;
struct ieee80211_node *obss;
int canreassoc;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- struct ieee80211_node_table *nt;
- /*
- * Fillin the neighbor table; it will already
- * exist if we are simply switching mastership.
- * XXX ic_sta always setup so this is unnecessary?
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- IEEE80211_NODE_UNLOCK(nt);
- }
-
/*
* Committed to selbs, setup state.
*/
- obss = ic->ic_bss;
+ obss = vap->iv_bss;
/*
* Check if old+new node have the same address in which
* case we can reassociate when operating in sta mode.
*/
canreassoc = (obss != NULL &&
- ic->ic_state == IEEE80211_S_RUN &&
+ vap->iv_state == IEEE80211_S_RUN &&
IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
- ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */
+ vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */
if (obss != NULL) {
copy_bss(selbs, obss);
- ieee80211_free_node(obss);
+ ieee80211_node_reclaim(obss);
+ obss = NULL; /* NB: guard against later use */
}
/*
* Delete unusable rates; we've already checked
* that the negotiated rate set is acceptable.
*/
- ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
+ ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates,
IEEE80211_F_DODEL | IEEE80211_F_JOIN);
- ieee80211_setbsschan(ic, selbs->ni_chan);
+ ieee80211_setcurchan(ic, selbs->ni_chan);
/*
* Set the erp state (mostly the slot time) to deal with
* the auto-select case; this should be redundant if the
* mode is locked.
*/
ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic);
+ ieee80211_wme_initparams(vap);
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
if (canreassoc) {
/* Reassociate */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
} else {
/*
* Act as if we received a DEAUTH frame in case we
@@ -616,21 +636,22 @@ ieee80211_sta_join1(struct ieee80211_node *selbs)
* us to try to re-authenticate if we are operating
* as a station.
*/
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
IEEE80211_FC0_SUBTYPE_DEAUTH);
}
} else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
return 1;
}
int
-ieee80211_sta_join(struct ieee80211com *ic,
+ieee80211_sta_join(struct ieee80211vap *vap,
const struct ieee80211_scan_entry *se)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr);
if (ni == NULL) {
/* XXX msg */
return 0;
@@ -651,23 +672,21 @@ ieee80211_sta_join(struct ieee80211com *ic,
ni->ni_fhdwell = se->se_fhdwell;
ni->ni_fhindex = se->se_fhindex;
ni->ni_erp = se->se_erp;
- ni->ni_rssi = se->se_rssi;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi);
ni->ni_noise = se->se_noise;
- if (se->se_htcap_ie != NULL) {
- ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
- ieee80211_parse_htcap(ni, ni->ni_htcap_ie);
+
+ if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ if (ni->ni_ies.htcap_ie != NULL)
+ ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie);
+ if (ni->ni_ies.htinfo_ie != NULL)
+ ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie);
}
- if (se->se_wpa_ie != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
- if (se->se_rsn_ie != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie);
- if (se->se_wme_ie != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie);
- if (se->se_ath_ie != NULL)
- ieee80211_saveath(ni, se->se_ath_ie);
-
- ic->ic_dtim_period = se->se_dtimperiod;
- ic->ic_dtim_count = 0;
+
+ vap->iv_dtim_period = se->se_dtimperiod;
+ vap->iv_dtim_count = 0;
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
@@ -681,10 +700,26 @@ ieee80211_sta_join(struct ieee80211com *ic,
* be passed in with a held reference.
*/
void
-ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_sta_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+
ic->ic_node_cleanup(ni);
- ieee80211_notify_node_leave(ic, ni);
+ ieee80211_notify_node_leave(ni);
+}
+
+/*
+ * Send a deauthenticate frame and drop the station.
+ */
+void
+ieee80211_node_deauth(struct ieee80211_node *ni, int reason)
+{
+ /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */
+ ieee80211_ref_node(ni);
+ if (ni->ni_associd != 0)
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_node_leave(ni);
+ ieee80211_free_node(ni);
}
static struct ieee80211_node *
@@ -698,6 +733,80 @@ node_alloc(struct ieee80211_node_table *nt)
}
/*
+ * Initialize an ie blob with the specified data. If previous
+ * data exists re-use the data block. As a side effect we clear
+ * all references to specific ie's; the caller is required to
+ * recalculate them.
+ */
+int
+ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len)
+{
+ /* NB: assumes data+len are the last fields */
+ memset(ies, 0, offsetof(struct ieee80211_ies, data));
+ if (ies->data != NULL && ies->len != len) {
+ /* data size changed */
+ FREE(ies->data, M_80211_NODE_IE);
+ ies->data = NULL;
+ }
+ if (ies->data == NULL) {
+ MALLOC(ies->data, uint8_t *, len, M_80211_NODE_IE, M_NOWAIT);
+ if (ies->data == NULL) {
+ ies->len = 0;
+ /* NB: pointers have already been zero'd above */
+ return 0;
+ }
+ }
+ memcpy(ies->data, data, len);
+ ies->len = len;
+ return 1;
+}
+
+/*
+ * Reclaim storage for an ie blob.
+ */
+void
+ieee80211_ies_cleanup(struct ieee80211_ies *ies)
+{
+ if (ies->data != NULL)
+ FREE(ies->data, M_80211_NODE_IE);
+}
+
+/*
+ * Expand an ie blob data contents and to fillin individual
+ * ie pointers. The data blob is assumed to be well-formed;
+ * we don't do any validity checking of ie lengths.
+ */
+void
+ieee80211_ies_expand(struct ieee80211_ies *ies)
+{
+ uint8_t *ie;
+ int ielen;
+
+ ie = ies->data;
+ ielen = ies->len;
+ while (ielen > 0) {
+ switch (ie[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(ie))
+ ies->wpa_ie = ie;
+ else if (iswmeoui(ie))
+ ies->wme_ie = ie;
+ else if (isatherosoui(ie))
+ ies->ath_ie = ie;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ ies->rsn_ie = ie;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ ies->htcap_ie = ie;
+ break;
+ }
+ ielen -= 2 + ie[1];
+ ie += 2 + ie[1];
+ }
+}
+
+/*
* Reclaim any resources in a node and reset any critical
* state. Typically nodes are free'd immediately after,
* but in some cases the storage may be reused so we need
@@ -707,17 +816,16 @@ static void
node_cleanup(struct ieee80211_node *ni)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int i;
/* NB: preserve ni_table */
if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
- if (ic->ic_opmode != IEEE80211_M_STA)
- ic->ic_ps_sta--;
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ vap->iv_ps_sta--;
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] power save mode off, %u sta's in ps mode\n",
- ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
}
/*
* Cleanup any HT-related state.
@@ -735,8 +843,8 @@ node_cleanup(struct ieee80211_node *ni)
/*
* Drain power save queue and, if needed, clear TIM.
*/
- if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ if (ieee80211_node_saveq_drain(ni) != 0 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
ni->ni_associd = 0;
if (ni->ni_challenge != NULL) {
@@ -773,39 +881,80 @@ node_free(struct ieee80211_node *ni)
struct ieee80211com *ic = ni->ni_ic;
ic->ic_node_cleanup(ni);
- if (ni->ni_wpa_ie != NULL)
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- if (ni->ni_rsn_ie != NULL)
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- if (ni->ni_wme_ie != NULL)
- FREE(ni->ni_wme_ie, M_80211_NODE);
- if (ni->ni_ath_ie != NULL)
- FREE(ni->ni_ath_ie, M_80211_NODE);
+ ieee80211_ies_cleanup(&ni->ni_ies);
IEEE80211_NODE_SAVEQ_DESTROY(ni);
+ IEEE80211_NODE_WDSQ_DESTROY(ni);
FREE(ni, M_80211_NODE);
}
+static void
+node_age(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+#if 0
+ IEEE80211_NODE_LOCK_ASSERT(&ic->ic_sta);
+#endif
+ /*
+ * Age frames on the power save queue.
+ */
+ if (ieee80211_node_saveq_age(ni) != 0 &&
+ IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
+ vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ /*
+ * Age frames on the wds pending queue.
+ */
+ if (IEEE80211_NODE_WDSQ_QLEN(ni) != 0)
+ ieee80211_node_wdsq_age(ni);
+ /*
+ * Age out HT resources (e.g. frames on the
+ * A-MPDU reorder queues).
+ */
+ if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT))
+ ieee80211_ht_node_age(ni);
+}
+
static int8_t
node_getrssi(const struct ieee80211_node *ni)
{
- return ni->ni_rssi;
+ uint32_t avgrssi = ni->ni_avgrssi;
+ int32_t rssi;
+
+ if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER)
+ return 0;
+ rssi = IEEE80211_RSSI_GET(avgrssi);
+ return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
}
static void
node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
{
- *rssi = ni->ni_rssi;
+ *rssi = node_getrssi(ni);
*noise = ni->ni_noise;
}
static void
-ieee80211_setup_node(struct ieee80211_node_table *nt,
- struct ieee80211_node *ni, const uint8_t *macaddr)
+node_getmimoinfo(const struct ieee80211_node *ni,
+ struct ieee80211_mimo_info *info)
+{
+ /* XXX zero data? */
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211_node_table *nt,
+ struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
int hash;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ ni = ic->ic_node_alloc(nt);
+ if (ni == NULL) {
+ vap->iv_stats.is_rx_nodealloc++;
+ return NULL;
+ }
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(macaddr), nt->nt_name);
@@ -815,31 +964,22 @@ ieee80211_setup_node(struct ieee80211_node_table *nt,
ni->ni_chan = IEEE80211_CHAN_ANYC;
ni->ni_authmode = IEEE80211_AUTH_OPEN;
ni->ni_txpower = ic->ic_txpowlimit; /* max power */
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
ni->ni_inact_reload = nt->nt_inact_init;
ni->ni_inact = ni->ni_inact_reload;
ni->ni_ath_defkeyix = 0x7fff;
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
IEEE80211_NODE_LOCK(nt);
TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
ni->ni_table = nt;
+ ni->ni_vap = vap;
ni->ni_ic = ic;
IEEE80211_NODE_UNLOCK(nt);
-}
-
-struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
-{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
- if (ni != NULL)
- ieee80211_setup_node(nt, ni, macaddr);
- else
- ic->ic_stats.is_rx_nodealloc++;
return ni;
}
@@ -850,65 +990,129 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* once the send completes.
*/
struct ieee80211_node *
-ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_tmp_node(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
ni = ic->ic_node_alloc(&ic->ic_sta);
if (ni != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s>\n", __func__, ni, ether_sprintf(macaddr));
+ ni->ni_table = NULL; /* NB: pedantic */
+ ni->ni_ic = ic; /* NB: needed to set channel */
+ ni->ni_vap = vap;
+
IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
ieee80211_node_initref(ni); /* mark referenced */
- ni->ni_txpower = ic->ic_bss->ni_txpower;
/* NB: required by ieee80211_fix_rate */
- ieee80211_node_set_chan(ic, ni);
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey,
IEEE80211_KEYIX_NONE);
/* XXX optimize away */
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
-
- ni->ni_table = NULL; /* NB: pedantic */
- ni->ni_ic = ic;
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
} else {
/* XXX msg */
- ic->ic_stats.is_rx_nodealloc++;
+ vap->iv_stats.is_rx_nodealloc++;
}
return ni;
}
struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr);
if (ni != NULL) {
- ieee80211_setup_node(nt, ni, macaddr);
/*
- * Inherit from ic_bss.
+ * Inherit from iv_bss.
*/
- ni->ni_authmode = ic->ic_bss->ni_authmode;
- ni->ni_txpower = ic->ic_bss->ni_txpower;
- ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
- ieee80211_node_set_chan(ic, ni);
- ni->ni_rsn = ic->ic_bss->ni_rsn;
- } else
- ic->ic_stats.is_rx_nodealloc++;
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan; /* XXX?? */
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ }
return ni;
}
-static struct ieee80211_node *
+/*
+ * Create a bss node for a legacy WDS vap. The far end does
+ * not associate so we just create create a new node and
+ * simulate an association. The caller is responsible for
+ * installing the node as the bss node and handling any further
+ * setup work like authorizing the port.
+ */
+struct ieee80211_node *
+ieee80211_node_create_wds(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+
+ /* XXX check if node already in sta table? */
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid);
+ if (ni != NULL) {
+ ni->ni_wdsvap = vap;
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bssid);
+ /*
+ * Inherit any manually configured settings.
+ */
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan;
+ ieee80211_node_set_chan(ni, chan);
+ /* NB: propagate ssid so available to WPA supplicant */
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ /* NB: no associd for peer */
+ /*
+ * There are no management frames to use to
+ * discover neighbor capabilities, so blindly
+ * propagate the local configuration.
+ */
+ if (vap->iv_flags & IEEE80211_F_WME)
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ if (vap->iv_flags & IEEE80211_F_FF)
+ ni->ni_flags |= IEEE80211_NODE_FF;
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ /*
+ * Device is HT-capable and HT is enabled for
+ * the vap; setup HT operation. On return
+ * ni_chan will be adjusted to an HT channel.
+ */
+ ieee80211_ht_wds_init(ni);
+ } else {
+ struct ieee80211_channel *c = ni->ni_chan;
+ /*
+ * Force a legacy channel to be used.
+ */
+ c = ieee80211_find_channel(ic,
+ c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT);
+ KASSERT(c != NULL, ("no legacy channel, %u/%x",
+ ni->ni_chan->ic_freq, ni->ni_chan->ic_flags));
+ ni->ni_chan = c;
+ }
+ }
+ return ni;
+}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-_ieee80211_find_node(struct ieee80211_node_table *nt,
- const uint8_t *macaddr)
+ieee80211_find_node_locked(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
@@ -921,7 +1125,7 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
ieee80211_ref_node(ni); /* mark referenced */
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__,
func, line,
ni, ether_sprintf(ni->ni_macaddr),
@@ -932,23 +1136,73 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
}
return NULL;
}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-#define _ieee80211_find_node(nt, mac) \
- _ieee80211_find_node_debug(nt, mac, func, line)
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
+{
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+#endif
+{
+ struct ieee80211_node *ni;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+ if (ni->ni_vap == vap &&
+ IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_ref_node(ni); /* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+ func, line,
+ ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+#endif
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_vap_node(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
IEEE80211_NODE_LOCK(nt);
- ni = _ieee80211_find_node(nt, macaddr);
+ ni = ieee80211_find_vap_node_locked(nt, vap, macaddr);
IEEE80211_NODE_UNLOCK(nt);
return ni;
}
@@ -960,21 +1214,20 @@ ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* it's private state.
*/
struct ieee80211_node *
-ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(macaddr));
- ni = ieee80211_dup_bss(nt, macaddr);
+ ni = ieee80211_dup_bss(vap, macaddr);
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
/* XXX no rate negotiation; just dup */
- ni->ni_rates = ic->ic_bss->ni_rates;
- if (ic->ic_newassoc != NULL)
- ic->ic_newassoc(ni, 1);
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ ni->ni_rates = vap->iv_bss->ni_rates;
+ if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc demo mode there are no management
* frames to use to discover neighbor capabilities,
@@ -982,11 +1235,13 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
* so we can do interesting things (e.g. use
* WME to disable ACK's).
*/
- if (ic->ic_flags & IEEE80211_F_WME)
+ if (vap->iv_flags & IEEE80211_F_WME)
ni->ni_flags |= IEEE80211_NODE_QOS;
- if (ic->ic_flags & IEEE80211_F_FF)
+ if (vap->iv_flags & IEEE80211_F_FF)
ni->ni_flags |= IEEE80211_NODE_FF;
}
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
/* XXX not right for 802.1x/WPA */
ieee80211_node_authorize(ni);
}
@@ -1009,14 +1264,12 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
ni->ni_fhindex = sp->fhindex;
ni->ni_erp = sp->erp;
ni->ni_timoff = sp->timoff;
- if (sp->wme != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
- if (sp->wpa != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
- if (sp->rsn != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn);
- if (sp->ath != NULL)
- ieee80211_saveath(ni, sp->ath);
+
+ if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ }
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, sp->rates, sp->xrates,
@@ -1031,16 +1284,18 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
* driver has an opportunity to setup it's private state.
*/
struct ieee80211_node *
-ieee80211_add_neighbor(struct ieee80211com *ic,
+ieee80211_add_neighbor(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const struct ieee80211_scanparams *sp)
{
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2));
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
ieee80211_init_neighbor(ni, wh, sp);
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
@@ -1077,16 +1332,13 @@ ieee80211_find_rxnode(struct ieee80211com *ic,
struct ieee80211_node_table *nt;
struct ieee80211_node *ni;
- /* XXX check ic_bss first in station mode */
/* XXX 4-address frames? */
nt = &ic->ic_sta;
IEEE80211_NODE_LOCK(nt);
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
IEEE80211_NODE_UNLOCK(nt);
return ni;
@@ -1121,12 +1373,10 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
ni = NULL;
if (ni == NULL) {
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
- if (nt->nt_keyixmap != NULL) {
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
+ if (ni != NULL && nt->nt_keyixmap != NULL) {
/*
* If the station has a unicast key cache slot
* assigned update the key->node mapping table.
@@ -1135,7 +1385,8 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
/* XXX can keyixmap[keyix] != NULL? */
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == NULL) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
"%s: add key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)+1);
@@ -1158,13 +1409,15 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
*/
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr,
+ieee80211_find_txnode_debug(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line)
#else
-ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_find_txnode(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta;
struct ieee80211_node *ni;
/*
@@ -1175,11 +1428,13 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
*/
/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ vap->iv_opmode == IEEE80211_M_WDS ||
+ IEEE80211_IS_MULTICAST(macaddr))
+ ni = ieee80211_ref_node(vap->iv_bss);
else {
- ni = _ieee80211_find_node(nt, macaddr);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
(ni != NULL && ni->ni_associd == 0)) {
/*
* Station is not associated; don't permit the
@@ -1193,91 +1448,44 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
IEEE80211_NODE_UNLOCK(nt);
if (ni == NULL) {
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc mode cons up a node for the destination.
* Note that we need an additional reference for the
- * caller to be consistent with _ieee80211_find_node.
+ * caller to be consistent with
+ * ieee80211_find_node_locked.
*/
- ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+ ni = ieee80211_fakeup_adhoc_node(vap, macaddr);
if (ni != NULL)
(void) ieee80211_ref_node(ni);
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] no node, discard frame (%s)\n",
- ether_sprintf(macaddr), __func__);
- ic->ic_stats.is_tx_nonode++;
- }
- }
- return ni;
-}
-
-/*
- * Like find but search based on the ssid too.
- */
-struct ieee80211_node *
-#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid,
- const char *func, int line)
-#else
-ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid)
-#endif
-{
-#define MATCH_SSID(ni, ssid, ssidlen) \
- (ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
- static const uint8_t zeromac[IEEE80211_ADDR_LEN];
- struct ieee80211_node *ni;
- int hash;
-
- IEEE80211_NODE_LOCK(nt);
- /*
- * A mac address that is all zero means match only the ssid;
- * otherwise we must match both.
- */
- if (IEEE80211_ADDR_EQ(macaddr, zeromac)) {
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- if (MATCH_SSID(ni, ssid, ssidlen))
- break;
- }
- } else {
- hash = IEEE80211_NODE_HASH(macaddr);
- LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
- if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
- MATCH_SSID(ni, ssid, ssidlen))
- break;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr,
+ "no node, discard frame (%s)", __func__);
+ vap->iv_stats.is_tx_nonode++;
}
}
- if (ni != NULL) {
- ieee80211_ref_node(ni); /* mark referenced */
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr),
- ieee80211_node_refcnt(ni));
- }
- IEEE80211_NODE_UNLOCK(nt);
return ni;
-#undef MATCH_SSID
}
static void
_ieee80211_free_node(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(ni->ni_macaddr),
nt != NULL ? nt->nt_name : "<gone>");
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ if (vap->iv_aid_bitmap != NULL)
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
if (nt != NULL) {
TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
LIST_REMOVE(ni, ni_hash);
}
- ic->ic_node_free(ni);
+ vap->iv_ic->ic_node_free(ni);
}
void
@@ -1290,7 +1498,7 @@ ieee80211_free_node(struct ieee80211_node *ni)
struct ieee80211_node_table *nt = ni->ni_table;
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
#endif
@@ -1310,7 +1518,8 @@ ieee80211_free_node(struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
"%s: %p<%s> clear key map entry", __func__,
ni, ether_sprintf(ni->ni_macaddr));
nt->nt_keyixmap[keyix] = NULL;
@@ -1331,8 +1540,9 @@ ieee80211_free_node(struct ieee80211_node *ni)
int
ieee80211_node_delucastkey(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap = ni->ni_vap;
+ /* XXX is ni_table safe? */
+ struct ieee80211_node_table *nt = &ni->ni_ic->ic_sta;
struct ieee80211_node *nikey;
ieee80211_keyix keyix;
int isowned, status;
@@ -1353,19 +1563,19 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni)
if (!isowned)
IEEE80211_NODE_LOCK(nt);
keyix = ni->ni_ucastkey.wk_rxkeyix;
- status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ status = ieee80211_crypto_delkey(vap, &ni->ni_ucastkey);
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) {
nikey = nt->nt_keyixmap[keyix];
nt->nt_keyixmap[keyix] = NULL;;
} else
nikey = NULL;
if (!isowned)
- IEEE80211_NODE_UNLOCK(&ic->ic_sta);
+ IEEE80211_NODE_UNLOCK(nt);
if (nikey != NULL) {
KASSERT(nikey == ni,
("key map out of sync, ni %p nikey %p", ni, nikey));
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: delete key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)-1);
@@ -1386,7 +1596,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
IEEE80211_NODE_LOCK_ASSERT(nt);
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: remove %p<%s> from %s table, refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
nt->nt_name, ieee80211_node_refcnt(ni)-1);
@@ -1400,7 +1610,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: %p<%s> clear key map entry\n",
__func__, ni, ether_sprintf(ni->ni_macaddr));
nt->nt_keyixmap[keyix] = NULL;
@@ -1420,23 +1630,151 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
_ieee80211_free_node(ni);
}
+/*
+ * Reclaim a (bss) node. Decrement the refcnt and reclaim
+ * the node if the only other reference to it is in the sta
+ * table. This is effectively ieee80211_free_node followed
+ * by node_reclaim when the refcnt is 1 (after the free).
+ */
static void
-ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
+ieee80211_node_reclaim(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
+ struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s: free all nodes in %s table\n", __func__, nt->nt_name);
+ KASSERT(nt != NULL, ("reclaim node not in table"));
+
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
+#endif
+ IEEE80211_NODE_LOCK(nt);
+ if (ieee80211_node_dectestref(ni)) {
+ /*
+ * Last reference, reclaim state.
+ */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ } else if (ieee80211_node_refcnt(ni) == 1 &&
+ nt->nt_keyixmap != NULL) {
+ ieee80211_keyix keyix;
+ /*
+ * Check for a last reference in the key mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ if (keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == ni) {
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
+ "%s: %p<%s> clear key map entry", __func__,
+ ni, ether_sprintf(ni->ni_macaddr));
+ nt->nt_keyixmap[keyix] = NULL;
+ ieee80211_node_decref(ni); /* XXX needed? */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ }
+ }
+ if (nt != NULL && ieee80211_node_refcnt(ni) == 1) {
+ /*
+ * Last reference is in the sta table; complete
+ * the reclaim. This handles bss nodes being
+ * recycled: the node has two references, one for
+ * iv_bss and one for the table. After dropping
+ * the iv_bss ref above we need to reclaim the sta
+ * table reference.
+ */
+ ieee80211_node_decref(ni); /* NB: be pendantic */
+ _ieee80211_free_node(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
- while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+ struct ieee80211_node_table *nt,
+ const char *name, int inact, int keyixmax)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ nt->nt_ic = ic;
+ IEEE80211_NODE_LOCK_INIT(nt, ifp->if_xname);
+ IEEE80211_NODE_ITERATE_LOCK_INIT(nt, ifp->if_xname);
+ TAILQ_INIT(&nt->nt_node);
+ nt->nt_name = name;
+ nt->nt_scangen = 1;
+ nt->nt_inact_init = inact;
+ nt->nt_keyixmax = keyixmax;
+ if (nt->nt_keyixmax > 0) {
+ MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
+ keyixmax * sizeof(struct ieee80211_node *),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (nt->nt_keyixmap == NULL)
+ if_printf(ic->ic_ifp,
+ "Cannot allocate key index map with %u entries\n",
+ keyixmax);
+ } else
+ nt->nt_keyixmap = NULL;
+}
+
+static void
+ieee80211_node_table_reset(struct ieee80211_node_table *nt,
+ struct ieee80211vap *match)
+{
+ struct ieee80211_node *ni, *next;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) {
+ if (match != NULL && ni->ni_vap != match)
+ continue;
+ /* XXX can this happen? if so need's work */
if (ni->ni_associd != 0) {
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
+ if (vap->iv_aid_bitmap != NULL)
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
}
+ ni->ni_wdsvap = NULL; /* clear reference */
node_reclaim(nt, ni);
}
+ if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) {
+ /*
+ * Make a separate pass to clear references to this vap
+ * held by DWDS entries. They will not be matched above
+ * because ni_vap will point to the ap vap but we still
+ * need to clear ni_wdsvap when the WDS vap is destroyed
+ * and/or reset.
+ */
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next)
+ if (ni->ni_wdsvap == match)
+ ni->ni_wdsvap = NULL;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+ ieee80211_node_table_reset(nt, NULL);
+ if (nt->nt_keyixmap != NULL) {
+#ifdef DIAGNOSTIC
+ /* XXX verify all entries are NULL */
+ int i;
+ for (i = 0; i < nt->nt_keyixmax; i++)
+ if (nt->nt_keyixmap[i] != NULL)
+ printf("%s: %s[%u] still active\n", __func__,
+ nt->nt_name, i);
+#endif
+ FREE(nt->nt_keyixmap, M_80211_NODE);
+ nt->nt_keyixmap = NULL;
+ }
+ IEEE80211_NODE_ITERATE_LOCK_DESTROY(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
}
/*
@@ -1450,16 +1788,14 @@ ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
* process each node only once.
*/
static void
-ieee80211_timeout_stations(struct ieee80211_node_table *nt)
+ieee80211_timeout_stations(struct ieee80211com *ic)
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
struct ieee80211_node *ni;
- u_int gen;
- int isadhoc;
+ int gen = 0;
- isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO);
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1473,14 +1809,25 @@ restart:
* will be reclaimed when the last reference to them
* goes away (when frame xmits complete).
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_STA) &&
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
(ni->ni_flags & IEEE80211_NODE_AREF) == 0)
continue;
/*
* Free fragment if not needed anymore
* (last fragment older than 1s).
- * XXX doesn't belong here
+ * XXX doesn't belong here, move to node_age
*/
if (ni->ni_rxfrag[0] != NULL &&
ticks > ni->ni_rxfragstamp + hz) {
@@ -1492,17 +1839,17 @@ restart:
/*
* Special case ourself; we may be idle for extended periods
* of time and regardless reclaiming our state is wrong.
+ * XXX run ic_node_age
*/
- if (ni == ic->ic_bss)
+ if (ni == vap->iv_bss)
continue;
- if (ni->ni_associd != 0 || isadhoc) {
+ if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO)) {
/*
- * Age frames on the power save queue.
+ * Age/drain resources held by the station.
*/
- if (ieee80211_node_saveq_age(ni) != 0 &&
- IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
- ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ ic->ic_node_age(ni);
/*
* Probe the station before time it out. We
* send a null data frame which may not be
@@ -1515,11 +1862,11 @@ restart:
* of); this will get fixed more properly
* soon with better handling of the rate set.
*/
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
(0 < ni->ni_inact &&
- ni->ni_inact <= ic->ic_inact_probe) &&
+ ni->ni_inact <= vap->iv_inact_probe) &&
ni->ni_rates.rs_nrates != 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
ni, "%s",
"probe station due to inactivity");
@@ -1537,9 +1884,9 @@ restart:
goto restart;
}
}
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
ni->ni_inact <= 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni,
"station timed out due to inactivity "
"(refcnt %u)", ieee80211_node_refcnt(ni));
@@ -1561,45 +1908,109 @@ restart:
ieee80211_ref_node(ni);
IEEE80211_NODE_UNLOCK(nt);
if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_EXPIRE);
}
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
ieee80211_free_node(ni);
- ic->ic_stats.is_node_timeout++;
+ vap->iv_stats.is_node_timeout++;
goto restart;
}
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
+}
+
+/*
+ * Aggressively reclaim resources. This should be used
+ * only in a critical situation to reclaim mbuf resources.
+ */
+void
+ieee80211_drain(struct ieee80211com *ic)
+{
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ /*
+ * Ignore entries for which have yet to receive an
+ * authentication frame. These are transient and
+ * will be reclaimed when the last reference to them
+ * goes away (when frame xmits complete).
+ */
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
+ (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ continue;
+ /*
+ * Free fragments.
+ * XXX doesn't belong here, move to node_drain
+ */
+ if (ni->ni_rxfrag[0] != NULL) {
+ m_freem(ni->ni_rxfrag[0]);
+ ni->ni_rxfrag[0] = NULL;
+ }
+ /*
+ * Drain resources held by the station.
+ */
+ ic->ic_node_drain(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
}
+/*
+ * Per-ieee80211com inactivity timer callback.
+ */
void
ieee80211_node_timeout(void *arg)
{
struct ieee80211com *ic = arg;
- ieee80211_scan_timeout(ic);
- ieee80211_timeout_stations(&ic->ic_sta);
-
- IEEE80211_LOCK(ic);
- ieee80211_erp_timeout(ic);
- ieee80211_ht_timeout(ic);
- IEEE80211_UNLOCK(ic);
+ /*
+ * Defer timeout processing if a channel switch is pending.
+ * We typically need to be mute so not doing things that
+ * might generate frames is good to handle in one place.
+ * Supressing the station timeout processing may extend the
+ * lifetime of inactive stations (by not decrementing their
+ * idle counters) but this should be ok unless the CSA is
+ * active for an unusually long time.
+ */
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
+ ieee80211_scan_timeout(ic);
+ ieee80211_timeout_stations(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_erp_timeout(ic);
+ ieee80211_ht_timeout(ic);
+ IEEE80211_UNLOCK(ic);
+ }
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
ieee80211_node_timeout, ic);
}
void
-ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg)
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
+ ieee80211_iter_func *f, void *arg)
{
struct ieee80211_node *ni;
u_int gen;
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1615,7 +2026,7 @@ restart:
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
}
void
@@ -1633,14 +2044,14 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxfragstamp);
printf("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n",
- ni->ni_rstamp, ni->ni_rssi, ni->ni_noise,
+ ni->ni_rstamp, node_getrssi(ni), ni->ni_noise,
ni->ni_intval, ni->ni_capinfo);
printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n",
ether_sprintf(ni->ni_bssid),
ni->ni_esslen, ni->ni_essid,
ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
- printf("\tfails %u inact %u txrate %u\n",
- ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+ printf("\tinact %u txrate %u\n",
+ ni->ni_inact, ni->ni_txrate);
printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
ni->ni_htcap, ni->ni_htparam,
ni->ni_htctlchan, ni->ni_ht2ndchan);
@@ -1658,16 +2069,22 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt)
void
ieee80211_notify_erp(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP);
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP);
}
/*
* Handle a station joining an 11g network.
*/
static void
-ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_join_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
@@ -1680,7 +2097,7 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
ic->ic_longslotsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station needs long slot time, count %d",
ic->ic_longslotsta);
/* XXX vap's w/ conflicting needs won't work */
@@ -1698,9 +2115,9 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then bump the counter and enable protection
* if configured.
*/
- if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+ if (!ieee80211_iserp_rateset(&ni->ni_rates)) {
ic->ic_nonerpsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station is !ERP, %d non-ERP stations associated",
ic->ic_nonerpsta);
/*
@@ -1708,18 +2125,19 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then we must enable use of Barker preamble.
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"%s", "station needs long preamble");
ic->ic_flags |= IEEE80211_F_USEBARKER;
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
}
/*
- * If protection is configured, enable it.
+ * If protection is configured and this is the first
+ * indication we should use protection, enable it.
*/
if (ic->ic_protmode != IEEE80211_PROT_NONE &&
ic->ic_nonerpsta == 1 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: enable use of protection\n", __func__);
ic->ic_flags |= IEEE80211_F_USEPROT;
ieee80211_notify_erp(ic);
@@ -1729,47 +2147,49 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
}
void
-ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+ieee80211_node_join(struct ieee80211_node *ni, int resp)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int newassoc;
if (ni->ni_associd == 0) {
uint16_t aid;
- IEEE80211_LOCK(ic);
+ KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap"));
/*
* It would be good to search the bitmap
* more efficiently, but this will do for now.
*/
- for (aid = 1; aid < ic->ic_max_aid; aid++) {
- if (!IEEE80211_AID_ISSET(aid,
- ic->ic_aid_bitmap))
+ for (aid = 1; aid < vap->iv_max_aid; aid++) {
+ if (!IEEE80211_AID_ISSET(vap, aid))
break;
}
- if (aid >= ic->ic_max_aid) {
- IEEE80211_UNLOCK(ic);
- IEEE80211_SEND_MGMT(ic, ni, resp,
+ if (aid >= vap->iv_max_aid) {
+ IEEE80211_SEND_MGMT(ni, resp,
IEEE80211_REASON_ASSOC_TOOMANY);
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
return;
}
ni->ni_associd = aid | 0xc000;
ni->ni_jointime = time_uptime;
- IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_LOCK(ic);
+ IEEE80211_AID_SET(vap, ni->ni_associd);
+ vap->iv_sta_assoc++;
ic->ic_sta_assoc++;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_join(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_join_11g(ic, ni);
+ ieee80211_node_join_11g(ni);
IEEE80211_UNLOCK(ic);
newassoc = 1;
} else
newassoc = 0;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
IEEE80211_NODE_AID(ni),
ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -1779,20 +2199,20 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
", turbo" : ""
);
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, newassoc);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_SUCCESS);
/* tell the authenticator about new station */
- if (ic->ic_auth->ia_node_join != NULL)
- ic->ic_auth->ia_node_join(ic, ni);
- ieee80211_notify_node_join(ic, ni,
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ ieee80211_notify_node_join(ni,
resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
@@ -1817,14 +2237,15 @@ disable_protection(struct ieee80211com *ic)
* Handle a station leaving an 11g network.
*/
static void
-ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
- ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
- ic->ic_bsschan->ic_flags, ic->ic_curmode));
+ ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq,
+ ic->ic_bsschan->ic_flags));
/*
* If a long slot station do the slot time bookkeeping.
@@ -1833,7 +2254,7 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_longslotsta > 0,
("bogus long slot station count %d", ic->ic_longslotsta));
ic->ic_longslotsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"long slot time station leaves, count now %d",
ic->ic_longslotsta);
if (ic->ic_longslotsta == 0) {
@@ -1843,7 +2264,8 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
ic->ic_opmode != IEEE80211_M_IBSS) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_ASSOC,
"%s: re-enable use of short slot time\n",
__func__);
ieee80211_set_shortslottime(ic, 1);
@@ -1857,13 +2279,13 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_nonerpsta > 0,
("bogus non-ERP station count %d", ic->ic_nonerpsta));
ic->ic_nonerpsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
" (non-ERP sta present)" : "");
if (ic->ic_nonerpsta == 0 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: disable use of protection\n", __func__);
disable_protection(ic);
}
@@ -1886,8 +2308,10 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "%s\n", "age out non-ERP sta present on channel");
+#if 0
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "age out non-ERP sta present on channel");
+#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
if (ic->ic_nonerpsta == 0)
disable_protection(ic);
@@ -1899,15 +2323,17 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
* when operating as an ap.
*/
void
-ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station with aid %d leaves", IEEE80211_NODE_AID(ni));
- KASSERT(ic->ic_opmode != IEEE80211_M_STA,
- ("unexpected operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA,
+ ("unexpected operating mode %u", vap->iv_opmode));
/*
* If node wasn't previously associated all
* we need to do is reclaim the reference.
@@ -1921,19 +2347,20 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* association id as the authenticator uses the
* associd to locate it's state block.
*/
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
IEEE80211_LOCK(ic);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
ni->ni_associd = 0;
+ vap->iv_sta_assoc--;
ic->ic_sta_assoc--;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_leave(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_leave_11g(ic, ni);
+ ieee80211_node_leave_11g(ni);
IEEE80211_UNLOCK(ic);
/*
* Cleanup station state. In particular clear various
@@ -1941,7 +2368,7 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* is reused before the reference count goes to zero
* (and memory is reclaimed).
*/
- ieee80211_sta_leave(ic, ni);
+ ieee80211_sta_leave(ni);
done:
/*
* Remove the node from any table it's recorded in and
@@ -1957,121 +2384,89 @@ done:
ieee80211_free_node(ni);
}
+struct rssiinfo {
+ struct ieee80211vap *vap;
+ int rssi_samples;
+ uint32_t rssi_total;
+};
+
+static void
+get_hostap_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only associated stations */
+ if (ni->ni_associd == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
+static void
+get_adhoc_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only neighbors */
+ /* XXX check bssid */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
int8_t
-ieee80211_getrssi(struct ieee80211com *ic)
+ieee80211_getrssi(struct ieee80211vap *vap)
{
#define NZ(x) ((x) == 0 ? 1 : (x))
- struct ieee80211_node_table *nt = &ic->ic_sta;
- int rssi_samples;
- int32_t rssi_total;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rssiinfo info;
- rssi_total = 0;
- rssi_samples = 0;
- switch (ic->ic_opmode) {
+ info.rssi_total = 0;
+ info.rssi_samples = 0;
+ info.vap = vap;
+ switch (vap->iv_opmode) {
case IEEE80211_M_IBSS: /* average of all ibss neighbors */
case IEEE80211_M_AHDEMO: /* average of all neighbors */
+ ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info);
+ break;
case IEEE80211_M_HOSTAP: /* average of all associated stations */
- /* XXX locking */
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) {
- int8_t rssi = ic->ic_node_getrssi(ni);
- if (rssi != 0) {
- rssi_samples++;
- rssi_total += rssi;
- }
- }
+ ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info);
break;
case IEEE80211_M_MONITOR: /* XXX */
case IEEE80211_M_STA: /* use stats from associated ap */
default:
- if (ic->ic_bss != NULL)
- rssi_total = ic->ic_node_getrssi(ic->ic_bss);
- rssi_samples = 1;
+ if (vap->iv_bss != NULL)
+ info.rssi_total = ic->ic_node_getrssi(vap->iv_bss);
+ info.rssi_samples = 1;
break;
}
- return rssi_total / NZ(rssi_samples);
+ return info.rssi_total / NZ(info.rssi_samples);
#undef NZ
}
void
-ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise)
+ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise)
{
- if (ic->ic_bss == NULL) /* NB: shouldn't happen */
+ if (vap->iv_bss == NULL) /* NB: shouldn't happen */
return;
- ic->ic_node_getsignal(ic->ic_bss, rssi, noise);
+ vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise);
/* for non-station mode return avg'd rssi accounting */
- if (ic->ic_opmode != IEEE80211_M_STA)
- *rssi = ieee80211_getrssi(ic);
-}
-
-/*
- * Node table support.
- */
-
-static void
-ieee80211_node_table_init(struct ieee80211com *ic,
- struct ieee80211_node_table *nt,
- const char *name, int inact, int keyixmax)
-{
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s %s table, inact %u\n", __func__, name, inact);
-
- nt->nt_ic = ic;
- /* XXX need unit */
- IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- TAILQ_INIT(&nt->nt_node);
- nt->nt_name = name;
- nt->nt_scangen = 1;
- nt->nt_inact_init = inact;
- nt->nt_keyixmax = keyixmax;
- if (nt->nt_keyixmax > 0) {
- MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
- keyixmax * sizeof(struct ieee80211_node *),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (nt->nt_keyixmap == NULL)
- if_printf(ic->ic_ifp,
- "Cannot allocate key index map with %u entries\n",
- keyixmax);
- } else
- nt->nt_keyixmap = NULL;
-}
-
-static void
-ieee80211_node_table_reset(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- IEEE80211_NODE_UNLOCK(nt);
-}
-
-static void
-ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- if (nt->nt_keyixmap != NULL) {
- /* XXX verify all entries are NULL */
- int i;
- for (i = 0; i < nt->nt_keyixmax; i++)
- if (nt->nt_keyixmap[i] != NULL)
- printf("%s: %s[%u] still active\n", __func__,
- nt->nt_name, i);
- FREE(nt->nt_keyixmap, M_80211_NODE);
- nt->nt_keyixmap = NULL;
- }
- IEEE80211_SCAN_LOCK_DESTROY(nt);
- IEEE80211_NODE_LOCK_DESTROY(nt);
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ *rssi = ieee80211_getrssi(vap);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 2799ed4..fb569a9 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,18 +32,16 @@
#include <net80211/ieee80211_ht.h> /* for aggregation state */
/*
- * Each ieee80211com instance has a single timer that fires once a
- * second. This is used to initiate various work depending on the
- * state of the instance: scanning (passive or active), ``transition''
- * (waiting for a response to a management frame when operating
- * as a station), and node inactivity processing (when operating
- * as an AP). For inactivity processing each node has a timeout
- * set in it's ni_inact field that is decremented on each timeout
- * and the node is reclaimed when the counter goes to zero. We
- * use different inactivity timeout values depending on whether
- * the node is associated and authorized (either by 802.1x or
- * open/shared key authentication) or associated but yet to be
- * authorized. The latter timeout is shorter to more aggressively
+ * Each ieee80211com instance has a single timer that fires every
+ * IEEE80211_INACT_WAIT seconds to handle "inactivity processing".
+ * This is used to do node inactivity processing when operating
+ * as an AP or in adhoc mode. For inactivity processing each node
+ * has a timeout set in it's ni_inact field that is decremented
+ * on each timeout and the node is reclaimed when the counter goes
+ * to zero. We use different inactivity timeout values depending
+ * on whether the node is associated and authorized (either by
+ * 802.1x or open/shared key authentication) or associated but yet
+ * to be authorized. The latter timeout is shorter to more aggressively
* reclaim nodes that leave part way through the 802.1x exchange.
*/
#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */
@@ -64,19 +62,28 @@
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
-struct ieee80211_rsnparms {
- uint8_t rsn_mcastcipher; /* mcast/group cipher */
- uint8_t rsn_mcastkeylen; /* mcast key length */
- uint8_t rsn_ucastcipherset; /* unicast cipher set */
- uint8_t rsn_ucastcipher; /* selected unicast cipher */
- uint8_t rsn_ucastkeylen; /* unicast key length */
- uint8_t rsn_keymgmtset; /* key mangement algorithms */
- uint8_t rsn_keymgmt; /* selected key mgmt algo */
- uint16_t rsn_caps; /* capabilities */
-};
-
struct ieee80211_node_table;
struct ieee80211com;
+struct ieee80211vap;
+
+/*
+ * Information element ``blob''. We use this structure
+ * to capture management frame payloads that need to be
+ * retained. Information elemnts within the payload that
+ * we need to consult have references recorded.
+ */
+struct ieee80211_ies {
+ /* the following are either NULL or point within data */
+ uint8_t *wpa_ie; /* captured WPA ie */
+ uint8_t *rsn_ie; /* captured RSN ie */
+ uint8_t *wme_ie; /* captured WME ie */
+ uint8_t *ath_ie; /* captured Atheros ie */
+ uint8_t *htcap_ie; /* captured HTCAP ie */
+ uint8_t *htinfo_ie; /* captured HTINFO ie */
+ /* NB: these must be the last members of this structure */
+ uint8_t *data; /* frame data > 802.11 header */
+ int len; /* data size in bytes */
+};
/*
* Node specific information. Note that drivers are expected
@@ -85,11 +92,12 @@ struct ieee80211com;
* the ieee80211com structure.
*/
struct ieee80211_node {
- struct ieee80211com *ni_ic;
- struct ieee80211_node_table *ni_table;
- TAILQ_ENTRY(ieee80211_node) ni_list;
- LIST_ENTRY(ieee80211_node) ni_hash;
- u_int ni_refcnt;
+ struct ieee80211vap *ni_vap; /* associated vap */
+ struct ieee80211com *ni_ic; /* copy from vap to save deref*/
+ struct ieee80211_node_table *ni_table; /* NB: may be NULL */
+ TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */
+ LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */
+ u_int ni_refcnt; /* count of held references */
u_int ni_scangen; /* gen# for timeout scan */
uint8_t ni_authmode; /* authentication algorithm */
uint8_t ni_ath_flags; /* Atheros feature flags */
@@ -99,7 +107,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
#define IEEE80211_NODE_AR 0x0010 /* AR capable */
-#define IEEE80211_NODE_BOOST 0x0080
+#define IEEE80211_NODE_BOOST 0x0080
#define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */
#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */
uint16_t ni_flags; /* special-purpose state */
@@ -111,6 +119,8 @@ struct ieee80211_node {
#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
+#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
uint16_t ni_ath_defkeyix;/* Atheros def key index */
@@ -119,22 +129,18 @@ struct ieee80211_node {
uint16_t ni_vlan; /* vlan tag */
uint32_t ni_jointime; /* time of join (secs) */
uint32_t *ni_challenge; /* shared-key challenge */
- uint8_t *ni_wpa_ie; /* captured WPA ie */
- uint8_t *ni_rsn_ie; /* captured RSN ie */
- uint8_t *ni_wme_ie; /* captured WME ie */
- uint8_t *ni_ath_ie; /* captured Atheros ie */
+ struct ieee80211_ies ni_ies; /* captured ie's */
/* tx seq per-tid */
uint16_t ni_txseqs[IEEE80211_TID_SIZE];
/* rx seq previous per-tid*/
uint16_t ni_rxseqs[IEEE80211_TID_SIZE];
uint32_t ni_rxfragstamp; /* time stamp of last rx frag */
struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */
- struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */
struct ieee80211_key ni_ucastkey; /* unicast key */
/* hardware */
uint32_t ni_rstamp; /* recv timestamp */
- int8_t ni_rssi; /* recv ssi */
+ uint32_t ni_avgrssi; /* recv ssi state */
int8_t ni_noise; /* noise floor */
/* header */
@@ -144,7 +150,7 @@ struct ieee80211_node {
/* beacon, probe response */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} ni_tstamp; /* from last rcv'd beacon */
uint16_t ni_intval; /* beacon interval */
uint16_t ni_capinfo; /* capabilities */
@@ -154,13 +160,12 @@ struct ieee80211_node {
struct ieee80211_channel *ni_chan;
uint16_t ni_fhdwell; /* FH only */
uint8_t ni_fhindex; /* FH only */
- uint8_t ni_erp; /* ERP from beacon/probe resp */
+ uint16_t ni_erp; /* ERP from beacon/probe resp */
uint16_t ni_timoff; /* byte offset to TIM ie */
uint8_t ni_dtim_period; /* DTIM period */
uint8_t ni_dtim_count; /* DTIM count for last bcn */
/* 11n state */
- uint8_t *ni_htcap_ie; /* captured HTCAP ie */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
uint8_t ni_htctlchan; /* HT control channel */
@@ -174,14 +179,18 @@ struct ieee80211_node {
struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
/* others */
- int ni_fails; /* failure count to associate */
short ni_inact; /* inactivity mark count */
short ni_inact_reload;/* inactivity reload value */
- int ni_txrate; /* index to ni_rates[] */
- struct ifqueue ni_savedq; /* ps-poll queue */
+ int ni_txrate; /* legacy rate/MCS */
+ struct ifqueue ni_savedq; /* ps-poll queue */
struct ieee80211_nodestats ni_stats; /* per-node statistics */
+
+ struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
+ /* XXX move to vap? */
+ struct ifqueue ni_wdsq; /* wds pending queue */
};
MALLOC_DECLARE(M_80211_NODE);
+MALLOC_DECLARE(M_80211_NODE_IE);
#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
#define IEEE80211_NODE_AMPDU \
@@ -193,6 +202,38 @@ MALLOC_DECLARE(M_80211_NODE);
#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v)
#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v)
+/*
+ * Filtered rssi calculation support. The receive rssi is maintained
+ * as an average over the last 10 frames received using a low pass filter
+ * (all frames for now, possibly need to be more selective). Calculations
+ * are designed such that a good compiler can optimize them. The avg
+ * rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and
+ * each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET
+ * to extract the current value.
+ *
+ * Note that we assume rssi data are in the range [-127..127] and we
+ * discard values <-20. This is consistent with assumptions throughout
+ * net80211 that signal strength data are in .5 dBm units relative to
+ * the current noise floor (linear, not log).
+ */
+#define IEEE80211_RSSI_LPF_LEN 10
+#define IEEE80211_RSSI_DUMMY_MARKER 127
+/* NB: pow2 to optimize out * and / */
+#define IEEE80211_RSSI_EP_MULTIPLIER (1<<7)
+#define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER)
+#define _IEEE80211_RSSI_LPF(x, y, len) \
+ (((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define IEEE80211_RSSI_LPF(x, y) do { \
+ if ((y) >= -20) { \
+ x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \
+ IEEE80211_RSSI_LPF_LEN); \
+ } \
+} while (0)
+#define IEEE80211_RSSI_EP_RND(x, mul) \
+ ((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+#define IEEE80211_RSSI_GET(x) \
+ IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER)
+
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
@@ -212,6 +253,9 @@ struct ieee80211com;
void ieee80211_node_attach(struct ieee80211com *);
void ieee80211_node_lateattach(struct ieee80211com *);
void ieee80211_node_detach(struct ieee80211com *);
+void ieee80211_node_vattach(struct ieee80211vap *);
+void ieee80211_node_latevattach(struct ieee80211vap *);
+void ieee80211_node_vdetach(struct ieee80211vap *);
static __inline int
ieee80211_node_is_authorized(const struct ieee80211_node *ni)
@@ -222,21 +266,32 @@ ieee80211_node_is_authorized(const struct ieee80211_node *ni)
void ieee80211_node_authorize(struct ieee80211_node *);
void ieee80211_node_unauthorize(struct ieee80211_node *);
-void ieee80211_probe_curchan(struct ieee80211com *, int);
-void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
-void ieee80211_reset_bss(struct ieee80211com *);
-void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
+void ieee80211_node_set_chan(struct ieee80211_node *,
+ struct ieee80211_channel *);
+void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
+void ieee80211_reset_bss(struct ieee80211vap *);
+void ieee80211_sync_curchan(struct ieee80211com *);
+void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
-int ieee80211_sta_join(struct ieee80211com *,
+int ieee80211_sta_join(struct ieee80211vap *,
const struct ieee80211_scan_entry *);
-void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_sta_leave(struct ieee80211_node *);
+void ieee80211_node_deauth(struct ieee80211_node *, int);
+
+int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int);
+void ieee80211_ies_cleanup(struct ieee80211_ies *);
+void ieee80211_ies_expand(struct ieee80211_ies *);
+#define ieee80211_ies_setie(_ies, _ie, _off) do { \
+ (_ies)._ie = (_ies).data + (_off); \
+} while (0)
/*
* Table of ieee80211_node instances. Each ieee80211com
- * has at least one for holding the scan candidates.
- * When operating as an access point or in ibss mode there
- * is a second table for associated stations or neighbors.
+ * has one that holds association stations (when operating
+ * as an ap) or neighbors (in ibss mode).
+ *
+ * XXX embed this in ieee80211com instead of indirect?
*/
struct ieee80211_node_table {
struct ieee80211com *nt_ic; /* back reference */
@@ -245,23 +300,41 @@ struct ieee80211_node_table {
LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
int nt_keyixmax; /* keyixmap size */
- const char *nt_name; /* for debugging */
+ const char *nt_name; /* table name for debug msgs */
ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
- u_int nt_scangen; /* gen# for timeout scan */
+ u_int nt_scangen; /* gen# for iterators */
int nt_inact_init; /* initial node inact setting */
};
-struct ieee80211_node *ieee80211_alloc_node(
- struct ieee80211_node_table *, const uint8_t *);
-struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
- const uint8_t *macaddr);
-struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
- const uint8_t *);
+struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *,
+ struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ struct ieee80211_channel *);
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_free_node_debug(struct ieee80211_node *,
const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_locked_debug(
+ struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
- const uint8_t *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_locked_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *,
@@ -270,42 +343,43 @@ struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
-struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
- struct ieee80211com *,
- const struct ieee80211_frame_min *, uint16_t keyix,
- const char *func, int line);
-struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *,
+struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const uint8_t *,
const char *func, int line);
-struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid,
- const char *func, int line);
#define ieee80211_free_node(ni) \
ieee80211_free_node_debug(ni, __func__, __LINE__)
+#define ieee80211_find_node_locked(nt, mac) \
+ ieee80211_find_node_locked_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_node(nt, mac) \
ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_rxnode(nt, wh) \
- ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
-#define ieee80211_find_rxnode_withkey(nt, wh, keyix) \
- ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__)
-#define ieee80211_find_txnode(nt, mac) \
- ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
- ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#define ieee80211_find_vap_node_locked(nt, vap, mac) \
+ ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_vap_node(nt, vap, mac) \
+ ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_rxnode(ic, wh) \
+ ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__)
+#define ieee80211_find_rxnode_withkey(ic, wh, keyix) \
+ ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__)
+#define ieee80211_find_txnode(vap, mac) \
+ ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__)
#else
void ieee80211_free_node(struct ieee80211_node *);
+struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
- const uint8_t *);
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node_locked(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
const struct ieee80211_frame_min *);
struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix);
-struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
- const uint8_t *);
-struct ieee80211_node *ieee80211_find_node_with_ssid(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid);
+struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
#endif
int ieee80211_node_delucastkey(struct ieee80211_node *);
void ieee80211_node_timeout(void *arg);
@@ -314,23 +388,22 @@ typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
+void ieee80211_notify_erp(struct ieee80211com *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
-void ieee80211_notify_erp(struct ieee80211com *);
-
-struct ieee80211_node *ieee80211_fakeup_adhoc_node(
- struct ieee80211_node_table *, const uint8_t macaddr[]);
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_scanparams;
void ieee80211_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
+struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
-void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
-int8_t ieee80211_getrssi(struct ieee80211com *);
-void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *);
+void ieee80211_node_join(struct ieee80211_node *,int);
+void ieee80211_node_leave(struct ieee80211_node *);
+int8_t ieee80211_getrssi(struct ieee80211vap *);
+void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *);
#endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index 6e31d1d..cc2a911 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_wds.h>
#ifdef INET
#include <netinet/in.h>
@@ -57,10 +59,10 @@ __FBSDID("$FreeBSD$");
#define ETHER_HEADER_COPY(dst, src) \
memcpy(dst, src, sizeof(struct ether_header))
-static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic,
+static struct mbuf *ieee80211_encap_fastframe(struct ieee80211vap *,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2);
-static int ieee80211_fragment(struct ieee80211com *, struct mbuf *,
+static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *,
u_int hdrsize, u_int ciphdrsize, u_int mtu);
static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
@@ -72,24 +74,344 @@ static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
* (e.g. beacons).
*/
static __inline int
-doprint(struct ieee80211com *ic, int subtype)
+doprint(struct ieee80211vap *vap, int subtype)
{
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
+ return (vap->iv_opmode == IEEE80211_M_IBSS);
}
return 1;
}
#endif
/*
+ * Start method for vap's. All packets from the stack come
+ * through here. We handle common processing of the packets
+ * before dispatching them to the underlying device.
+ */
+void
+ieee80211_start(struct ifnet *ifp)
+{
+#define IS_DWDS(vap) \
+ (vap->iv_opmode == IEEE80211_M_WDS && \
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0)
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ struct ether_header *eh;
+ int error;
+
+ /* NB: parent must be up and running */
+ if (!IFNET_IS_UP_RUNNING(parent)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, parent %s not up+running\n",
+ __func__, parent->if_xname);
+ /* XXX stat */
+ return;
+ }
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /*
+ * In power save, wakeup device for transmit.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+ return;
+ }
+ /*
+ * No data frames go out unless we're running.
+ * Note in particular this covers CAC and CSA
+ * states (though maybe we should check muting
+ * for CSA).
+ */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_LOCK(ic);
+ /* re-check under the com lock to avoid races */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, in %s state\n",
+ __func__, ieee80211_state_name[vap->iv_state]);
+ vap->iv_stats.is_tx_badstate++;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ IEEE80211_UNLOCK(ic);
+ return;
+ }
+ IEEE80211_UNLOCK(ic);
+ }
+ for (;;) {
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /*
+ * Sanitize mbuf flags for net80211 use. We cannot
+ * clear M_PWR_SAV because this may be set for frames
+ * that are re-submitted from the power save queue.
+ *
+ * NB: This must be done before ieee80211_classify as
+ * it marks EAPOL in frames with M_EAPOL.
+ */
+ m->m_flags &= ~(M_80211_TX - M_PWR_SAV);
+ /*
+ * Cancel any background scan.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN)
+ ieee80211_cancel_anyscan(vap);
+ /*
+ * Find the node for the destination so we can do
+ * things like power save and fast frames aggregation.
+ *
+ * NB: past this point various code assumes the first
+ * mbuf has the 802.3 header present (and contiguous).
+ */
+ ni = NULL;
+ if (m->m_len < sizeof(struct ether_header) &&
+ (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "discard frame, %s\n", "m_pullup failed");
+ vap->iv_stats.is_tx_nobuf++; /* XXX */
+ ifp->if_oerrors++;
+ continue;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ if (IS_DWDS(vap)) {
+ /*
+ * Only unicast frames from the above go out
+ * DWDS vaps; multicast frames are handled by
+ * dispatching the frame as it comes through
+ * the AP vap (see below).
+ */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS,
+ eh->ether_dhost, "mcast", "%s", "on DWDS");
+ vap->iv_stats.is_dwds_mcast++;
+ m_freem(m);
+ continue;
+ }
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * Spam DWDS vap's w/ multicast traffic.
+ */
+ /* XXX only if dwds in use? */
+ ieee80211_dwds_mcast(vap, m);
+ }
+ }
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(m);
+ continue;
+ }
+ /* XXX AUTH'd */
+ if (ni->ni_associd == 0) {
+ /*
+ * Destination is not associated; must special
+ * case DWDS where we point iv_bss at the node
+ * for the associated station.
+ * XXX adhoc mode?
+ */
+ if (ni != vap->iv_bss || IS_DWDS(vap)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh->ether_dhost, NULL,
+ "sta not associated (type 0x%04x)",
+ htons(eh->ether_type));
+ vap->iv_stats.is_tx_notassoc++;
+ ifp->if_oerrors++;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ }
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ (m->m_flags & M_PWR_SAV) == 0) {
+ /*
+ * Station in power save mode; pass the frame
+ * to the 802.11 layer and continue. We'll get
+ * the frame back when the time is right.
+ * XXX lose WDS vap linkage?
+ */
+ ieee80211_pwrsave(ni, m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ /* calculate priority so drivers can find the tx queue */
+ if (ieee80211_classify(ni, m)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh->ether_dhost, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ ifp->if_oerrors++;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+
+ BPF_MTAP(ifp, m); /* 802.11 tx path */
+
+ /*
+ * XXX When ni is associated with a WDS link then
+ * the vap will be the WDS vap but ni_vap will point
+ * to the ap vap the station associated to. Once
+ * we handoff the packet to the driver the callback
+ * to ieee80211_encap won't be able to tell if the
+ * packet should be encapsulated for WDS or not (e.g.
+ * multicast frames will not be handled correctly).
+ * We hack this by marking the mbuf so ieee80211_encap
+ * can do the right thing.
+ */
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ m->m_flags |= M_WDS;
+ else
+ m->m_flags &= ~M_WDS;
+
+ /*
+ * Stash the node pointer and hand the frame off to
+ * the underlying device. Note that we do this after
+ * any call to ieee80211_dwds_mcast because that code
+ * uses any existing value for rcvif.
+ */
+ m->m_pkthdr.rcvif = (void *)ni;
+
+ /* XXX defer if_start calls? */
+ IFQ_HANDOFF(parent, m, error);
+ if (error != 0) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ieee80211_free_node(ni);
+ } else {
+ ifp->if_opackets++;
+ }
+ ic->ic_lastdata = ticks;
+ }
+#undef IS_DWDS
+}
+
+/*
+ * 802.11 output routine. This is (currently) used only to
+ * connect bpf write calls to the 802.11 layer for injecting
+ * raw 802.11 frames. Note we locate the ieee80211com from
+ * the ifnet using a spare field setup at attach time. This
+ * will go away when the virtual ap support comes in.
+ */
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt0)
+{
+#define senderr(e) do { error = (e); goto bad;} while (0)
+ struct ieee80211_node *ni = NULL;
+ struct ieee80211vap *vap;
+ struct ieee80211_frame *wh;
+ int error;
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ /*
+ * Short-circuit requests if the vap is marked OACTIVE
+ * as this is used when tearing down state to indicate
+ * the vap may be gone. This can also happen because a
+ * packet came down through ieee80211_start before the
+ * vap entered RUN state in which case it's also ok to
+ * just drop the frame. This should not be necessary
+ * but callers of if_output don't check OACTIVE.
+ */
+ senderr(ENETDOWN);
+ }
+ vap = ifp->if_softc;
+ /*
+ * Hand to the 802.3 code if not tagged as
+ * a raw 802.11 frame.
+ */
+ if (dst->sa_family != AF_IEEE80211)
+ return vap->iv_output(ifp, m, dst, rt0);
+#ifdef MAC
+ error = mac_check_ifnet_transmit(ifp, m);
+ if (error)
+ senderr(error);
+#endif
+ if (ifp->if_flags & IFF_MONITOR)
+ senderr(ENETDOWN);
+ if (!IFNET_IS_UP_RUNNING(ifp))
+ senderr(ENETDOWN);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ "block %s frame in CAC state\n", "raw data");
+ vap->iv_stats.is_tx_badstate++;
+ senderr(EIO); /* XXX */
+ }
+ /* XXX bypass bridge, pfil, carp, etc. */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
+ senderr(EIO); /* XXX */
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0)
+ senderr(EIO); /* XXX */
+
+ /* locate destination node */
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ case IEEE80211_FC1_DIR_FROMDS:
+ ni = ieee80211_find_txnode(vap, wh->i_addr1);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ case IEEE80211_FC1_DIR_DSTODS:
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
+ senderr(EIO); /* XXX */
+ ni = ieee80211_find_txnode(vap, wh->i_addr3);
+ break;
+ default:
+ senderr(EIO); /* XXX */
+ }
+ if (ni == NULL) {
+ /*
+ * Permit packets w/ bpf params through regardless
+ * (see below about sa_len).
+ */
+ if (dst->sa_len == 0)
+ senderr(EHOSTUNREACH);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ }
+
+ /*
+ * Sanitize mbuf for net80211 flags leaked from above.
+ *
+ * NB: This must be done before ieee80211_classify as
+ * it marks EAPOL in frames with M_EAPOL.
+ */
+ m->m_flags &= ~M_80211_TX;
+
+ /* calculate priority so drivers can find the tx queue */
+ /* XXX assumes an 802.3 frame */
+ if (ieee80211_classify(ni, m))
+ senderr(EIO); /* XXX */
+
+ BPF_MTAP(ifp, m);
+
+ /*
+ * NB: DLT_IEEE802_11_RADIO identifies the parameters are
+ * present by setting the sa_len field of the sockaddr (yes,
+ * this is a hack).
+ * NB: we assume sa_data is suitably aligned to cast.
+ */
+ return vap->iv_ic->ic_raw_xmit(ni, m,
+ (const struct ieee80211_bpf_params *)(dst->sa_len ?
+ dst->sa_data : NULL));
+bad:
+ if (m != NULL)
+ m_freem(m);
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+#undef senderr
+}
+
+/*
* Set the direction field and address fields of an outgoing
* non-QoS frame. Note this should be called early on in
* constructing a frame as it sets i_fc[1]; other bits can
* then be or'd in.
*/
static void
-ieee80211_send_setup(struct ieee80211com *ic,
+ieee80211_send_setup(
struct ieee80211_node *ni,
struct ieee80211_frame *wh,
int type,
@@ -101,7 +423,9 @@ ieee80211_send_setup(struct ieee80211com *ic,
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
- switch (ic->ic_opmode) {
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
@@ -123,9 +447,8 @@ ieee80211_send_setup(struct ieee80211com *ic,
break;
case IEEE80211_M_WDS:
wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
- /* XXX cheat, bssid holds RA */
- IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr1, da);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, da);
IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
break;
@@ -139,6 +462,7 @@ ieee80211_send_setup(struct ieee80211com *ic,
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
*(uint16_t *)&wh->i_dur[0] = 0;
+ /* XXX probe response use per-vap seq#? */
/* NB: use non-QoS tid */
*(uint16_t *)&wh->i_seq[0] =
htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -151,55 +475,55 @@ ieee80211_send_setup(struct ieee80211com *ic,
* must have a reference as the pointer will be passed to the driver
* and potentially held for a long time. If the frame is successfully
* dispatched to the driver, then it is responsible for freeing the
- * reference (and potentially free'ing up any associated storage).
+ * reference (and potentially free'ing up any associated storage);
+ * otherwise deal with reclaiming any reference (on error).
*/
int
-ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int type)
+ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
- /*
- * Yech, hack alert! We want to pass the node down to the
- * driver's start routine. If we don't do so then the start
- * routine must immediately look it up again and that can
- * cause a lock order reversal if, for example, this frame
- * is being sent because the station is being timedout and
- * the frame being sent is a DEAUTH message. We could stick
- * this in an m_tag and tack that on to the mbuf. However
- * that's rather expensive to do for every frame so instead
- * we stuff it in the rcvif field since outbound frames do
- * not (presently) use this.
- */
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT]);
+ vap->iv_stats.is_tx_badstate++;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return EIO; /* XXX */
+ }
+
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
- if (m == NULL)
+ if (m == NULL) {
+ ieee80211_free_node(ni);
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
+ }
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | type,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
m->m_flags &= ~M_LINK0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] encrypting frame (%s)\n",
- ether_sprintf(wh->i_addr1), __func__);
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
+ "encrypting frame (%s)", __func__);
wh->i_fc[1] |= IEEE80211_FC1_WEP;
}
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all management frames to the highest queue */
+ if (type != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ /* NB: force non-ProbeResp frames to the highest queue */
M_WME_SETAC(m, WME_AC_VO);
} else
M_WME_SETAC(m, WME_AC_BE);
#ifdef IEEE80211_DEBUG
/* avoid printing too many frames */
- if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
- ieee80211_msg_dumppkts(ic)) {
+ if ((ieee80211_msg_debug(vap) && doprint(vap, type)) ||
+ ieee80211_msg_dumppkts(vap)) {
printf("[%s] send %s on channel %u\n",
ether_sprintf(wh->i_addr1),
ieee80211_mgt_subtype_name[
@@ -209,135 +533,8 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * Raw packet transmit stub for legacy drivers.
- * Send the packet through the mgt q so we bypass
- * the normal encapsulation work.
- */
-int
-ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
-
- m->m_pkthdr.rcvif = (void *) ni;
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * 802.11 output routine. This is (currently) used only to
- * connect bpf write calls to the 802.11 layer for injecting
- * raw 802.11 frames. Note we locate the ieee80211com from
- * the ifnet using a spare field setup at attach time. This
- * will go away when the virtual ap support comes in.
- */
-int
-ieee80211_output(struct ifnet *ifp, struct mbuf *m,
- struct sockaddr *dst, struct rtentry *rt0)
-{
-#define senderr(e) do { error = (e); goto bad;} while (0)
- struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */
- struct ieee80211_node *ni = NULL;
- struct ieee80211_frame *wh;
- int error;
-
- /*
- * Hand to the 802.3 code if not tagged as
- * a raw 802.11 frame.
- */
- if (dst->sa_family != AF_IEEE80211)
- return ether_output(ifp, m, dst, rt0);
-#ifdef MAC
- error = mac_check_ifnet_transmit(ifp, m);
- if (error)
- senderr(error);
-#endif
- if (ifp->if_flags & IFF_MONITOR)
- senderr(ENETDOWN);
- if ((ifp->if_flags & IFF_UP) == 0)
- senderr(ENETDOWN);
- /* XXX bypass bridge, pfil, carp, etc. */
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
- senderr(EIO); /* XXX */
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0)
- senderr(EIO); /* XXX */
-
- /* locate destination node */
- switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
- case IEEE80211_FC1_DIR_NODS:
- case IEEE80211_FC1_DIR_FROMDS:
- ni = ieee80211_find_txnode(ic, wh->i_addr1);
- break;
- case IEEE80211_FC1_DIR_TODS:
- case IEEE80211_FC1_DIR_DSTODS:
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
- senderr(EIO); /* XXX */
- ni = ieee80211_find_txnode(ic, wh->i_addr3);
- break;
- default:
- senderr(EIO); /* XXX */
- }
- if (ni == NULL) {
- /*
- * Permit packets w/ bpf params through regardless
- * (see below about sa_len).
- */
- if (dst->sa_len == 0)
- senderr(EHOSTUNREACH);
- ni = ieee80211_ref_node(ic->ic_bss);
- }
-
- /* XXX ctrl frames should go through */
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m);
- error = 0;
- goto reclaim;
- }
-
- /* calculate priority so drivers can find the tx queue */
- /* XXX assumes an 802.3 frame */
- if (ieee80211_classify(ic, m, ni))
- senderr(EIO); /* XXX */
-
- BPF_MTAP(ifp, m);
- /*
- * NB: DLT_IEEE802_11_RADIO identifies the parameters are
- * present by setting the sa_len field of the sockaddr (yes,
- * this is a hack).
- * NB: we assume sa_data is suitably aligned to cast.
- */
- return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
- (dst->sa_len ? dst->sa_data : NULL));
-bad:
- if (m != NULL)
- m_freem(m);
-reclaim:
- if (ni != NULL)
- ieee80211_free_node(ni);
- return error;
-#undef senderr
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -345,50 +542,61 @@ reclaim:
*
* NB: the caller is assumed to have setup a node reference
* for use; this is necessary to deal with a race condition
- * when probing for inactive stations.
+ * when probing for inactive stations. Like ieee80211_mgmt_output
+ * we must cleanup any node reference on error; however we
+ * can safely just unref it as we know it will never be the
+ * last reference to the node.
*/
int
ieee80211_send_nulldata(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct mbuf *m;
struct ieee80211_frame *wh;
- MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state", "null data");
+ ieee80211_unref_node(&ni);
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == NULL) {
/* XXX debug msg */
ieee80211_unref_node(&ni);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return ENOMEM;
}
MH_ALIGN(m, sizeof(struct ieee80211_frame));
- m->m_pkthdr.rcvif = (void *) ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
- /* NB: power management bit is never sent by an AP */
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_WDS)
- wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
- m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ if (vap->iv_opmode != IEEE80211_M_WDS) {
+ /* NB: power management bit is never sent by an AP */
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ vap->iv_opmode != IEEE80211_M_HOSTAP)
+ wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
+ m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ } else {
+ /* NB: 4-address frame */
+ m->m_len = m->m_pkthdr.len =
+ sizeof(struct ieee80211_frame_addr4);
+ }
M_WME_SETAC(m, WME_AC_BE);
IEEE80211_NODE_STAT(ni, tx_data);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send null data frame on channel %u, pwr mgt %s\n",
- ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni,
+ "send null data frame on channel %u, pwr mgt %s",
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -398,13 +606,23 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
* applied.
*/
int
-ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m)
{
+ const struct ether_header *eh = mtod(m, struct ether_header *);
int v_wme_ac, d_wme_ac, ac;
-#ifdef INET
- struct ether_header *eh;
-#endif
+ /*
+ * Always promote PAE/EAPOL frames to high priority.
+ */
+ if (eh->ether_type == htons(ETHERTYPE_PAE)) {
+ /* NB: mark so others don't need to check header */
+ m->m_flags |= M_EAPOL;
+ ac = WME_AC_VO;
+ goto done;
+ }
+ /*
+ * Non-qos traffic goes to BE.
+ */
if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
ac = WME_AC_BE;
goto done;
@@ -430,7 +648,6 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
}
#ifdef INET
- eh = mtod(m, struct ether_header *);
if (eh->ether_type == htons(ETHERTYPE_IP)) {
uint8_t tos;
/*
@@ -459,13 +676,15 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
/*
* Apply ACM policy.
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) {
static const int acmap[4] = {
WME_AC_BK, /* WME_AC_BE */
WME_AC_BK, /* WME_AC_BK */
WME_AC_BE, /* WME_AC_VI */
WME_AC_VI, /* WME_AC_VO */
};
+ struct ieee80211com *ic = ni->ni_ic;
+
while (ac != WME_AC_BK &&
ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
ac = acmap[ac];
@@ -482,11 +701,11 @@ done:
* and fail rudely if they don't find the space they need.
*/
static struct mbuf *
-ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize,
struct ieee80211_key *key, struct mbuf *m)
{
#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc))
- int needed_space = ic->ic_headroom + hdrsize;
+ int needed_space = vap->iv_ic->ic_headroom + hdrsize;
if (key != NULL) {
/* XXX belongs in crypto code? */
@@ -501,9 +720,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) {
m = m_unshare(m, M_NOWAIT);
if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot get writable mbuf\n", __func__);
- ic->ic_stats.is_tx_nobuf++; /* XXX new stat */
+ vap->iv_stats.is_tx_nobuf++; /* XXX new stat */
return NULL;
}
}
@@ -520,9 +739,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
if (n == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
m_freem(m);
return NULL;
}
@@ -564,13 +783,14 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
* we fall back to the default transmit key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getucastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
return &ni->ni_ucastkey;
}
@@ -582,12 +802,13 @@ ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* the default tx key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getmcastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
}
/*
@@ -595,16 +816,21 @@ ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* If an error is encountered NULL is returned. The caller is required
* to provide a node reference and pullup the ethernet header in the
* first mbuf.
+ *
+ * NB: Packet is assumed to be processed by ieee80211_classify which
+ * marked EAPOL frames w/ M_EAPOL.
*/
struct mbuf *
-ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni)
+ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh))
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ether_header eh;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
- int hdrsize, datalen, addqos, txfrag, isff;
+ int hdrsize, hdrspace, datalen, addqos, txfrag, isff, is4addr;
/*
* Copy existing Ethernet header to a safe place. The
@@ -612,7 +838,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* reorganizing state for the final encapsulation.
*/
KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
- memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+ ETHER_HEADER_COPY(&eh, mtod(m, caddr_t));
/*
* Insure space for additional headers. First identify
@@ -626,23 +852,24 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* buffer may not be expanded as needed by the cipher
* routines, but they will/should discard it.
*/
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- if (ic->ic_opmode == IEEE80211_M_STA ||
- !IEEE80211_IS_MULTICAST(eh.ether_dhost))
- key = ieee80211_crypto_getucastkey(ic, ni);
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ !IEEE80211_IS_MULTICAST(eh.ether_dhost) ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ key = ieee80211_crypto_getucastkey(vap, ni);
else
- key = ieee80211_crypto_getmcastkey(ic, ni);
- if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(eh.ether_dhost), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ key = ieee80211_crypto_getmcastkey(vap, ni);
+ if (key == NULL && (m->m_flags & M_EAPOL) == 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ eh.ether_dhost,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
goto bad;
}
} else
key = NULL;
- /* XXX 4-address format */
/*
* XXX Some ap's don't handle QoS-encapsulated EAPOL
* frames so suppress use. This may be an issue if other
@@ -651,13 +878,31 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* configurable.
*/
addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
- eh.ether_type != htons(ETHERTYPE_PAE);
+ (m->m_flags & M_EAPOL) == 0;
if (addqos)
hdrsize = sizeof(struct ieee80211_qosframe);
else
hdrsize = sizeof(struct ieee80211_frame);
+ /*
+ * 4-address frames need to be generated for:
+ * o packets sent through a WDS vap (M_WDS || IEEE80211_M_WDS)
+ * o packets relayed by a station operating with dynamic WDS
+ * (IEEE80211_M_STA+IEEE80211_F_DWDS and src address)
+ */
+ is4addr = (m->m_flags & M_WDS) ||
+ vap->iv_opmode == IEEE80211_M_WDS || /* XXX redundant? */
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_flags & IEEE80211_F_DWDS) &&
+ !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr));
+ if (is4addr)
+ hdrsize += IEEE80211_ADDR_LEN;
+ /*
+ * Honor driver DATAPAD requirement.
+ */
if (ic->ic_flags & IEEE80211_F_DATAPAD)
- hdrsize = roundup(hdrsize, sizeof(uint32_t));
+ hdrspace = roundup(hdrsize, sizeof(uint32_t));
+ else
+ hdrspace = hdrsize;
if ((isff = m->m_flags & M_FF) != 0) {
struct mbuf *m2;
@@ -671,7 +916,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
*/
m2 = m->m_nextpkt;
if (m2 == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: only one frame\n", __func__);
goto bad;
}
@@ -681,8 +926,8 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* layout; this allocates space according to what
* ieee80211_encap_fastframe will do.
*/
- m = ieee80211_mbuf_adjust(ic,
- hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 +
+ m = ieee80211_mbuf_adjust(vap,
+ hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 +
sizeof(struct ether_header),
key, m);
if (m == NULL) {
@@ -697,22 +942,22 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* at the end of first frame.
*/
KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
- memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header));
- m2 = ieee80211_mbuf_adjust(ic,
+ ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
+ m2 = ieee80211_mbuf_adjust(vap,
ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
NULL, m2);
if (m2 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
}
- m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2);
+ m = ieee80211_encap_fastframe(vap, m, &eh, m2, &eh2);
if (m == NULL)
goto bad;
} else {
/*
* Normal frame.
*/
- m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+ m = ieee80211_mbuf_adjust(vap, hdrspace, key, m);
if (m == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
@@ -729,15 +974,21 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
- M_PREPEND(m, hdrsize, M_DONTWAIT);
+ M_PREPEND(m, hdrspace, M_DONTWAIT);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
goto bad;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(uint16_t *)wh->i_dur = 0;
- switch (ic->ic_opmode) {
+ if (is4addr) {
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+ } else switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
@@ -750,10 +1001,10 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
/*
- * NB: always use the bssid from ic_bss as the
+ * NB: always use the bssid from iv_bss as the
* neighbor's may be stale after an ibss merge
*/
- IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid);
break;
case IEEE80211_M_HOSTAP:
wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
@@ -762,42 +1013,47 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
+ case IEEE80211_M_WDS: /* NB: is4addr should always be true */
goto bad;
}
if (m->m_flags & M_MORE_DATA)
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
if (addqos) {
- struct ieee80211_qosframe *qwh =
- (struct ieee80211_qosframe *) wh;
+ uint8_t *qos;
int ac, tid;
+ if (is4addr) {
+ qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+ } else
+ qos = ((struct ieee80211_qosframe *) wh)->i_qos;
ac = M_WME_GETAC(m);
/* map from access class/queue to 11e header priorty value */
tid = WME_AC_TO_TID(ac);
- qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+ qos[0] = tid & IEEE80211_QOS_TID;
/*
* Check if A-MPDU tx aggregation is setup or if we
* should try to enable it. The sta must be associated
- * with HT and A-MPDU enabled for use. On the first
- * frame that goes out We issue an ADDBA request and
- * wait for a reply. The frame being encapsulated
- * will go out w/o using A-MPDU, or possibly it might
- * be collected by the driver and held/retransmit.
- * ieee80211_ampdu_request handles staggering requests
- * in case the receiver NAK's us or we are otherwise
- * unable to establish a BA stream.
+ * with HT and A-MPDU enabled for use. When the policy
+ * routine decides we should enable A-MPDU we issue an
+ * ADDBA request and wait for a reply. The frame being
+ * encapsulated will go out w/o using A-MPDU, or possibly
+ * it might be collected by the driver and held/retransmit.
+ * The default ic_ampdu_enable routine handles staggering
+ * ADDBA requests in case the receiver NAK's us or we are
+ * otherwise unable to establish a BA stream.
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+ ieee80211_txampdu_count_packet(tap);
if (IEEE80211_AMPDU_RUNNING(tap)) {
/*
* Operational, mark frame for aggregation.
*/
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
- } else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+ } else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
+ ic->ic_ampdu_enable(ni, tap)) {
/*
* Not negotiated yet, request service.
*/
@@ -806,9 +1062,9 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
/* XXX works even when BA marked above */
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
- qwh->i_qos[1] = 0;
- qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
+ qos[1] = 0;
+ wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
*(uint16_t *)wh->i_seq =
htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -819,38 +1075,32 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
}
/* check if xmit fragmentation is required */
- txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold &&
+ txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (ic->ic_caps & IEEE80211_C_TXFRAG) &&
+ (vap->iv_caps & IEEE80211_C_TXFRAG) &&
!isff); /* NB: don't fragment ff's */
if (key != NULL) {
/*
* IEEE 802.1X: send EAPOL frames always in the clear.
* WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
*/
- if (eh.ether_type != htons(ETHERTYPE_PAE) ||
- ((ic->ic_flags & IEEE80211_F_WPA) &&
- (ic->ic_opmode == IEEE80211_M_STA ?
+ if ((m->m_flags & M_EAPOL) == 0 ||
+ ((vap->iv_flags & IEEE80211_F_WPA) &&
+ (vap->iv_opmode == IEEE80211_M_STA ?
!IEEE80211_KEY_UNDEFINED(key) :
!IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
- if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] enmic failed, discard frame\n",
- ether_sprintf(eh.ether_dhost));
- ic->ic_stats.is_crypto_enmicfail++;
+ if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh.ether_dhost,
+ "%s", "enmic failed, discard frame");
+ vap->iv_stats.is_crypto_enmicfail++;
goto bad;
}
}
}
- /*
- * NB: frag flags may leak from above; they should only
- * be set on return to the caller if we fragment at
- * the 802.11 layer.
- */
- m->m_flags &= ~(M_FRAG | M_FIRSTFRAG);
- if (txfrag && !ieee80211_fragment(ic, m, hdrsize,
- key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold))
+ if (txfrag && !ieee80211_fragment(vap, m, hdrsize,
+ key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold))
goto bad;
IEEE80211_NODE_STAT(ni, tx_data);
@@ -860,11 +1110,16 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_NODE_STAT(ni, tx_ucast);
IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+ /* XXX fragmented frames not handled */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+
return m;
bad:
if (m != NULL)
m_freem(m);
return NULL;
+#undef WH4
}
/*
@@ -875,7 +1130,7 @@ bad:
* type that specifies the payload size).
*/
static struct mbuf *
-ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
+ieee80211_encap1(struct ieee80211vap *vap, struct mbuf *m,
const struct ether_header *eh)
{
struct llc *llc;
@@ -894,9 +1149,9 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for ether_header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
ETHER_HEADER_COPY(mtod(m, void *), eh);
@@ -914,7 +1169,7 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
* problem (should not happen).
*/
static struct mbuf *
-ieee80211_encap_fastframe(struct ieee80211com *ic,
+ieee80211_encap_fastframe(struct ieee80211vap *vap,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2)
{
@@ -925,12 +1180,12 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
/*
* First, each frame gets a standard encapsulation.
*/
- m1 = ieee80211_encap1(ic, m1, eh1);
+ m1 = ieee80211_encap1(vap, m1, eh1);
if (m1 == NULL) {
m_freem(m2);
return NULL;
}
- m2 = ieee80211_encap1(ic, m2, eh2);
+ m2 = ieee80211_encap1(vap, m2, eh2);
if (m2 == NULL) {
m_freem(m1);
return NULL;
@@ -969,18 +1224,18 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
m1->m_pkthdr.len += m2->m_pkthdr.len;
M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for tunnel header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for llc header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
llc = mtod(m1, struct llc *);
@@ -991,7 +1246,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
- ic->ic_stats.is_ff_encap++;
+ vap->iv_stats.is_ff_encap++;
return m1;
}
@@ -1005,7 +1260,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
* packet's mbufs but that is significantly more complicated.
*/
static int
-ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
u_int hdrsize, u_int ciphdrsize, u_int mtu)
{
struct ieee80211_frame *wh, *whf;
@@ -1028,6 +1283,7 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
fragsize = totalhdrsize + remainder;
if (fragsize > mtu)
fragsize = mtu;
+ /* XXX fragsize can be >2048! */
KASSERT(fragsize < MCLBYTES,
("fragment size %u too big!", fragsize));
if (fragsize > MHLEN)
@@ -1073,8 +1329,8 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize)));
m0->m_flags |= M_FIRSTFRAG | M_FRAG;
- ic->ic_stats.is_tx_fragframes++;
- ic->ic_stats.is_tx_frags += fragno-1;
+ vap->iv_stats.is_tx_fragframes++;
+ vap->iv_stats.is_tx_frags += fragno-1;
return 1;
bad:
@@ -1125,7 +1381,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
}
/*
- * Add an ssid elemet to a frame.
+ * Add an ssid element to a frame.
*/
static uint8_t *
ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
@@ -1157,188 +1413,39 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
return frm;
}
+/*
+ * Add a CFParams element to a frame.
+ */
static uint8_t *
-ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie)
+ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic)
{
-#define WPA_OUI_BYTES 0x00, 0x50, 0xf2
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
- static const uint8_t cipher_suite[][4] = {
- { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */
- { WPA_OUI_BYTES, WPA_CSE_TKIP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */
- { WPA_OUI_BYTES, WPA_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { WPA_OUI_BYTES, WPA_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { WPA_OUI_BYTES, WPA_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_VENDOR;
- *frm++ = 0; /* length filled in below */
- memcpy(frm, oui, sizeof(oui)); /* WPA OUI */
- frm += sizeof(oui);
- ADDSHORT(frm, WPA_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH)
- ADDSHORT(frm, rsn->rsn_caps);
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("WPA IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+ *frm++ = IEEE80211_ELEMID_CFPARMS;
+ *frm++ = 6;
+ *frm++ = 0; /* CFP count */
+ *frm++ = 2; /* CFP period */
+ ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */
+ ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */
return frm;
#undef ADDSHORT
-#undef ADDSELECTOR
-#undef WPA_OUI_BYTES
}
-static uint8_t *
-ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie)
+static __inline uint8_t *
+add_appie(uint8_t *frm, const struct ieee80211_appie *ie)
{
-#define RSN_OUI_BYTES 0x00, 0x0f, 0xac
-#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
- frm += 2; \
-} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t cipher_suite[][4] = {
- { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */
- { RSN_OUI_BYTES, RSN_CSE_TKIP },
- { RSN_OUI_BYTES, RSN_CSE_WRAP },
- { RSN_OUI_BYTES, RSN_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { RSN_OUI_BYTES, RSN_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { RSN_OUI_BYTES, RSN_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_RSN;
- *frm++ = 0; /* length filled in below */
- ADDSHORT(frm, RSN_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- ADDSHORT(frm, rsn->rsn_caps);
- /* XXX PMKID */
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("RSN IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
- return frm;
-#undef ADDSELECTOR
-#undef ADDSHORT
-#undef RSN_OUI_BYTES
+ memcpy(frm, ie->ie_data, ie->ie_len);
+ return frm + ie->ie_len;
}
-/*
- * Add a WPA/RSN element to a frame.
- */
-static uint8_t *
-ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic)
+static __inline uint8_t *
+add_ie(uint8_t *frm, const uint8_t *ie)
{
-
- KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
- if (ic->ic_flags & IEEE80211_F_WPA2)
- frm = ieee80211_setup_rsn_ie(ic, frm);
- if (ic->ic_flags & IEEE80211_F_WPA1)
- frm = ieee80211_setup_wpa_ie(ic, frm);
- return frm;
+ memcpy(frm, ie, 2 + ie[1]);
+ return frm + 2 + ie[1];
}
#define WME_OUI_BYTES 0x00, 0x50, 0xf2
@@ -1432,6 +1539,94 @@ ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix)
#undef ATH_OUI_BYTES
/*
+ * Add an 11h Power Constraint element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap)
+{
+ const struct ieee80211_channel *c = vap->iv_bss->ni_chan;
+ /* XXX per-vap tx power limit? */
+ int8_t limit = vap->iv_ic->ic_txpowlimit / 2;
+
+ frm[0] = IEEE80211_ELEMID_PWRCNSTR;
+ frm[1] = 1;
+ frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0;
+ return frm + 3;
+}
+
+/*
+ * Add an 11h Power Capability element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c)
+{
+ frm[0] = IEEE80211_ELEMID_PWRCAP;
+ frm[1] = 2;
+ frm[2] = c->ic_minpower;
+ frm[3] = c->ic_maxpower;
+ return frm + 4;
+}
+
+/*
+ * Add an 11h Supported Channels element to a frame.
+ */
+static uint8_t *
+ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic)
+{
+ static const int ielen = 26;
+
+ frm[0] = IEEE80211_ELEMID_SUPPCHAN;
+ frm[1] = ielen;
+ /* XXX not correct */
+ memcpy(frm+2, ic->ic_chan_avail, ielen);
+ return frm + 2 + ielen;
+}
+
+/*
+ * Add an 11h Channel Switch Announcement element to a frame.
+ * Note that we use the per-vap CSA count to adjust the global
+ * counter so we can use this routine to form probe response
+ * frames and get the current count.
+ */
+static uint8_t *
+ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm;
+
+ csa->csa_ie = IEEE80211_ELEMID_CHANSWITCHANN;
+ csa->csa_len = 3;
+ csa->csa_mode = 1; /* XXX force quiet on channel */
+ csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan);
+ csa->csa_count = ic->ic_csa_count - vap->iv_csa_count;
+ return frm + sizeof(*csa);
+}
+
+/*
+ * Add an 11h country information element to a frame.
+ */
+static uint8_t *
+ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic)
+{
+
+ if (ic->ic_countryie == NULL ||
+ ic->ic_countryie_chan != ic->ic_bsschan) {
+ /*
+ * Handle lazy construction of ie. This is done on
+ * first use and after a channel change that requires
+ * re-calculation.
+ */
+ if (ic->ic_countryie != NULL)
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = ieee80211_alloc_countryie(ic);
+ if (ic->ic_countryie == NULL)
+ return frm;
+ ic->ic_countryie_chan = ic->ic_bsschan;
+ }
+ return add_appie(frm, ic->ic_countryie);
+}
+
+/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
@@ -1440,21 +1635,28 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen)
+ const uint8_t *ssid, size_t ssidlen)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
const struct ieee80211_rateset *rs;
struct mbuf *m;
uint8_t *frm;
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+ "block %s frame in CAC state", "probe request");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
/*
* Hold a reference on the node so it doesn't go away until after
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1465,18 +1667,23 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
+ * [tlv] RSN (optional)
* [tlv] extended supported rates
+ * [tlv] WPA (optional)
* [tlv] user-specified ie's
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
- 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (optie != NULL ? optielen : 0)
+ + sizeof(struct ieee80211_ie_wpa)
+ + (vap->iv_appie_probereq != NULL ?
+ vap->iv_appie_probereq->ie_len : 0)
);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
return ENOMEM;
}
@@ -1484,62 +1691,70 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ssid, ssidlen);
rs = ieee80211_get_suprates(ic, ic->ic_curchan);
frm = ieee80211_add_rates(frm, rs);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, rs);
-
- if (optie != NULL) {
- memcpy(frm, optie, optielen);
- frm += optielen;
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
}
+ if (vap->iv_appie_probereq != NULL)
+ frm = add_appie(frm, vap->iv_appie_probereq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
if (m == NULL)
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
sa, da, bssid);
/* XXX power management? */
+ M_WME_SETAC(m, WME_AC_BE);
+
IEEE80211_NODE_STAT(ni, tx_probereq);
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send probe req on channel %u\n",
- ether_sprintf(wh->i_addr1),
- ieee80211_chan2ieee(ic, ic->ic_curchan));
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe req on channel %u bssid %s ssid \"%.*s\"\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid),
+ ssidlen, ssid);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ic->ic_ifp);
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
* Calculate capability information for mgt frames.
*/
static uint16_t
-getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
+getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
+ struct ieee80211com *ic = vap->iv_ic;
uint16_t capinfo;
- KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode"));
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
capinfo = IEEE80211_CAPINFO_ESS;
- else if (ic->ic_opmode == IEEE80211_M_IBSS)
+ else if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = 0;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH))
+ capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
return capinfo;
}
@@ -1549,12 +1764,13 @@ getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
* count bumped to reflect our use for an indeterminant time.
*/
int
-ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int arg)
+ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
{
#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
- const struct ieee80211_rateset *rs;
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node *bss = vap->iv_bss;
struct mbuf *m;
uint8_t *frm;
uint16_t capinfo;
@@ -1567,7 +1783,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1575,112 +1791,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ieee80211_ref_node(ni);
switch (type) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- /*
- * probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] cabability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] parameter set (FH/DS)
- * [tlv] parameter set (IBSS)
- * [tlv] extended rate phy (ERP)
- * [tlv] extended supported rates
- * [tlv] WPA
- * [tlv] WME (optional)
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Vendor OUI HT capabilities (optional)
- * [tlv] Vendor OUI HT information (optional)
- * [tlv] Atheros capabilities
- */
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
- 8
- + sizeof(uint16_t)
- + sizeof(uint16_t)
- + 2 + IEEE80211_NWID_LEN
- + 2 + IEEE80211_RATE_SIZE
- + 7 /* max(7,3) */
- + 6
- + 3
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- /* XXX !WPA1+WPA2 fits w/o a cluster */
- + (ic->ic_flags & IEEE80211_F_WPA ?
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- + sizeof(struct ieee80211_wme_param)
- /* XXX check for cluster requirement */
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
- + 2*sizeof(struct ieee80211_ie_htinfo) + 4
- + sizeof(struct ieee80211_ath_ie)
- );
- if (m == NULL)
- senderr(ENOMEM, is_tx_nobuf);
-
- memset(frm, 0, 8); /* timestamp should be filled later */
- frm += 8;
- *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
- frm += 2;
- capinfo = getcapinfo(ic, ic->ic_curchan);
- *(uint16_t *)frm = htole16(capinfo);
- frm += 2;
-
- frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
- ic->ic_bss->ni_esslen);
- rs = ieee80211_get_suprates(ic, ic->ic_curchan);
- frm = ieee80211_add_rates(frm, rs);
-
- if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
- *frm++ = IEEE80211_ELEMID_FHPARMS;
- *frm++ = 5;
- *frm++ = ni->ni_fhdwell & 0x00ff;
- *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
- *frm++ = IEEE80211_FH_CHANSET(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = IEEE80211_FH_CHANPAT(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = ni->ni_fhindex;
- } else {
- *frm++ = IEEE80211_ELEMID_DSPARMS;
- *frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
- }
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- *frm++ = IEEE80211_ELEMID_IBSSPARMS;
- *frm++ = 2;
- *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
- }
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
- frm = ieee80211_add_erp(frm, ic);
- frm = ieee80211_add_xrates(frm, rs);
- /*
- * NB: legacy 11b clients do not get certain ie's.
- * The caller identifies such clients by passing
- * a token in arg to us. Could expand this to be
- * any legacy client for stuff like HT ie's.
- */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap(frm, ni);
- frm = ieee80211_add_htinfo(frm, ni);
- }
- if (ic->ic_flags & IEEE80211_F_WME)
- frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap_vendor(frm, ni);
- frm = ieee80211_add_htinfo_vendor(frm, ni);
- }
- if (ni->ni_ath_ie != NULL)
- frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
- ni->ni_ath_defkeyix);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- break;
case IEEE80211_FC0_SUBTYPE_AUTH:
status = arg >> 16;
@@ -1699,10 +1809,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
is_shared_key = has_challenge ||
arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
(arg == IEEE80211_AUTH_SHARED_REQUEST &&
- ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+ bss->ni_authmode == IEEE80211_AUTH_SHARED);
m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
3 * sizeof(uint16_t)
+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0)
@@ -1725,9 +1835,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
m->m_pkthdr.len = m->m_len =
4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN;
if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] request encrypt frame (%s)\n",
- ether_sprintf(ni->ni_macaddr), __func__);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "request encrypt frame (%s)", __func__);
m->m_flags |= M_LINK0; /* WEP-encrypt, please */
}
} else
@@ -1739,15 +1848,14 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
else
IEEE80211_NODE_STAT(ni, tx_auth_fail);
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] send station deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "send station deauthenticate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1772,11 +1880,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME
+ * [4] power capability (optional)
+ * [28] supported channels (optional)
* [tlv] HT capabilities
+ * [tlv] WME (optional)
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Atheros capabilities (if negotiated)
- * [tlv] user-specified ie's
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1786,18 +1896,24 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + 4
+ + 2 + 26
+ sizeof(struct ieee80211_wme_info)
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
+ + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ sizeof(struct ieee80211_ath_ie)
- + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+ + (vap->iv_appie_wpa != NULL ?
+ vap->iv_appie_wpa->ie_len : 0)
+ + (vap->iv_appie_assocreq != NULL ?
+ vap->iv_appie_assocreq->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
/*
* NB: Some 11a AP's reject the request when
@@ -1810,50 +1926,63 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
- (ic->ic_flags & IEEE80211_F_DOTH))
+ (vap->iv_flags & IEEE80211_F_DOTH))
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
- KASSERT(ic->ic_bss->ni_intval != 0,
- ("beacon interval is zero!"));
+ KASSERT(bss->ni_intval != 0, ("beacon interval is zero!"));
*(uint16_t *)frm = htole16(howmany(ic->ic_lintval,
- ic->ic_bss->ni_intval));
+ bss->ni_intval));
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(frm, bss->ni_bssid);
frm += IEEE80211_ADDR_LEN;
}
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) {
+ frm = ieee80211_add_powercapability(frm,
+ ic->ic_curchan);
+ frm = ieee80211_add_supportedchannels(frm, ic);
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
frm = ieee80211_add_htcap(frm, ni);
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if ((ic->ic_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
frm = ieee80211_add_htcap_vendor(frm, ni);
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
- (ic->ic_flags & IEEE80211_F_WPA) == 0 &&
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
+ (vap->iv_flags & IEEE80211_F_WPA) == 0 &&
ni->ni_authmode != IEEE80211_AUTH_8021X &&
- ic->ic_def_txkey != IEEE80211_KEYIX_NONE ?
- ic->ic_def_txkey : 0x7fff);
- if (ic->ic_opt_ie != NULL) {
- memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
- frm += ic->ic_opt_ie_len;
- }
+ vap->iv_def_txkey != IEEE80211_KEYIX_NONE ?
+ vap->iv_def_txkey : 0x7fff);
+ if (vap->iv_appie_assocreq != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocreq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
@@ -1865,10 +1994,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME (if enabled and STA enabled)
- * [tlv] HT capabilities (standard or vendor OUI)
- * [tlv] HT information (standard or vendor OUI)
- * [tlv] Atheros capabilities (if enabled and STA enabled)
+ * [tlv] HT capabilities (standard, if STA enabled)
+ * [tlv] HT information (standard, if STA enabled)
+ * [tlv] WME (if configured and STA enabled)
+ * [tlv] HT capabilities (vendor OUI, if STA enabled)
+ * [tlv] HT information (vendor OUI, if STA enabled)
+ * [tlv] Atheros capabilities (if STA enabled)
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1877,15 +2009,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ sizeof(uint16_t)
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ie_htcap) + 4
+ sizeof(struct ieee80211_ie_htinfo) + 4
+ + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_assocresp != NULL ?
+ vap->iv_appie_assocresp->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- capinfo = getcapinfo(ic, ic->ic_curchan);
+ capinfo = getcapinfo(vap, bss->ni_chan);
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -1906,23 +2040,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
}
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if ((vap->iv_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
ni->ni_ath_defkeyix);
+ if (vap->iv_appie_assocresp != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocresp);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] send station disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "send station disassociate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1936,17 +2072,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
break;
default:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] invalid mgmt frame type %u\n",
- ether_sprintf(ni->ni_macaddr), type);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "invalid mgmt frame type %u", type);
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
- ret = ieee80211_mgmt_output(ic, ni, m, type);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, type);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1954,19 +2086,283 @@ bad:
#undef HTFLAGS
}
+/*
+ * Return an mbuf with a probe response frame in it.
+ * Space is left to prepend and 802.11 header at the
+ * front but it's left to the caller to fill in.
+ */
+struct mbuf *
+ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
+{
+ struct ieee80211vap *vap = bss->ni_vap;
+ struct ieee80211com *ic = bss->ni_ic;
+ const struct ieee80211_rateset *rs;
+ struct mbuf *m;
+ uint16_t capinfo;
+ uint8_t *frm;
+
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (FH/DS)
+ * [tlv] parameter set (IBSS)
+ * [tlv] country (optional)
+ * [tlv] RSN (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] WPA (optional)
+ * [tlv] WME (optional)
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * [tlv] Atheros capabilities
+ * [tlv] AppIE's (optional)
+ */
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ 8
+ + sizeof(uint16_t)
+ + sizeof(uint16_t)
+ + 2 + IEEE80211_NWID_LEN
+ + 2 + IEEE80211_RATE_SIZE
+ + 7 /* max(7,3) */
+ + IEEE80211_COUNTRY_MAX_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ + 3
+ + sizeof(struct ieee80211_csa_ie)
+ + 3
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_ie_htcap)
+ + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ie_wpa)
+ + sizeof(struct ieee80211_wme_param)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_proberesp != NULL ?
+ vap->iv_appie_proberesp->ie_len : 0)
+ );
+ if (m == NULL) {
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(uint16_t *)frm = htole16(bss->ni_intval);
+ frm += 2;
+ capinfo = getcapinfo(vap, bss->ni_chan);
+ *(uint16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen);
+ rs = ieee80211_get_suprates(ic, bss->ni_chan);
+ frm = ieee80211_add_rates(frm, rs);
+
+ if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) {
+ *frm++ = IEEE80211_ELEMID_FHPARMS;
+ *frm++ = 5;
+ *frm++ = bss->ni_fhdwell & 0x00ff;
+ *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff;
+ *frm++ = IEEE80211_FH_CHANSET(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = IEEE80211_FH_CHANPAT(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = bss->ni_fhindex;
+ } else {
+ *frm++ = IEEE80211_ELEMID_DSPARMS;
+ *frm++ = 1;
+ *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ }
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ }
+ if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
+ frm = ieee80211_add_erp(frm, ic);
+ frm = ieee80211_add_xrates(frm, rs);
+ /*
+ * NB: legacy 11b clients do not get certain ie's.
+ * The caller identifies such clients by passing
+ * a token in legacy to us. Could expand this to be
+ * any legacy client for stuff like HT ie's.
+ */
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap(frm, bss);
+ frm = ieee80211_add_htinfo(frm, bss);
+ }
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap_vendor(frm, bss);
+ frm = ieee80211_add_htinfo_vendor(frm, bss);
+ }
+ if (bss->ni_ies.ath_ie != NULL && legacy != IEEE80211_SEND_LEGACY_11B)
+ frm = ieee80211_add_ath(frm, bss->ni_ath_flags,
+ bss->ni_ath_defkeyix);
+ if (vap->iv_appie_proberesp != NULL)
+ frm = add_appie(frm, vap->iv_appie_proberesp);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+ return m;
+}
+
+/*
+ * Send a probe response frame to the specified mac address.
+ * This does not go through the normal mgt frame api so we
+ * can specify the destination address and re-use the bss node
+ * for the sta reference.
+ */
+int
+ieee80211_send_proberesp(struct ieee80211vap *vap,
+ const uint8_t da[IEEE80211_ADDR_LEN], int legacy)
+{
+ struct ieee80211_node *bss = vap->iv_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss,
+ "block %s frame in CAC state", "probe response");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ /*
+ * Hold a reference on the node so it doesn't go away until after
+ * the xmit is complete all the way in the driver. On error we
+ * will remove our reference.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+ __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr),
+ ieee80211_node_refcnt(bss)+1);
+ ieee80211_ref_node(bss);
+
+ m = ieee80211_alloc_proberesp(bss, legacy);
+ if (m == NULL) {
+ ieee80211_free_node(bss);
+ return ENOMEM;
+ }
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ KASSERT(m != NULL, ("no room for header"));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ ieee80211_send_setup(bss, wh,
+ IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
+ vap->iv_myaddr, da, bss->ni_bssid);
+ /* XXX power management? */
+
+ M_WME_SETAC(m, WME_AC_BE);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe resp on channel %u to %s%s\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da),
+ legacy ? " <legacy>" : "");
+ IEEE80211_NODE_STAT(bss, tx_mgmt);
+
+ return ic->ic_raw_xmit(bss, m, NULL);
+}
+
+/*
+ * Allocate and build a RTS (Request To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN],
+ const uint8_t ta[IEEE80211_ADDR_LEN],
+ uint16_t dur)
+{
+ struct ieee80211_frame_rts *rts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ rts = mtod(m, struct ieee80211_frame_rts *);
+ rts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS;
+ rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)rts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(rts->i_ra, ra);
+ IEEE80211_ADDR_COPY(rts->i_ta, ta);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts);
+ }
+ return m;
+}
+
+/*
+ * Allocate and build a CTS (Clear To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_cts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur)
+{
+ struct ieee80211_frame_cts *cts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ cts = mtod(m, struct ieee80211_frame_cts *);
+ cts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS;
+ cts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)cts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(cts->i_ra, ra);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts);
+ }
+ return m;
+}
+
static void
ieee80211_tx_mgt_timeout(void *arg)
{
struct ieee80211_node *ni = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ic->ic_state != IEEE80211_S_INIT &&
- (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (vap->iv_state != IEEE80211_S_INIT &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) {
/*
* NB: it's safe to specify a timeout as the reason here;
* it'll only be used in the right state.
*/
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
IEEE80211_SCAN_FAIL_TIMEOUT);
}
}
@@ -1974,7 +2370,7 @@ ieee80211_tx_mgt_timeout(void *arg)
static void
ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
enum ieee80211_state ostate = (enum ieee80211_state) arg;
/*
@@ -1988,27 +2384,20 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
*
* XXX what happens if !acked but response shows up before callback?
*/
- if (ic->ic_state == ostate)
- callout_reset(&ic->ic_mgtsend,
+ if (vap->iv_state == ostate)
+ callout_reset(&vap->iv_mgtsend,
status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
ieee80211_tx_mgt_timeout, ni);
}
-/*
- * Allocate a beacon frame and fillin the appropriate bits.
- */
-struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211_node *ni,
- struct ieee80211_beacon_offsets *bo)
+static void
+ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
+ struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct mbuf *m;
- int pktlen;
- uint8_t *frm;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
uint16_t capinfo;
- struct ieee80211_rateset *rs;
/*
* beacon frame format
@@ -2018,46 +2407,23 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [3] parameter set (DS)
+ * [8] CF parameter set (optional)
* [tlv] parameter set (IBSS/TIM)
- * [tlv] country code
+ * [tlv] country (optional)
+ * [tlv] RSN parameters
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
* [tlv] extended rate phy (ERP)
* [tlv] extended supported rates
- * [tlv] WME parameters
- * [tlv] WPA/RSN parameters
* [tlv] HT capabilities
* [tlv] HT information
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Vendor OUI HT information (optional)
- * XXX Vendor-specific OIDs (e.g. Atheros)
- * NB: we allocate the max space required for the TIM bitmap.
+ * [tlv] application data (optional)
*/
- rs = &ni->ni_rates;
- pktlen = 8 /* time stamp */
- + sizeof(uint16_t) /* beacon interval */
- + sizeof(uint16_t) /* capabilities */
- + 2 + ni->ni_esslen /* ssid */
- + 2 + IEEE80211_RATE_SIZE /* supported rates */
- + 2 + 1 /* DS parameters */
- + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */
- + sizeof(struct ieee80211_country_ie) /* country code */
- + 2 + 1 /* ERP */
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (ic->ic_caps & IEEE80211_C_WME ? /* WME */
- sizeof(struct ieee80211_wme_param) : 0)
- + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- /* XXX conditional? */
- + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
- + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
- ;
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: cannot get buf; size %u\n", __func__, pktlen);
- ic->ic_stats.is_tx_nobuf++;
- return NULL;
- }
memset(bo, 0, sizeof(*bo));
@@ -2065,68 +2431,169 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
frm += 8;
*(uint16_t *)frm = htole16(ni->ni_intval);
frm += 2;
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
bo->bo_caps = (uint16_t *)frm;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
*frm++ = IEEE80211_ELEMID_SSID;
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) {
*frm++ = ni->ni_esslen;
memcpy(frm, ni->ni_essid, ni->ni_esslen);
frm += ni->ni_esslen;
} else
*frm++ = 0;
frm = ieee80211_add_rates(frm, rs);
- if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) {
+ if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) {
*frm++ = IEEE80211_ELEMID_DSPARMS;
*frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ }
+ if (ic->ic_flags & IEEE80211_F_PCF) {
+ bo->bo_cfp = frm;
+ frm = ieee80211_add_cfparms(frm, ic);
}
bo->bo_tim = frm;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
bo->bo_tim_len = 0;
- } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
tie->tim_ie = IEEE80211_ELEMID_TIM;
tie->tim_len = 4; /* length */
tie->tim_count = 0; /* DTIM count */
- tie->tim_period = ic->ic_dtim_period; /* DTIM period */
+ tie->tim_period = vap->iv_dtim_period; /* DTIM period */
tie->tim_bitctl = 0; /* bitmap control */
tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */
frm += sizeof(struct ieee80211_tim_ie);
bo->bo_tim_len = 1;
}
bo->bo_tim_trailer = frm;
- if (ic->ic_flags & IEEE80211_F_DOTH)
- frm = ieee80211_add_countryie(frm, ic,
- ic->ic_countrycode, ic->ic_location);
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ bo->bo_csa = frm;
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ } else
+ bo->bo_csa = frm;
+ if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
}
frm = ieee80211_add_xrates(frm, rs);
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
frm = ieee80211_add_htcap(frm, ni);
bo->bo_htinfo = frm;
frm = ieee80211_add_htinfo(frm, ni);
}
- if (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
}
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
+ if (vap->iv_appie_beacon != NULL) {
+ bo->bo_appie = frm;
+ bo->bo_appie_len = vap->iv_appie_beacon->ie_len;
+ frm = add_appie(frm, vap->iv_appie_beacon);
+ }
bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
+ bo->bo_csa_trailer_len = frm - bo->bo_csa;
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+}
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211_node *ni,
+ struct ieee80211_beacon_offsets *bo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+ int pktlen;
+ uint8_t *frm;
+
+ /*
+ * beacon frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [3] parameter set (DS)
+ * [8] CF parameter set (optional)
+ * [tlv] parameter set (IBSS/TIM)
+ * [tlv] country (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] RSN parameters
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
+ * [tlv] application data (optional)
+ * NB: we allocate the max space required for the TIM bitmap.
+ * XXX how big is this?
+ */
+ pktlen = 8 /* time stamp */
+ + sizeof(uint16_t) /* beacon interval */
+ + sizeof(uint16_t) /* capabilities */
+ + 2 + ni->ni_esslen /* ssid */
+ + 2 + IEEE80211_RATE_SIZE /* supported rates */
+ + 2 + 1 /* DS parameters */
+ + 2 + 6 /* CF parameters */
+ + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */
+ + IEEE80211_COUNTRY_MAX_SIZE /* country */
+ + 2 + 1 /* power control */
+ + sizeof(struct ieee80211_csa_ie) /* CSA */
+ + 2 + 1 /* ERP */
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
+ 2*sizeof(struct ieee80211_ie_wpa) : 0)
+ /* XXX conditional? */
+ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
+ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
+ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */
+ sizeof(struct ieee80211_wme_param) : 0)
+ + IEEE80211_MAX_APPIE
+ ;
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
+ if (m == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: cannot get buf; size %u\n", __func__, pktlen);
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ ieee80211_beacon_construct(m, frm, bo, ni);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
KASSERT(m != NULL, ("no space for 802.11 header?"));
@@ -2136,7 +2603,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
*(uint16_t *)wh->i_dur = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
*(uint16_t *)wh->i_seq = 0;
@@ -2150,16 +2617,46 @@ int
ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
uint16_t capinfo;
- IEEE80211_BEACON_LOCK(ic);
+ IEEE80211_LOCK(ic);
+ /*
+ * Handle 11h channel change when we've reached the count.
+ * We must recalculate the beacon frame contents to account
+ * for the new channel. Note we do this only for the first
+ * vap that reaches this point; subsequent vaps just update
+ * their beacon state to reflect the recalculated channel.
+ */
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) &&
+ vap->iv_csa_count == ic->ic_csa_count) {
+ vap->iv_csa_count = 0;
+ /*
+ * Effect channel change before reconstructing the beacon
+ * frame contents as many places reference ni_chan.
+ */
+ if (ic->ic_csa_newchan != NULL)
+ ieee80211_csa_completeswitch(ic);
+ /*
+ * NB: ieee80211_beacon_construct clears all pending
+ * updates in bo_flags so we don't need to explicitly
+ * clear IEEE80211_BEACON_CSA.
+ */
+ ieee80211_beacon_construct(m,
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni);
+
+ /* XXX do WME aggressive mode processing? */
+ IEEE80211_UNLOCK(ic);
+ return 1; /* just assume length changed */
+ }
+
/* XXX faster to recalculate entirely or just changes? */
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
*bo->bo_caps = htole16(capinfo);
- if (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WME) {
struct ieee80211_wme_state *wme = &ic->ic_wme;
/*
@@ -2172,11 +2669,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
if (wme->wme_flags & WME_F_AGGRMODE) {
if (wme->wme_hipri_traffic >
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, disable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags &= ~WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic =
wme->wme_hipri_switch_hysteresis;
} else
@@ -2184,11 +2681,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
} else {
if (wme->wme_hipri_traffic <=
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, enable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags |= WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic = 0;
} else
wme->wme_hipri_traffic =
@@ -2200,12 +2697,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
}
}
- if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
- ieee80211_ht_update_beacon(ic, bo);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
+ ieee80211_ht_update_beacon(vap, bo);
clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
struct ieee80211_tim_ie *tie =
(struct ieee80211_tim_ie *) bo->bo_tim;
if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
@@ -2217,7 +2714,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* data to make room. Note that we know there is
* contiguous space because ieee80211_beacon_allocate
* insures there is space in the mbuf to write a
- * maximal-size virtual bitmap (based on ic_max_aid).
+ * maximal-size virtual bitmap (based on iv_max_aid).
*/
/*
* Calculate the bitmap size and offset, copy any
@@ -2226,16 +2723,16 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* Note that the tim bitmap must contain at least
* one byte and any offset must be even.
*/
- if (ic->ic_ps_pending != 0) {
+ if (vap->iv_ps_pending != 0) {
timoff = 128; /* impossibly large */
- for (i = 0; i < ic->ic_tim_len; i++)
- if (ic->ic_tim_bitmap[i]) {
+ for (i = 0; i < vap->iv_tim_len; i++)
+ if (vap->iv_tim_bitmap[i]) {
timoff = i &~ 1;
break;
}
KASSERT(timoff != 128, ("tim bitmap empty!"));
- for (i = ic->ic_tim_len-1; i >= timoff; i--)
- if (ic->ic_tim_bitmap[i])
+ for (i = vap->iv_tim_len-1; i >= timoff; i--)
+ if (vap->iv_tim_bitmap[i])
break;
timlen = 1 + (i - timoff);
} else {
@@ -2250,9 +2747,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
bo->bo_tim_trailer+adjust,
bo->bo_tim_trailer_len);
bo->bo_tim_trailer += adjust;
- bo->bo_wme += adjust;
bo->bo_erp += adjust;
bo->bo_htinfo += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_wme += adjust;
+ bo->bo_csa += adjust;
bo->bo_tim_len = timlen;
/* update information element */
@@ -2260,14 +2759,14 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl = timoff;
len_changed = 1;
}
- memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+ memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff,
bo->bo_tim_len);
clrbit(bo->bo_flags, IEEE80211_BEACON_TIM);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
"%s: TIM updated, pending %u, off %u, len %u\n",
- __func__, ic->ic_ps_pending, timoff, timlen);
+ __func__, vap->iv_ps_pending, timoff, timlen);
}
/* count down DTIM period */
if (tie->tim_count == 0)
@@ -2279,6 +2778,33 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl |= 1;
else
tie->tim_bitctl &= ~1;
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) {
+ struct ieee80211_csa_ie *csa =
+ (struct ieee80211_csa_ie *) bo->bo_csa;
+
+ /*
+ * Insert or update CSA ie. If we're just starting
+ * to count down to the channel switch then we need
+ * to insert the CSA ie. Otherwise we just need to
+ * drop the count. The actual change happens above
+ * when the vap's count reaches the target count.
+ */
+ if (vap->iv_csa_count == 0) {
+ memmove(&csa[1], csa, bo->bo_csa_trailer_len);
+ bo->bo_erp += sizeof(*csa);
+ bo->bo_wme += sizeof(*csa);
+ bo->bo_appie += sizeof(*csa);
+ bo->bo_csa_trailer_len += sizeof(*csa);
+ bo->bo_tim_trailer_len += sizeof(*csa);
+ m->m_len += sizeof(*csa);
+ m->m_pkthdr.len += sizeof(*csa);
+
+ ieee80211_add_csa(bo->bo_csa, vap);
+ } else
+ csa->csa_count--;
+ vap->iv_csa_count++;
+ /* NB: don't clear IEEE80211_BEACON_CSA */
+ }
if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
* ERP element needs updating.
@@ -2287,7 +2813,31 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
}
}
- IEEE80211_BEACON_UNLOCK(ic);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) {
+ const struct ieee80211_appie *aie = vap->iv_appie_beacon;
+ int aielen;
+ uint8_t *frm;
+
+ aielen = 0;
+ if (aie != NULL)
+ aielen += aie->ie_len;
+ if (aielen != bo->bo_appie_len) {
+ /* copy up/down trailer */
+ int adjust = aielen - bo->bo_appie_len;
+ ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust,
+ bo->bo_tim_trailer_len);
+ bo->bo_tim_trailer += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_appie_len = aielen;
+
+ len_changed = 1;
+ }
+ frm = bo->bo_appie;
+ if (aie != NULL)
+ frm = add_appie(frm, aie);
+ clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE);
+ }
+ IEEE80211_UNLOCK(ic);
return len_changed;
}
diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c
new file mode 100644
index 0000000..e1c847f
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.c
@@ -0,0 +1,472 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 PHY-related support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
+
+#ifdef notyet
+struct ieee80211_ds_plcp_hdr {
+ uint8_t i_signal;
+ uint8_t i_service;
+ uint16_t i_length;
+ uint16_t i_crc;
+} __packed;
+
+#endif /* notyet */
+
+/* shorthands to compact tables for readability */
+#define OFDM IEEE80211_T_OFDM
+#define CCK IEEE80211_T_CCK
+#define TURBO IEEE80211_T_TURBO
+#define PBCC (IEEE80211_T_HT+1) /* XXX */
+
+static struct ieee80211_rate_table ieee80211_11b_table = {
+ 4, /* number of rates, XXX no PBCC */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 1 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 1 },
+/* 22 Mb */ { PBCC, 22000, 0x04, 44, 3 }
+ },
+};
+
+
+static struct ieee80211_rate_table ieee80211_11g_table = {
+ 12, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 2 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 3 },
+/* 6 Mb */ { OFDM, 6000, 0x00, 12, 4 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 12 Mb */ { OFDM, 12000, 0x00, 24, 6 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 6 },
+/* 24 Mb */ { OFDM, 24000, 0x00, 48, 8 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 8 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 8 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 8 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_11a_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { OFDM, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { OFDM, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_half_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 0 },
+/* 9 Mb */ { OFDM, 4500, 0x00, 9, 0 },
+/* 12 Mb */ { OFDM, 6000, 0x00, (0x80|12), 2 },
+/* 18 Mb */ { OFDM, 9000, 0x00, 18, 2 },
+/* 24 Mb */ { OFDM, 12000, 0x00, (0x80|24), 4 },
+/* 36 Mb */ { OFDM, 18000, 0x00, 36, 4 },
+/* 48 Mb */ { OFDM, 24000, 0x00, 48, 4 },
+/* 54 Mb */ { OFDM, 27000, 0x00, 54, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_quarter_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 1500, 0x00, (0x80| 3), 0 },
+/* 9 Mb */ { OFDM, 2250, 0x00, 4, 0 },
+/* 12 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 2 },
+/* 18 Mb */ { OFDM, 4500, 0x00, 9, 2 },
+/* 24 Mb */ { OFDM, 6000, 0x00, (0x80|12), 4 },
+/* 36 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 48 Mb */ { OFDM, 12000, 0x00, 24, 4 },
+/* 54 Mb */ { OFDM, 13500, 0x00, 27, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turbog_table = {
+ 7, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 1 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 1 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 3 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 3 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 3 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 3 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turboa_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { TURBO, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 4 }
+ },
+};
+
+#undef OFDM
+#undef CCK
+#undef TURBO
+#undef XR
+
+/*
+ * Setup a rate table's reverse lookup table and fill in
+ * ack durations. The reverse lookup tables are assumed
+ * to be initialized to zero (or at least the first entry).
+ * We use this as a key that indicates whether or not
+ * we've previously setup the reverse lookup table.
+ *
+ * XXX not reentrant, but shouldn't matter
+ */
+static void
+ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+#define WLAN_CTRL_FRAME_SIZE \
+ (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
+
+ int i;
+
+ for (i = 0; i < N(rt->rateCodeToIndex); i++)
+ rt->rateCodeToIndex[i] = (uint8_t) -1;
+ for (i = 0; i < rt->rateCount; i++) {
+ uint8_t code = rt->info[i].dot11Rate;
+ uint8_t cix = rt->info[i].ctlRateIndex;
+ uint8_t ctl_rate = rt->info[cix].dot11Rate;
+
+ rt->rateCodeToIndex[code] = i;
+ if (code & IEEE80211_RATE_BASIC) {
+ /*
+ * Map w/o basic rate bit too.
+ */
+ code &= IEEE80211_RATE_VAL;
+ rt->rateCodeToIndex[code] = i;
+ }
+
+ /*
+ * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
+ * depends on whether they are marked as basic rates;
+ * the static tables are setup with an 11b-compatible
+ * 2Mb/s rate which will work but is suboptimal
+ *
+ * NB: Control rate is always less than or equal to the
+ * current rate, so control rate's reverse lookup entry
+ * has been installed and following call is safe.
+ */
+ rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
+ rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
+ }
+
+#undef WLAN_CTRL_FRAME_SIZE
+#undef N
+}
+
+/* Setup all rate tables */
+static void
+ieee80211_phy_init(void)
+{
+#define N(arr) (int)(sizeof(arr) / sizeof(arr[0]))
+ static struct ieee80211_rate_table * const ratetables[] = {
+ &ieee80211_half_table,
+ &ieee80211_quarter_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_turbog_table,
+ &ieee80211_turboa_table,
+ &ieee80211_turboa_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_11b_table
+ };
+ int i;
+
+ for (i = 0; i < N(ratetables); ++i)
+ ieee80211_setup_ratetable(ratetables[i]);
+
+#undef N
+}
+SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
+
+const struct ieee80211_rate_table *
+ieee80211_get_ratetable(struct ieee80211_channel *c)
+{
+ const struct ieee80211_rate_table *rt;
+
+ /* XXX HT */
+ if (IEEE80211_IS_CHAN_HALF(c))
+ rt = &ieee80211_half_table;
+ else if (IEEE80211_IS_CHAN_QUARTER(c))
+ rt = &ieee80211_quarter_table;
+ else if (IEEE80211_IS_CHAN_HTA(c))
+ rt = &ieee80211_11a_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_HTG(c))
+ rt = &ieee80211_11g_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_108G(c))
+ rt = &ieee80211_turbog_table;
+ else if (IEEE80211_IS_CHAN_ST(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_TURBO(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_A(c))
+ rt = &ieee80211_11a_table;
+ else if (IEEE80211_IS_CHAN_ANYG(c))
+ rt = &ieee80211_11g_table;
+ else if (IEEE80211_IS_CHAN_B(c))
+ rt = &ieee80211_11b_table;
+ else {
+ /* NB: should not get here */
+ panic("%s: no rate table for channel; freq %u flags 0x%x\n",
+ __func__, c->ic_freq, c->ic_flags);
+ }
+ return rt;
+}
+
+/*
+ * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
+ *
+ * Note we do no parameter checking; this routine is mainly
+ * used to derive an 802.11 rate for constructing radiotap
+ * header data for rx frames.
+ *
+ * XXX might be a candidate for inline
+ */
+uint8_t
+ieee80211_plcp2rate(uint8_t plcp, int ofdm)
+{
+ if (ofdm) {
+ static const uint8_t ofdm_plcp2rate[16] = {
+ [0xb] = 12,
+ [0xf] = 18,
+ [0xa] = 24,
+ [0xe] = 36,
+ [0x9] = 48,
+ [0xd] = 72,
+ [0x8] = 96,
+ [0xc] = 108
+ };
+ return ofdm_plcp2rate[plcp & 0xf];
+ } else {
+ static const uint8_t cck_plcp2rate[16] = {
+ [0xa] = 2, /* 0x0a */
+ [0x4] = 4, /* 0x14 */
+ [0x7] = 11, /* 0x37 */
+ [0xe] = 22, /* 0x6e */
+ [0xc] = 44, /* 0xdc , actually PBCC */
+ };
+ return cck_plcp2rate[plcp & 0xf];
+ }
+}
+
+/*
+ * Covert 802.11 rate to PLCP signal.
+ */
+uint8_t
+ieee80211_rate2plcp(int rate)
+{
+ switch (rate) {
+ /* CCK rates (returned values are device-dependent) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified rate, phy, and short preamble setting.
+ * SIFS is included.
+ */
+uint16_t
+ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ uint32_t kbps;
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ kbps = rt->info[rix].rateKbps;
+ if (kbps == 0) /* XXX bandaid for channel changes */
+ return 0;
+
+ switch (rt->info[rix].phy) {
+ case IEEE80211_T_CCK:
+#define CCK_SIFS_TIME 10
+#define CCK_PREAMBLE_BITS 144
+#define CCK_PLCP_BITS 48
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (isShortPreamble && rt->info[rix].shortPreamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = CCK_SIFS_TIME + phyTime
+ + ((numBits * 1000)/kbps);
+ break;
+#undef CCK_SIFS_TIME
+#undef CCK_PREAMBLE_BITS
+#undef CCK_PLCP_BITS
+
+ case IEEE80211_T_OFDM:
+#define OFDM_SIFS_TIME 16
+#define OFDM_PREAMBLE_TIME 20
+#define OFDM_PLCP_BITS 22
+#define OFDM_SYMBOL_TIME 4
+
+#define OFDM_SIFS_TIME_HALF 32
+#define OFDM_PREAMBLE_TIME_HALF 40
+#define OFDM_PLCP_BITS_HALF 22
+#define OFDM_SYMBOL_TIME_HALF 8
+
+#define OFDM_SIFS_TIME_QUARTER 64
+#define OFDM_PREAMBLE_TIME_QUARTER 80
+#define OFDM_PLCP_BITS_QUARTER 22
+#define OFDM_SYMBOL_TIME_QUARTER 16
+ if (rt == &ieee80211_half_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_QUARTER
+ + OFDM_PREAMBLE_TIME_QUARTER
+ + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
+ } else if (rt == &ieee80211_quarter_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_HALF
+ + OFDM_PREAMBLE_TIME_HALF
+ + (numSymbols * OFDM_SYMBOL_TIME_HALF);
+ } else { /* full rate channel */
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("full rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME
+ + OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
+ }
+ break;
+
+#undef OFDM_SIFS_TIME
+#undef OFDM_PREAMBLE_TIME
+#undef OFDM_PLCP_BITS
+#undef OFDM_SYMBOL_TIME
+
+ case IEEE80211_T_TURBO:
+#define TURBO_SIFS_TIME 8
+#define TURBO_PREAMBLE_TIME 14
+#define TURBO_PLCP_BITS 22
+#define TURBO_SYMBOL_TIME 4
+ /* we still save OFDM rates in kbps - so double them */
+ bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("turbo bps"));
+
+ numBits = TURBO_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
+ + (numSymbols * TURBO_SYMBOL_TIME);
+ break;
+#undef TURBO_SIFS_TIME
+#undef TURBO_PREAMBLE_TIME
+#undef TURBO_PLCP_BITS
+#undef TURBO_SYMBOL_TIME
+
+ default:
+ panic("%s: unknown phy %u (rate %u)\n", __func__,
+ rt->info[rix].phy, rate);
+ break;
+ }
+ return txTime;
+}
diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h
new file mode 100644
index 0000000..2b5adcf
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET80211_IEEE80211_PHY_H_
+#define _NET80211_IEEE80211_PHY_H_
+
+#ifdef _KERNEL
+/*
+ * IEEE 802.11 PHY-related definitions.
+ */
+
+/*
+ * Contention window (slots).
+ */
+#define IEEE80211_CW_MAX 1023 /* aCWmax */
+#define IEEE80211_CW_MIN_0 31 /* DS/CCK aCWmin, ERP aCWmin(0) */
+#define IEEE80211_CW_MIN_1 15 /* OFDM aCWmin, ERP aCWmin(1) */
+
+/*
+ * SIFS (microseconds).
+ */
+#define IEEE80211_DUR_SIFS 10 /* DS/CCK/ERP SIFS */
+#define IEEE80211_DUR_OFDM_SIFS 16 /* OFDM SIFS */
+
+/*
+ * Slot time (microseconds).
+ */
+#define IEEE80211_DUR_SLOT 20 /* DS/CCK slottime, ERP long slottime */
+#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */
+#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */
+
+/*
+ * DIFS (microseconds).
+ */
+#define IEEE80211_DUR_DIFS(sifs, slot) ((sifs) + 2 * (slot))
+
+struct ieee80211_channel;
+
+struct ieee80211_rate_table {
+ int rateCount; /* NB: for proper padding */
+ uint8_t rateCodeToIndex[256]; /* back mapping */
+ struct {
+ uint8_t phy; /* CCK/OFDM/TURBO */
+ uint32_t rateKbps; /* transfer rate in kbs */
+ uint8_t shortPreamble; /* mask for enabling short
+ * preamble in CCK rate code */
+ uint8_t dot11Rate; /* value for supported rates
+ * info element of MLME */
+ uint8_t ctlRateIndex; /* index of next lower basic
+ * rate; used for dur. calcs */
+ uint16_t lpAckDuration; /* long preamble ACK dur. */
+ uint16_t spAckDuration; /* short preamble ACK dur. */
+ } info[32];
+};
+
+const struct ieee80211_rate_table *ieee80211_get_ratetable(
+ struct ieee80211_channel *);
+
+static __inline__ uint8_t
+ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ uint8_t
+ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ enum ieee80211_phytype
+ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[rix].phy;
+}
+
+/*
+ * Calculate ACK field for
+ * o non-fragment data frames
+ * o management frames
+ * sent using rate, phy and short preamble setting.
+ */
+static __inline__ uint16_t
+ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
+ uint8_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ if (isShortPreamble) {
+ KASSERT(rt->info[rix].spAckDuration != 0,
+ ("shpreamble ack dur is not computed!\n"));
+ return rt->info[rix].spAckDuration;
+ } else {
+ KASSERT(rt->info[rix].lpAckDuration != 0,
+ ("lgpreamble ack dur is not computed!\n"));
+ return rt->info[rix].lpAckDuration;
+ }
+}
+
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified 802.11 rate code, phy, and short preamble
+ * setting.
+ *
+ * NB: SIFS is included.
+ */
+uint16_t ieee80211_compute_duration(const struct ieee80211_rate_table *,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble);
+/*
+ * Convert PLCP signal/rate field to 802.11 rate code (.5Mbits/s)
+ */
+uint8_t ieee80211_plcp2rate(uint8_t, int);
+/*
+ * Convert 802.11 rate code to PLCP signal.
+ */
+uint8_t ieee80211_rate2plcp(int);
+#endif /* _KERNEL */
+#endif /* !_NET80211_IEEE80211_PHY_H_ */
diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c
index e764e1f..2a7dda8 100644
--- a/sys/net80211/ieee80211_power.c
+++ b/sys/net80211/ieee80211_power.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 power save support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -43,43 +45,57 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
-static void ieee80211_set_tim(struct ieee80211_node *ni, int set);
+static void ieee80211_update_ps(struct ieee80211vap *, int);
+static int ieee80211_set_tim(struct ieee80211_node *, int);
+
+MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state");
void
ieee80211_power_attach(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
+}
+
+void
+ieee80211_power_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_power_vattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
/* NB: driver should override */
- ic->ic_set_tim = ieee80211_set_tim;
+ vap->iv_update_ps = ieee80211_update_ps;
+ vap->iv_set_tim = ieee80211_set_tim;
}
}
void
-ieee80211_power_lateattach(struct ieee80211com *ic)
+ieee80211_power_latevattach(struct ieee80211vap *vap)
{
/*
* Allocate these only if needed. Beware that we
* know adhoc mode doesn't support ATIM yet...
*/
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
- MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
- M_DEVBUF, M_NOWAIT | M_ZERO);
- if (ic->ic_tim_bitmap == NULL) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t);
+ MALLOC(vap->iv_tim_bitmap, uint8_t *, vap->iv_tim_len,
+ M_80211_POWER, M_NOWAIT | M_ZERO);
+ if (vap->iv_tim_bitmap == NULL) {
printf("%s: no memory for TIM bitmap!\n", __func__);
/* XXX good enough to keep from crashing? */
- ic->ic_tim_len = 0;
+ vap->iv_tim_len = 0;
}
}
}
void
-ieee80211_power_detach(struct ieee80211com *ic)
+ieee80211_power_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_tim_bitmap != NULL) {
- FREE(ic->ic_tim_bitmap, M_DEVBUF);
- ic->ic_tim_bitmap = NULL;
+ if (vap->iv_tim_bitmap != NULL) {
+ FREE(vap->iv_tim_bitmap, M_80211_POWER);
+ vap->iv_tim_bitmap = NULL;
}
}
@@ -116,12 +132,16 @@ ieee80211_node_saveq_age(struct ieee80211_node *ni)
int discard = 0;
if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
struct mbuf *m;
IEEE80211_NODE_SAVEQ_LOCK(ni);
while (IF_POLL(&ni->ni_savedq, m) != NULL &&
M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
-IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "discard frame, age %u", M_AGE_GET(m));
_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
m_freem(m);
discard++;
@@ -130,7 +150,7 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
M_AGE_SUB(m, IEEE80211_INACT_WAIT);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"discard %u frames for age", discard);
IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
}
@@ -138,35 +158,52 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
}
/*
- * Indicate whether there are frames queued for a station in power-save mode.
+ * Handle a change in the PS station occupancy.
*/
static void
+ieee80211_update_ps(struct ieee80211vap *vap, int nsta)
+{
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static int
ieee80211_set_tim(struct ieee80211_node *ni, int set)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
uint16_t aid;
+ int changed;
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS,
- ("operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
aid = IEEE80211_AID(ni->ni_associd);
- KASSERT(aid < ic->ic_max_aid,
- ("bogus aid %u, max %u", aid, ic->ic_max_aid));
+ KASSERT(aid < vap->iv_max_aid,
+ ("bogus aid %u, max %u", aid, vap->iv_max_aid));
- IEEE80211_BEACON_LOCK(ic);
- if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+ IEEE80211_LOCK(ic);
+ changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0));
+ if (changed) {
if (set) {
- setbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending++;
+ setbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending++;
} else {
- clrbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending--;
+ clrbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending--;
}
- /* NB: we know ic is in RUN state so no need to check */
- ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
+ /* NB: we know vap is in RUN state so no need to check */
+ vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM);
}
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_UNLOCK(ic);
+
+ return changed;
}
/*
@@ -177,6 +214,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set)
void
ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int qlen, age;
@@ -184,13 +222,13 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
if (_IF_QFULL(&ni->ni_savedq)) {
_IF_DROP(&ni->ni_savedq);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] pwr save q overflow, drops %d (size %d)\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "pwr save q overflow, drops %d (size %d)",
+ ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_dumppkts(ic))
- ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1);
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
#endif
m_freem(m);
return;
@@ -207,57 +245,26 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] save frame with age %d, %u now queued\n",
- ether_sprintf(ni->ni_macaddr), age, qlen);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "save frame with age %d, %u now queued", age, qlen);
- if (qlen == 1 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 1);
+ if (qlen == 1 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 1);
}
/*
- * Handle station power-save state change.
+ * Unload the frames from the ps q but don't send them
+ * to the driver yet. We do this in two stages to minimize
+ * locking but also because there's no easy way to preserve
+ * ordering given the existing ifnet access mechanisms.
+ * XXX could be optimized
*/
-void
-ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+static void
+pwrsave_flushq(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m, *mhead, *mtail;
int mcount;
- if (enable) {
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
- ic->ic_ps_sta++;
- ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode on, %u sta's in ps mode", ic->ic_ps_sta);
- return;
- }
-
- if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
- ic->ic_ps_sta--;
- ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode off, %u sta's in ps mode", ic->ic_ps_sta);
- /* XXX if no stations in ps mode, flush mc frames */
-
- /*
- * Flush queued unicast frames.
- */
- if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni));
- /*
- * Unload the frames from the ps q but don't send them
- * to the driver yet. We do this in two stages to minimize
- * locking but also because there's no easy way to preserve
- * ordering given the existing ifnet access mechanisms.
- * XXX could be optimized
- */
IEEE80211_NODE_SAVEQ_LOCK(ni);
mcount = IEEE80211_NODE_SAVEQ_QLEN(ni);
mhead = mtail = NULL;
@@ -275,26 +282,67 @@ ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
if (mhead != NULL) {
/* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount);
+ /* XXX bypasses q max and OACTIVE */
+ struct ifnet *ifp = ni->ni_vap->iv_ifp;
+ IF_PREPEND_LIST(&ifp->if_snd, mhead, mtail, mcount);
+ if_start(ifp);
+ }
+}
+
+/*
+ * Handle station power-save state change.
+ */
+void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int update;
+
+ update = 0;
+ if (enable) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ vap->iv_ps_sta++;
+ update = 1;
+ }
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode on, %u sta's in ps mode", vap->iv_ps_sta);
+
+ if (update)
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ } else {
+ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+ vap->iv_ps_sta--;
+ update = 1;
+ }
+ ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
+
+ /* NB: order here is intentional so TIM is clear before flush */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ if (update) {
+ /* NB if no sta's in ps, driver should flush mc q */
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ }
+ pwrsave_flushq(ni);
}
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
}
/*
* Handle power-save state change in station mode.
*/
void
-ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
+ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_node *ni = vap->iv_bss;
int qlen;
if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
return;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"sta power save mode %s", enable ? "on" : "off");
if (!enable) {
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
@@ -307,20 +355,9 @@ ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
*/
qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
if (qlen != 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"flush ps queue, %u packets queued", qlen);
- for (;;) {
- struct mbuf *m;
-
- IEEE80211_NODE_SAVEQ_LOCK(ni);
- _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
- IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- if (m == NULL)
- break;
- /* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
- }
+ pwrsave_flushq(ni);
}
} else {
ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h
index c8461f6..27f5d5a 100644
--- a/sys/net80211/ieee80211_power.h
+++ b/sys/net80211/ieee80211_power.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,14 +30,16 @@
struct ieee80211com;
void ieee80211_power_attach(struct ieee80211com *);
-void ieee80211_power_lateattach(struct ieee80211com *);
void ieee80211_power_detach(struct ieee80211com *);
+void ieee80211_power_vattach(struct ieee80211vap *);
+void ieee80211_power_vdetach(struct ieee80211vap *);
+void ieee80211_power_latevattach(struct ieee80211vap *);
int ieee80211_node_saveq_drain(struct ieee80211_node *);
int ieee80211_node_saveq_age(struct ieee80211_node *);
void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
-void ieee80211_sta_pwrsave(struct ieee80211com *, int enable);
+void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_POWER_H_ */
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index e67d40c..fe03b15 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,25 +32,32 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/taskqueue.h>
#include <sys/socket.h>
+#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_monitor.h>
+#include <net80211/ieee80211_input.h>
/* XXX tunables */
#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */
#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */
-#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
-
const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
@@ -66,11 +73,9 @@ const char *ieee80211_ctl_subtype_name[] = {
const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
"IBSS", /* IEEE80211_M_IBSS */
"STA", /* IEEE80211_M_STA */
- "#2",
+ "WDS", /* IEEE80211_M_WDS */
"AHDEMO", /* IEEE80211_M_AHDEMO */
- "#4", "#5",
"HOSTAP", /* IEEE80211_M_HOSTAP */
- "#7",
"MONITOR" /* IEEE80211_M_MONITOR */
};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
@@ -91,11 +96,19 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
-static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void parent_updown(void *, int);
+static int ieee80211_new_state_locked(struct ieee80211vap *,
+ enum ieee80211_state, int);
-static void
-null_update_beacon(struct ieee80211com *ic, int item)
+static int
+null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
{
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
+
+ if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n");
+ m_freem(m);
+ return ENETDOWN;
}
void
@@ -103,54 +116,131 @@ ieee80211_proto_attach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
- /* XXX room for crypto */
- ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
-
- ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
- ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- ic->ic_bmiss_max = IEEE80211_BMISS_MAX;
- callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE);
- callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE);
- ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
+ /* XXX no way to recalculate on ifdetach */
+ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+ /* XXX sanity check... */
+ max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ }
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+ TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
- mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF);
-
- /* protocol state change handler */
- ic->ic_newstate = ieee80211_newstate;
- ic->ic_update_beacon = null_update_beacon;
-
/* initialize management frame handlers */
- ic->ic_recv_mgmt = ieee80211_recv_mgmt;
ic->ic_send_mgmt = ieee80211_send_mgmt;
- ic->ic_raw_xmit = ieee80211_raw_xmit;
+ ic->ic_raw_xmit = null_raw_xmit;
+
+ ieee80211_adhoc_attach(ic);
+ ieee80211_sta_attach(ic);
+ ieee80211_wds_attach(ic);
+ ieee80211_hostap_attach(ic);
+ ieee80211_monitor_attach(ic);
}
void
ieee80211_proto_detach(struct ieee80211com *ic)
{
+ ieee80211_monitor_detach(ic);
+ ieee80211_hostap_detach(ic);
+ ieee80211_wds_detach(ic);
+ ieee80211_adhoc_detach(ic);
+ ieee80211_sta_detach(ic);
+}
+
+static void
+null_update_beacon(struct ieee80211vap *vap, int item)
+{
+}
+
+void
+ieee80211_proto_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_ifp->if_hdrlen;
+
+ vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
+ vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
+ vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
+ callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
+ callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
+ /*
+ * Install default tx rate handling: no fixed rate, lowest
+ * supported rate for mgmt and multicast frames. Default
+ * max retry count. These settings can be changed by the
+ * driver and/or user applications.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i];
+
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: we default to min supported rate for channel */
+ vap->iv_txparms[i].mgmtrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].mcastrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: default to MCS 0 */
+ vap->iv_txparms[i].mgmtrate = 0 | 0x80;
+ vap->iv_txparms[i].mcastrate = 0 | 0x80;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ vap->iv_roaming = IEEE80211_ROAMING_AUTO;
+
+ vap->iv_update_beacon = null_update_beacon;
+ vap->iv_deliver_data = ieee80211_deliver_data;
+
+ /* attach support for operating mode */
+ ic->ic_vattach[vap->iv_opmode](vap);
+}
+void
+ieee80211_proto_vdetach(struct ieee80211vap *vap)
+{
+#define FREEAPPIE(ie) do { \
+ if (ie != NULL) \
+ FREE(ie, M_80211_NODE_IE); \
+} while (0)
+ /*
+ * Detach operating mode module.
+ */
+ if (vap->iv_opdetach != NULL)
+ vap->iv_opdetach(vap);
/*
* This should not be needed as we detach when reseting
* the state but be conservative here since the
* authenticator may do things like spawn kernel threads.
*/
- if (ic->ic_auth->ia_detach)
- ic->ic_auth->ia_detach(ic);
-
- ieee80211_drain_ifq(&ic->ic_mgtq);
- mtx_destroy(&ic->ic_mgtq.ifq_mtx);
-
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
/*
* Detach any ACL'ator.
*/
- if (ic->ic_acl != NULL)
- ic->ic_acl->iac_detach(ic);
+ if (vap->iv_acl != NULL)
+ vap->iv_acl->iac_detach(vap);
+
+ FREEAPPIE(vap->iv_appie_beacon);
+ FREEAPPIE(vap->iv_appie_probereq);
+ FREEAPPIE(vap->iv_appie_proberesp);
+ FREEAPPIE(vap->iv_appie_assocreq);
+ FREEAPPIE(vap->iv_appie_assocresp);
+ FREEAPPIE(vap->iv_appie_wpa);
+#undef FREEAPPIE
}
/*
@@ -363,16 +453,53 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
struct ieee80211_rateset *nrs, int flags)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int i, j, rix, error;
- int okrate, badrate, fixedrate;
+ int okrate, badrate, fixedrate, ucastrate;
const struct ieee80211_rateset *srs;
uint8_t r;
error = 0;
okrate = badrate = 0;
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate;
+ if (ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * Workaround awkwardness with fixed rate. We are called
+ * to check both the legacy rate set and the HT rate set
+ * but we must apply any legacy fixed rate check only to the
+ * legacy rate set and vice versa. We cannot tell what type
+ * of rate set we've been given (legacy or HT) but we can
+ * distinguish the fixed rate type (MCS have 0x80 set).
+ * So to deal with this the caller communicates whether to
+ * check MCS or legacy rate using the flags and we use the
+ * type of any fixed rate to avoid applying an MCS to a
+ * legacy rate and vice versa.
+ */
+ if (ucastrate & 0x80) {
+ if (flags & IEEE80211_F_DOFRATE)
+ flags &= ~IEEE80211_F_DOFRATE;
+ } else if ((ucastrate & 0x80) == 0) {
+ if (flags & IEEE80211_F_DOFMCS)
+ flags &= ~IEEE80211_F_DOFMCS;
+ }
+ /* NB: required to make MCS match below work */
+ ucastrate &= IEEE80211_RATE_VAL;
+ }
fixedrate = IEEE80211_FIXED_RATE_NONE;
- srs = ieee80211_get_suprates(ic, ni->ni_chan);
+ /*
+ * XXX we are called to process both MCS and legacy rates;
+ * we must use the appropriate basic rate set or chaos will
+ * ensue; for now callers that want MCS must supply
+ * IEEE80211_F_DOBRS; at some point we'll need to split this
+ * function so there are two variants, one for MCS and one
+ * for legacy rates.
+ */
+ if (flags & IEEE80211_F_DOBRS)
+ srs = (const struct ieee80211_rateset *)
+ ieee80211_get_suphtrates(ic, ni->ni_chan);
+ else
+ srs = ieee80211_get_suprates(ic, ni->ni_chan);
for (i = 0; i < nrs->rs_nrates; ) {
if (flags & IEEE80211_F_DOSORT) {
/*
@@ -391,7 +518,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
/*
* Check for fixed rate.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against supported rates.
@@ -431,9 +558,13 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
i++;
}
if (okrate == 0 || error != 0 ||
- ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate))
+ ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) &&
+ fixedrate != ucastrate)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+ "%s: flags 0x%x okrate %d error %d fixedrate 0x%x "
+ "ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
return badrate | IEEE80211_RATE_BASIC;
- else
+ } else
return RV(okrate);
#undef RV
}
@@ -491,7 +622,7 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
* NB: the rate set is assumed to be sorted.
*/
int
-ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+ieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
@@ -516,14 +647,15 @@ ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
}
/*
- * Mark the basic rates for the 11g rate table based on the
+ * Mark the basic rates for the rate table based on the
* operating mode. For real 11g we mark all the 11b rates
* and 6, 12, and 24 OFDM. For 11b compatibility we mark only
* 11b rates. There's also a pseudo 11a-mode used to mark only
* the basic OFDM rates.
*/
-void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+static void
+setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode, int add)
{
static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
{ .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */
@@ -531,16 +663,17 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
{ 2, { 2, 4 } }, /* IEEE80211_MODE_11B */
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */
{ .rs_nrates = 0 }, /* IEEE80211_MODE_FH */
- /* IEEE80211_MODE_PUREG (not yet) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */
- /* IEEE80211_MODE_11NG (mixed b/g) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */
};
int i, j;
for (i = 0; i < rs->rs_nrates; i++) {
- rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+ if (!add)
+ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
for (j = 0; j < basic[mode].rs_nrates; j++)
if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
@@ -550,14 +683,40 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
}
/*
- * WME protocol support. The following parameters come from the spec.
+ * Set the basic rates in a rate set.
+ */
+void
+ieee80211_setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 0);
+}
+
+/*
+ * Add basic rates to a rate set.
+ */
+void
+ieee80211_addbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 1);
+}
+
+/*
+ * WME protocol support.
+ *
+ * The default 11a/b/g/n parameters come from the WiFi Alliance WMM
+ * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n
+ * Draft 2.0 Test Plan (Appendix D).
+ *
+ * Static/Dynamic Turbo mode settings come from Atheros.
*/
typedef struct phyParamType {
- uint8_t aifsn;
- uint8_t logcwmin;
- uint8_t logcwmax;
- uint16_t txopLimit;
- uint8_t acm;
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txopLimit;
+ uint8_t acm;
} paramType;
static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
@@ -646,15 +805,18 @@ static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
{ 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
};
-void
-ieee80211_wme_initparams(struct ieee80211com *ic)
+static void
+ieee80211_wme_initparams_locked(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const paramType *pPhyParam, *pBssPhyParam;
struct wmeParams *wmep;
enum ieee80211_phymode mode;
int i;
+ IEEE80211_LOCK_ASSERT(ic);
+
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
return;
@@ -704,7 +866,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -721,7 +883,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s bss [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -733,7 +895,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
);
}
/* NB: check ic_bss to avoid NULL deref on initial attach */
- if (ic->ic_bss != NULL) {
+ if (vap->iv_bss != NULL) {
/*
* Calculate agressive mode switching threshold based
* on beacon interval. This doesn't need locking since
@@ -741,16 +903,26 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
* which point we start sending beacon frames.
*/
wme->wme_hipri_switch_thresh =
- (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
- ieee80211_wme_updateparams(ic);
+ (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100;
+ ieee80211_wme_updateparams(vap);
}
}
+void
+ieee80211_wme_initparams(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_initparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Update WME parameters for ourself and the BSS.
*/
void
-ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
{
static const paramType phyParam[IEEE80211_MODE_MAX] = {
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */
@@ -764,6 +936,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/
};
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const struct wmeParams *wmep;
struct wmeParams *chanp, *bssp;
@@ -806,11 +979,11 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
* BE uses agressive params to optimize performance of
* legacy/non-QoS traffic.
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP &&
(wme->wme_flags & WME_F_AGGRMODE) != 0) ||
- (ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
- (ic->ic_flags & IEEE80211_F_WME) == 0) {
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+ (vap->iv_flags & IEEE80211_F_WME) == 0) {
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
@@ -820,9 +993,9 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
chanp->wmep_logcwmax = bssp->wmep_logcwmax =
phyParam[mode].logcwmax;
chanp->wmep_txopLimit = bssp->wmep_txopLimit =
- (ic->ic_flags & IEEE80211_F_BURST) ?
+ (vap->iv_flags & IEEE80211_F_BURST) ?
phyParam[mode].txopLimit : 0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
@@ -834,7 +1007,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ /* XXX multi-bss */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
3, /* IEEE80211_MODE_AUTO */
@@ -852,77 +1026,238 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s log2(cwmin) %u\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
, chanp->wmep_logcwmin
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME);
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
wme->wme_update(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
- ic->ic_opmode == IEEE80211_M_STA ?
+ vap->iv_opmode == IEEE80211_M_STA ?
wme->wme_wmeChanParams.cap_info :
wme->wme_bssChanParams.cap_info);
}
void
-ieee80211_wme_updateparams(struct ieee80211com *ic)
+ieee80211_wme_updateparams(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_caps & IEEE80211_C_WME) {
- IEEE80211_BEACON_LOCK(ic);
- ieee80211_wme_updateparams_locked(ic);
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_updateparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
}
}
+static void
+parent_updown(void *arg, int npending)
+{
+ struct ifnet *parent = arg;
+
+ parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
+}
+
/*
- * Start a device. If this is the first vap running on the
- * underlying device then we first bring it up.
+ * Start a vap running. If this is the first vap to be
+ * set running on the underlying device then we
+ * automatically bring the device up.
*/
-int
-ieee80211_init(struct ieee80211com *ic, int forcescan)
+void
+ieee80211_start_locked(struct ieee80211vap *vap)
{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
- IEEE80211_DPRINTF(ic,
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "start running");
+ "start running, %d vaps running\n", ic->ic_nrunning);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ /*
+ * Mark us running. Note that it's ok to do this first;
+ * if we need to bring the parent device up we defer that
+ * to avoid dropping the com lock. We expect the device
+ * to respond to being marked up by calling back into us
+ * through ieee80211_start_all at which point we'll come
+ * back in here and complete the work.
+ */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ /*
+ * We are not running; if this we are the first vap
+ * to be brought up auto-up the parent if necessary.
+ */
+ if (ic->ic_nrunning++ == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s: up parent %s\n", __func__, parent->if_xname);
+ parent->if_flags |= IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ return;
+ }
+ }
/*
- * Kick the 802.11 state machine as appropriate.
+ * If the parent is up and running, then kick the
+ * 802.11 state machine as appropriate.
*/
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if ((parent->if_drv_flags & IFF_DRV_RUNNING) &&
+ vap->iv_roaming != IEEE80211_ROAMING_MANUAL) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+#if 0
+ /* XXX bypasses scan too easily; disable for now */
+ /*
+ * Try to be intelligent about clocking the state
+ * machine. If we're currently in RUN state then
+ * we should be able to apply any new state/parameters
+ * simply by re-associating. Otherwise we need to
+ * re-scan to select an appropriate ap.
+ */
+ if (vap->iv_state >= IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_ASSOC, 1);
+ else
+#endif
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
} else {
/*
- * For monitor+wds modes there's nothing to do but
- * start running. Otherwise, if this is the first
+ * For monitor+wds mode there's nothing to do but
+ * start running. Otherwise if this is the first
* vap to be brought up, start a scan which may be
* preempted if the station is locked to a particular
* channel.
*/
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) {
- ic->ic_state = IEEE80211_S_INIT; /* XXX*/
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ /* XXX needed? */
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0);
+ if (vap->iv_opmode == IEEE80211_M_MONITOR ||
+ vap->iv_opmode == IEEE80211_M_WDS)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_RUN, -1);
+ else
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
}
}
- return 0;
+}
+
+/*
+ * Start a single vap.
+ */
+void
+ieee80211_init(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+
+ /*
+ * This routine is publicly accessible through the vap's
+ * if_init method so guard against calls during detach.
+ * ieee80211_vap_detach null's the backpointer before
+ * tearing down state to signal any callback should be
+ * rejected/ignored.
+ */
+ if (vap != NULL) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", __func__);
+
+ IEEE80211_LOCK(vap->iv_ic);
+ ieee80211_start_locked(vap);
+ IEEE80211_UNLOCK(vap->iv_ic);
+ }
+}
+
+/*
+ * Start all runnable vap's on a device.
+ */
+void
+ieee80211_start_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_start_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop a vap. We force it down using the state machine
+ * then mark it's ifnet not running. If this is the last
+ * vap running on the underlying device then we close it
+ * too to insure it will be properly initialized when the
+ * next vap is brought up.
+ */
+void
+ieee80211_stop_locked(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifnet *parent = ic->ic_ifp;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "stop running, %d vaps running\n", ic->ic_nrunning);
+
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */
+ if (--ic->ic_nrunning == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING)) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "down parent %s\n", parent->if_xname);
+ parent->if_flags &= ~IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ }
+ }
+}
+
+void
+ieee80211_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_stop_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop all vap's running on a device.
+ */
+void
+ieee80211_stop_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -932,19 +1267,20 @@ ieee80211_init(struct ieee80211com *ic, int forcescan)
* the driver to effect the change.
*/
void
-ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
+ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
if (chan == NULL) { /* XXX should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no channel with freq %u flags 0x%x\n",
__func__, ic->ic_bsschan->ic_freq, newflags);
return;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
ieee80211_phymode_name[ieee80211_chan2mode(chan)],
@@ -960,57 +1296,21 @@ ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
+ struct ieee80211vap *vap;
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- /* XXX check ic_curchan != ic_bsschan? */
+ if (ic->ic_flags & IEEE80211_F_SCAN)
return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "beacon miss");
-
- /*
- * Our handling is only meaningful for stations that are
- * associated; any other conditions else will be handled
- * through different means (e.g. the tx timeout on mgt frames).
- */
- if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN)
- return;
-
- if (++ic->ic_bmiss_count < ic->ic_bmiss_max) {
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
- * Send a directed probe req before falling back to a scan;
- * if we receive a response ic_bmiss_count will be reset.
- * Some cards mistakenly report beacon miss so this avoids
- * the expensive scan if the ap is still there.
+ * We only pass events through for sta vap's in RUN state;
+ * may be too restrictive but for now this saves all the
+ * handlers duplicating these checks.
*/
- ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr,
- ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid,
- ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- return;
- }
- ic->ic_bmiss_count = 0;
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /*
- * If we receive a beacon miss interrupt when using
- * dynamic turbo, attempt to switch modes before
- * reassociating.
- */
- if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP))
- ieee80211_dturbo_switch(ic,
- ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
- /*
- * Try to reassociate before scanning for a new ap.
- */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
- } else {
- /*
- * Somebody else is controlling state changes (e.g.
- * a user-mode app) don't do anything that would
- * confuse them; just drop into scan mode so they'll
- * notified of the state change and given control.
- */
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
}
}
@@ -1019,377 +1319,378 @@ ieee80211_beacon_miss(struct ieee80211com *ic)
* were received in the last period. If not post a
* beacon miss; otherwise reset the counter.
*/
-static void
+void
ieee80211_swbmiss(void *arg)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211vap *vap = arg;
- if (ic->ic_swbmiss_count == 0) {
- ieee80211_beacon_miss(ic);
- if (ic->ic_bmiss_count == 0) /* don't re-arm timer */
+ if (vap->iv_swbmiss_count == 0) {
+ if (vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
+ if (vap->iv_bmiss_count == 0) /* don't re-arm timer */
return;
} else
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+}
+
+/*
+ * Start an 802.11h channel switch. We record the parameters,
+ * mark the operation pending, notify each vap through the
+ * beacon update mechanism so it can update the beacon frame
+ * contents, and then switch vap's to CSA state to block outbound
+ * traffic. Devices that handle CSA directly can use the state
+ * switch to do the right thing so long as they call
+ * ieee80211_csa_completeswitch when it's time to complete the
+ * channel change. Devices that depend on the net80211 layer can
+ * use ieee80211_beacon_update to handle the countdown and the
+ * channel switch.
+ */
+void
+ieee80211_csa_startswitch(struct ieee80211com *ic,
+ struct ieee80211_channel *c, int mode, int count)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ic->ic_csa_newchan = c;
+ ic->ic_csa_count = count;
+ /* XXX record mode? */
+ ic->ic_flags |= IEEE80211_F_CSAPENDING;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
+ /* switch to CSA state to block outbound traffic */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0);
+ }
+ ieee80211_notify_csa(ic, c, mode, count);
+}
+
+/*
+ * Complete an 802.11h channel switch started by ieee80211_csa_startswitch.
+ * We clear state and move all vap's in CSA state to RUN state
+ * so they can again transmit.
+ */
+void
+ieee80211_csa_completeswitch(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending"));
+
+ ieee80211_setcurchan(ic, ic->ic_csa_newchan);
+ ic->ic_csa_newchan = NULL;
+ ic->ic_flags &= ~IEEE80211_F_CSAPENDING;
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CSA)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+}
+
+/*
+ * Complete a DFS CAC started by ieee80211_dfs_cac_start.
+ * We clear state and move all vap's in CAC state to RUN state.
+ */
+void
+ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ /*
+ * Complete CAC state change for lead vap first; then
+ * clock all the other vap's waiting.
+ */
+ KASSERT(vap0->iv_state == IEEE80211_S_CAC,
+ ("wrong state %d", vap0->iv_state));
+ ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CAC)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_UNLOCK(ic);
}
+/*
+ * Force all vap's other than the specified vap to the INIT state
+ * and mark them as waiting for a scan to complete. These vaps
+ * will be brought up when the scan completes and the scanning vap
+ * reaches RUN state by wakeupwaiting.
+ * XXX if we do this in threads we can use sleep/wakeup.
+ */
static void
-sta_disassoc(void *arg, struct ieee80211_node *ni)
+markwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_node_leave(ic, ni);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_state != IEEE80211_S_INIT) {
+ vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ }
}
}
+/*
+ * Wakeup all vap's waiting for a scan to complete. This is the
+ * companion to markwaiting (above) and is used to coordinate
+ * multiple vaps scanning.
+ */
static void
-sta_deauth(void *arg, struct ieee80211_node *ni)
+wakeupwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_LEAVE);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ /* NB: sta's cannot go INIT->RUN */
+ vap->iv_newstate(vap,
+ vap->iv_opmode == IEEE80211_M_STA ?
+ IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
+ }
+ }
}
/*
- * Handle deauth with reason. We retry only for
- * the cases where we might succeed. Otherwise
- * we downgrade the ap and scan.
+ * Handle post state change work common to all operating modes.
*/
static void
-sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason)
+ieee80211_newstate_cb(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- switch (reason) {
- case IEEE80211_STATUS_SUCCESS:
- case IEEE80211_STATUS_TIMEOUT:
- case IEEE80211_REASON_ASSOC_EXPIRE:
- case IEEE80211_REASON_NOT_AUTHED:
- case IEEE80211_REASON_NOT_ASSOCED:
- case IEEE80211_REASON_ASSOC_LEAVE:
- case IEEE80211_REASON_ASSOC_NOT_AUTHED:
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- default:
- ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg);
+
+ if (nstate == IEEE80211_S_RUN) {
+ /*
+ * OACTIVE may be set on the vap if the upper layer
+ * tried to transmit (e.g. IPv6 NDP) before we reach
+ * RUN state. Clear it and restart xmit.
+ *
+ * Note this can also happen as a result of SLEEP->RUN
+ * (i.e. coming out of power save mode).
+ */
+ vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ if_start(vap->iv_ifp);
+
+ /* bring up any vaps waiting on us */
+ wakeupwaiting(vap);
+ } else if (nstate == IEEE80211_S_INIT) {
+ /*
+ * Flush the scan cache if we did the last scan (XXX?)
+ * and flush any frames on send queues from this vap.
+ * Note the mgt q is used only for legacy drivers and
+ * will go away shortly.
+ */
+ ieee80211_scan_flush(vap);
+
+ /* XXX NB: cast for altq */
+ ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
}
+ vap->iv_newstate_cb = NULL;
}
+/*
+ * Public interface for initiating a state machine change.
+ * This routine single-threads the request and coordinates
+ * the scheduling of multiple vaps for the purpose of selecting
+ * an operating channel. Specifically the following scenarios
+ * are handled:
+ * o only one vap can be selecting a channel so on transition to
+ * SCAN state if another vap is already scanning then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o only one vap can be doing CAC of a channel so on transition to
+ * CAC state if another vap is already scanning for radar then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o if another vap is already running when a request is made
+ * to SCAN then an operating channel has been chosen; bypass
+ * the scan and just join the channel
+ *
+ * Note that the state change call is done through the iv_newstate
+ * method pointer so any driver routine gets invoked. The driver
+ * will normally call back into operating mode-specific
+ * ieee80211_newstate routines (below) unless it needs to completely
+ * bypass the state machine (e.g. because the firmware has it's
+ * own idea how things should work). Bypassing the net80211 layer
+ * is usually a mistake and indicates lack of proper integration
+ * with the net80211 layer.
+ */
static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ieee80211_new_state_locked(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211vap *vp;
enum ieee80211_state ostate;
-
- ostate = ic->ic_state;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
- ic->ic_state = nstate; /* state transition */
- callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */
- if (ostate != IEEE80211_S_SCAN)
- ieee80211_cancel_scan(ic); /* background scan */
- ni = ic->ic_bss; /* NB: no reference held */
- if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)
- callout_stop(&ic->ic_swbmiss);
- switch (nstate) {
- case IEEE80211_S_INIT:
- switch (ostate) {
- case IEEE80211_S_INIT:
- break;
- case IEEE80211_S_RUN:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_sta_leave(ic, ni);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_LEAVE);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_deauth, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_SCAN:
- ieee80211_cancel_scan(ic);
- break;
- case IEEE80211_S_AUTH:
- break;
- default:
- break;
+ int nrunning, nscanning, rc;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ nrunning = nscanning = 0;
+ /* XXX can track this state instead of calculating */
+ TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
+ if (vp != vap) {
+ if (vp->iv_state >= IEEE80211_S_RUN)
+ nrunning++;
+ /* XXX doesn't handle bg scan */
+ /* NB: CAC+AUTH+ASSOC treated like SCAN */
+ else if (vp->iv_state > IEEE80211_S_INIT)
+ nscanning++;
}
- if (ostate != IEEE80211_S_INIT) {
- /* NB: optimize INIT -> INIT case */
- ieee80211_drain_ifq(&ic->ic_mgtq);
- ieee80211_reset_bss(ic);
- ieee80211_scan_flush(ic);
- }
- if (ic->ic_auth->ia_detach != NULL)
- ic->ic_auth->ia_detach(ic);
- break;
+ }
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate],
+ nrunning, nscanning);
+ switch (nstate) {
case IEEE80211_S_SCAN:
- switch (ostate) {
- case IEEE80211_S_INIT:
- createibss:
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
- /*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
- */
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic, ic->ic_des_chan);
- } else {
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_FLUSH,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- }
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
+ if (ostate == IEEE80211_S_INIT) {
/*
- * These can happen either because of a timeout
- * on an assoc/auth response or because of a
- * change in state that requires a reset. For
- * the former we're called with a non-zero arg
- * that is the cause for the failure; pass this
- * to the scan code so it can update state.
- * Otherwise trigger a new scan unless we're in
- * manual roaming mode in which case an application
- * must issue an explicit scan request.
+ * INIT -> SCAN happens on initial bringup.
*/
- if (arg != 0)
- ieee80211_scan_assoc_fail(ic,
- ic->ic_bss->ni_macaddr, arg);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
- case IEEE80211_S_RUN: /* beacon miss */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_sta_leave(ic, ni);
- ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid,
- ic->ic_des_ssid);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- goto createibss;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_AUTH:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- /* ??? */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- sta_authretry(ic, ni, arg>>8);
- break;
- }
- break;
- case IEEE80211_S_RUN:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- ic->ic_state = ostate; /* stay RUN */
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /* try to reauth */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- }
- break;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- break;
- case IEEE80211_S_RUN:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- IEEE80211_SEND_MGMT(ic, ni, arg ?
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_RUN:
- if (ic->ic_flags & IEEE80211_F_WPA) {
- /* XXX validate prerequisites */
- }
- switch (ostate) {
- case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS ||
- ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ KASSERT(!(nscanning && nrunning),
+ ("%d scanning and %d running", nscanning, nrunning));
+ if (nscanning) {
/*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
+ * Someone is scanning, defer our state
+ * change until the work has completed.
*/
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic,
- ic->ic_curchan, ic->ic_flags_ext));
- break;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /* fall thru... */
- case IEEE80211_S_AUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- /* fall thru... */
- case IEEE80211_S_RUN:
- break;
- case IEEE80211_S_SCAN: /* adhoc/hostap mode */
- case IEEE80211_S_ASSOC: /* infra mode */
- KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
- ("%s: bogus xmit rate %u setup\n", __func__,
- ni->ni_txrate));
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_debug(ic)) {
- if (ic->ic_opmode == IEEE80211_M_STA)
- if_printf(ifp, "associated ");
+ if (nrunning) {
+ /*
+ * Someone is operating; just join the channel
+ * they have chosen.
+ */
+ /* XXX kill arg? */
+ /* XXX check each opmode, adhoc? */
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ nstate = IEEE80211_S_SCAN;
else
- if_printf(ifp, "synchronized ");
- printf("with %s ssid ",
- ether_sprintf(ni->ni_bssid));
- ieee80211_print_essid(ic->ic_bss->ni_essid,
- ni->ni_esslen);
- printf(" channel %d start %uMb\n",
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
- }
+ nstate = IEEE80211_S_RUN;
+#ifdef IEEE80211_DEBUG
+ if (nstate != IEEE80211_S_SCAN) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE,
+ "%s: override, now %s -> %s\n",
+ __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ }
#endif
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_scan_assoc_success(ic,
- ni->ni_macaddr);
- ieee80211_notify_node_join(ic, ni,
- arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
- if_start(ifp); /* XXX not authorized yet */
- break;
- default:
- break;
+ } else {
+ /*
+ * SCAN was forced; e.g. on beacon miss. Force
+ * other running vap's to INIT state and mark
+ * them as waiting for the scan to complete. This
+ * insures they don't interfere with our scanning.
+ *
+ * XXX not always right, assumes ap follows sta
+ */
+ markwaiting(vap);
}
- if (ostate != IEEE80211_S_RUN &&
- ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) &&
+ nscanning) {
/*
- * Start s/w beacon miss timer for devices w/o
- * hardware support. We fudge a bit here since
- * we're doing this in software.
+ * Legacy WDS with someone else scanning; don't
+ * go online until that completes as we should
+ * follow the other vap to the channel they choose.
*/
- ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS(
- 2 * ic->ic_bmissthreshold * ni->ni_intval);
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s (legacy WDS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /*
- * Start/stop the authenticator when operating as an
- * AP. We delay until here to allow configuration to
- * happen out of order.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
- ic->ic_auth->ia_attach != NULL) {
- /* XXX check failure */
- ic->ic_auth->ia_attach(ic);
- } else if (ic->ic_auth->ia_detach != NULL) {
- ic->ic_auth->ia_detach(ic);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
+ !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) {
+ /*
+ * This is a DFS channel, transition to CAC state
+ * instead of RUN. This allows us to initiate
+ * Channel Availability Check (CAC) as specified
+ * by 11h/DFS.
+ */
+ nstate = IEEE80211_S_CAC;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: override %s -> %s (DFS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
}
- /*
- * When 802.1x is not in use mark the port authorized
- * at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- /*
- * Enable inactivity processing.
- * XXX
- */
- callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
- ieee80211_node_timeout, ic);
break;
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_INIT ) {
+ /* XXX don't believe this */
+ /* INIT -> INIT. nothing to do */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ }
+ /* fall thru... */
default:
break;
}
- return 0;
+ /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */
+ if (ostate != nstate) {
+ /*
+ * Arrange for work to happen after state change completes.
+ * If this happens asynchronously the caller must arrange
+ * for the com lock to be held.
+ */
+ vap->iv_newstate_cb = ieee80211_newstate_cb;
+ }
+ rc = vap->iv_newstate(vap, nstate, arg);
+ if (rc == 0 && vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, nstate, arg);
+done:
+ return rc;
+}
+
+int
+ieee80211_new_state(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int rc;
+
+ IEEE80211_LOCK(ic);
+ rc = ieee80211_new_state_locked(vap, nstate, arg);
+ IEEE80211_UNLOCK(ic);
+ return rc;
}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 9f94f1c..ec7061d 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,63 +44,65 @@ enum ieee80211_state {
};
#define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1)
-#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
- ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
-
-/*
- * The formation of some management frames requires guidance to
- * deal with legacy clients. When the client is identified as
- * "legacy 11b" this parameter can be passed in the arg param of a
- * IEEE80211_SEND_MGMT call.
- */
-#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
-#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
-#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+#define IEEE80211_SEND_MGMT(_ni,_type,_arg) \
+ ((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg))
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[];
void ieee80211_proto_attach(struct ieee80211com *);
void ieee80211_proto_detach(struct ieee80211com *);
+void ieee80211_proto_vattach(struct ieee80211vap *);
+void ieee80211_proto_vdetach(struct ieee80211vap *);
+
+void ieee80211_syncifflag_locked(struct ieee80211com *, int flag);
+void ieee80211_syncflag(struct ieee80211vap *, int flag);
+void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
-struct ieee80211_node;
-int ieee80211_input(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, uint32_t);
-void ieee80211_deliver_data(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct mbuf *ieee80211_decap1(struct mbuf *, int *);
-int ieee80211_setup_rates(struct ieee80211_node *ni,
- const uint8_t *rates, const uint8_t *xrates, int flags);
-void ieee80211_saveie(uint8_t **, const uint8_t *);
-void ieee80211_saveath(struct ieee80211_node *, uint8_t *);
-void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, int, uint32_t);
-int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *,
- struct mbuf *, int type);
+#define ieee80211_input(ni, m, rssi, noise, rstamp) \
+ ((ni)->ni_vap->iv_input(ni, m, rssi, noise, rstamp))
+int ieee80211_input_all(struct ieee80211com *, struct mbuf *,
+ int, int, uint32_t);
+int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int);
struct ieee80211_bpf_params;
int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
int ieee80211_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *);
+void ieee80211_start(struct ifnet *);
int ieee80211_send_nulldata(struct ieee80211_node *);
-int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
- int, int);
+int ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
+struct mbuf *ieee80211_encap(struct ieee80211_node *, struct mbuf *);
+int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
+struct ieee80211_appie;
int ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen);
-int ieee80211_classify(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
-struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
+ const uint8_t *ssid, size_t ssidlen);
+/*
+ * The formation of ProbeResponse frames requires guidance to
+ * deal with legacy clients. When the client is identified as
+ * "legacy 11b" ieee80211_send_proberesp is passed this token.
+ */
+#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
+#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
+#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+struct mbuf *ieee80211_alloc_proberesp(struct ieee80211_node *, int);
+int ieee80211_send_proberesp(struct ieee80211vap *,
+ const uint8_t da[IEEE80211_ADDR_LEN], int);
+struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
+struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
-int ieee80211_iserp_rateset(struct ieee80211com *,
- struct ieee80211_rateset *);
-void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+int ieee80211_iserp_rateset(const struct ieee80211_rateset *);
+void ieee80211_setbasicrates(struct ieee80211_rateset *,
+ enum ieee80211_phymode);
+void ieee80211_addbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
/*
@@ -146,16 +148,16 @@ ieee80211_anyhdrsize(const void *data)
/*
* Template for an in-kernel authenticator. Authenticators
* register with the protocol code and are typically loaded
- * as separate modules as needed.
+ * as separate modules as needed. One special authenticator
+ * is xauth; it intercepts requests so that protocols like
+ * WPA can be handled in user space.
*/
struct ieee80211_authenticator {
const char *ia_name; /* printable name */
- int (*ia_attach)(struct ieee80211com *);
- void (*ia_detach)(struct ieee80211com *);
- void (*ia_node_join)(struct ieee80211com *,
- struct ieee80211_node *);
- void (*ia_node_leave)(struct ieee80211com *,
- struct ieee80211_node *);
+ int (*ia_attach)(struct ieee80211vap *);
+ void (*ia_detach)(struct ieee80211vap *);
+ void (*ia_node_join)(struct ieee80211_node *);
+ void (*ia_node_leave)(struct ieee80211_node *);
};
void ieee80211_authenticator_register(int type,
const struct ieee80211_authenticator *);
@@ -166,23 +168,23 @@ struct ieee80211req;
/*
* Template for an MAC ACL policy module. Such modules
* register with the protocol code and are passed the sender's
- * address of each received frame for validation.
+ * address of each received auth frame for validation.
*/
struct ieee80211_aclator {
const char *iac_name; /* printable name */
- int (*iac_attach)(struct ieee80211com *);
- void (*iac_detach)(struct ieee80211com *);
- int (*iac_check)(struct ieee80211com *,
+ int (*iac_attach)(struct ieee80211vap *);
+ void (*iac_detach)(struct ieee80211vap *);
+ int (*iac_check)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_add)(struct ieee80211com *,
+ int (*iac_add)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_remove)(struct ieee80211com *,
+ int (*iac_remove)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_flush)(struct ieee80211com *);
- int (*iac_setpolicy)(struct ieee80211com *, int);
- int (*iac_getpolicy)(struct ieee80211com *);
- int (*iac_setioctl)(struct ieee80211com *, struct ieee80211req *);
- int (*iac_getioctl)(struct ieee80211com *, struct ieee80211req *);
+ int (*iac_flush)(struct ieee80211vap *);
+ int (*iac_setpolicy)(struct ieee80211vap *, int);
+ int (*iac_getpolicy)(struct ieee80211vap *);
+ int (*iac_setioctl)(struct ieee80211vap *, struct ieee80211req *);
+ int (*iac_getioctl)(struct ieee80211vap *, struct ieee80211req *);
};
void ieee80211_aclator_register(const struct ieee80211_aclator *);
void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
@@ -190,11 +192,12 @@ const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
/* flags for ieee80211_fix_rate() */
#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
-#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
+#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed legacy rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
+#define IEEE80211_F_DOFMCS 0x00000040 /* use fixed HT rate */
int ieee80211_fix_rate(struct ieee80211_node *,
struct ieee80211_rateset *, int);
@@ -233,15 +236,38 @@ struct ieee80211_wme_state {
int (*wme_update)(struct ieee80211com *);
};
-void ieee80211_wme_initparams(struct ieee80211com *);
-void ieee80211_wme_updateparams(struct ieee80211com *);
-void ieee80211_wme_updateparams_locked(struct ieee80211com *);
+void ieee80211_wme_initparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams_locked(struct ieee80211vap *);
+
+/*
+ * Return the WME TID from a QoS frame. If no TID
+ * is present return the index for the "non-QoS" entry.
+ */
+static __inline uint8_t
+ieee80211_gettid(const struct ieee80211_frame *wh)
+{
+ uint8_t tid;
+
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ tid = ((const struct ieee80211_qosframe *)wh)->
+ i_qos[0] & IEEE80211_QOS_TID;
+ tid++;
+ } else
+ tid = IEEE80211_NONQOS_TID;
+ return tid;
+}
-#define ieee80211_new_state(_ic, _nstate, _arg) \
- (((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-int ieee80211_init(struct ieee80211com *, int forcescan);
-void ieee80211_dturbo_switch(struct ieee80211com *, int newflags);
+void ieee80211_start_locked(struct ieee80211vap *);
+void ieee80211_init(void *);
+void ieee80211_start_all(struct ieee80211com *);
+void ieee80211_stop_locked(struct ieee80211vap *);
+void ieee80211_stop(struct ieee80211vap *);
+void ieee80211_stop_all(struct ieee80211com *);
+void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags);
+void ieee80211_swbmiss(void *arg);
void ieee80211_beacon_miss(struct ieee80211com *);
+int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int);
void ieee80211_print_essid(const uint8_t *, int);
void ieee80211_dump_pkt(struct ieee80211com *,
const uint8_t *, int, int, int);
@@ -275,7 +301,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
struct ieee80211_beacon_offsets *);
/*
- * Beacon frame updates are signaled through calls to ic_update_beacon
+ * Beacon frame updates are signaled through calls to iv_update_beacon
* with one of the IEEE80211_BEACON_* tokens defined below. For devices
* that construct beacon frames on the host this can trigger a rebuild
* or defer the processing. For devices that offload beacon frame
@@ -283,7 +309,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
* array in the ieee80211_beacon_offsets structure is intended to record
* deferred processing requirements; ieee80211_beacon_update uses the
* state to optimize work. Since this structure is owned by the driver
- * and not visible to the 802.11 layer drivers must supply an ic_update_beacon
+ * and not visible to the 802.11 layer drivers must supply an iv_update_beacon
* callback that marks the flag bits and schedules (as necessary) an update.
*/
enum {
@@ -299,14 +325,36 @@ enum {
int ieee80211_beacon_update(struct ieee80211_node *,
struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
+void ieee80211_csa_startswitch(struct ieee80211com *,
+ struct ieee80211_channel *, int mode, int count);
+void ieee80211_csa_completeswitch(struct ieee80211com *);
+void ieee80211_cac_completeswitch(struct ieee80211vap *);
+
/*
* Notification methods called from the 802.11 state machine.
* Note that while these are defined here, their implementation
* is OS-specific.
*/
-void ieee80211_notify_node_join(struct ieee80211com *,
- struct ieee80211_node *, int newassoc);
-void ieee80211_notify_node_leave(struct ieee80211com *,
- struct ieee80211_node *);
-void ieee80211_notify_scan_done(struct ieee80211com *);
+void ieee80211_notify_node_join(struct ieee80211_node *, int newassoc);
+void ieee80211_notify_node_leave(struct ieee80211_node *);
+void ieee80211_notify_scan_done(struct ieee80211vap *);
+void ieee80211_notify_wds_discover(struct ieee80211_node *);
+void ieee80211_notify_csa(struct ieee80211com *,
+ const struct ieee80211_channel *, int mode, int count);
+void ieee80211_notify_radar(struct ieee80211com *,
+ const struct ieee80211_channel *);
+enum ieee80211_notify_cac_event {
+ IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */
+ IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */
+ IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */
+ IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
+};
+void ieee80211_notify_cac(struct ieee80211com *,
+ const struct ieee80211_channel *,
+ enum ieee80211_notify_cac_event);
+void ieee80211_notify_node_deauth(struct ieee80211_node *);
+void ieee80211_notify_node_auth(struct ieee80211_node *);
+void ieee80211_notify_country(struct ieee80211vap *, const uint8_t [],
+ const uint8_t cc[2]);
+void ieee80211_notify_radio(struct ieee80211com *, int);
#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c
index 7f1b3dc..4cf2dc0 100644
--- a/sys/net80211/ieee80211_regdomain.c
+++ b/sys/net80211/ieee80211_regdomain.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 regdomain support.
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -37,26 +38,60 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_types.h>
-#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+static void
+null_getradiocaps(struct ieee80211com *ic, int *n, struct ieee80211_channel *c)
+{
+ /* just feed back the current channel list */
+ *n = ic->ic_nchans;
+ memcpy(c, ic->ic_channels,
+ ic->ic_nchans*sizeof(struct ieee80211_channel));
+}
+
+static int
+null_setregdomain(struct ieee80211com *ic,
+ struct ieee80211_regdomain *rd,
+ int nchans, struct ieee80211_channel chans[])
+{
+ return 0; /* accept anything */
+}
+
void
ieee80211_regdomain_attach(struct ieee80211com *ic)
{
- ic->ic_regdomain = 0; /* XXX */
- ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
- ic->ic_location = 1+2; /* both */
+ if (ic->ic_regdomain.regdomain == 0 &&
+ ic->ic_regdomain.country == CTRY_DEFAULT) {
+ ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */
+ ic->ic_regdomain.location = ' '; /* both */
+ ic->ic_regdomain.isocc[0] = 'U'; /* XXX */
+ ic->ic_regdomain.isocc[1] = 'S'; /* XXX */
+ /* XXX? too late to setup default channel list */
+ }
+ ic->ic_getradiocaps = null_getradiocaps;
+ ic->ic_setregdomain = null_setregdomain;
}
void
ieee80211_regdomain_detach(struct ieee80211com *ic)
{
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+}
+
+void
+ieee80211_regdomain_vattach(struct ieee80211vap *vap)
+{
+}
+
+void
+ieee80211_regdomain_vdetach(struct ieee80211vap *vap)
+{
}
static void
@@ -68,6 +103,7 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
c->ic_ieee = ieee;
c->ic_flags = flags;
+ c->ic_extieee = 0;
}
/*
@@ -76,24 +112,27 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
* when a driver does not obtain the channel list from another
* source (such as firmware).
*/
-void
+int
ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
+ const struct ieee80211_regdomain *rd, const uint8_t bands[])
{
int i;
/* XXX just do something for now */
ic->ic_nchans = 0;
- if (isset(&bands, IEEE80211_MODE_11B) ||
- isset(&bands, IEEE80211_MODE_11G)) {
- for (i = 1; i <= (ecm ? 14 : 11); i++) {
- if (isset(&bands, IEEE80211_MODE_11B))
+ if (isset(bands, IEEE80211_MODE_11B) ||
+ isset(bands, IEEE80211_MODE_11G)) {
+ int maxchan = 11;
+ if (rd != NULL && rd->ecm)
+ maxchan = 14;
+ for (i = 1; i <= maxchan; i++) {
+ if (isset(bands, IEEE80211_MODE_11B))
addchan(ic, i, IEEE80211_CHAN_B);
- if (isset(&bands, IEEE80211_MODE_11G))
+ if (isset(bands, IEEE80211_MODE_11G))
addchan(ic, i, IEEE80211_CHAN_G);
}
}
- if (isset(&bands, IEEE80211_MODE_11A)) {
+ if (isset(bands, IEEE80211_MODE_11A)) {
for (i = 36; i <= 64; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
for (i = 100; i <= 140; i += 4)
@@ -101,17 +140,73 @@ ieee80211_init_channels(struct ieee80211com *ic,
for (i = 149; i <= 161; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
}
- ic->ic_regdomain = rd;
- ic->ic_countrycode = cc;
- ic->ic_location = outdoor;
+ if (rd != NULL)
+ ic->ic_regdomain = *rd;
+
+ return 0;
+}
+
+static __inline int
+chancompar(const void *a, const void *b)
+{
+ const struct ieee80211_channel *ca = a;
+ const struct ieee80211_channel *cb = b;
+
+ return (ca->ic_freq == cb->ic_freq) ?
+ (ca->ic_flags & IEEE80211_CHAN_ALL) -
+ (cb->ic_flags & IEEE80211_CHAN_ALL) :
+ ca->ic_freq - cb->ic_freq;
+}
+
+/*
+ * Insertion sort.
+ */
+#define swap(_a, _b, _size) { \
+ uint8_t *s = _b; \
+ int i = _size; \
+ do { \
+ uint8_t tmp = *_a; \
+ *_a++ = *s; \
+ *s++ = tmp; \
+ } while (--i); \
+ _a -= _size; \
+}
+
+static void
+sort_channels(void *a, size_t n, size_t size)
+{
+ uint8_t *aa = a;
+ uint8_t *ai, *t;
+
+ KASSERT(n > 0, ("no channels"));
+ for (ai = aa+size; --n >= 1; ai += size)
+ for (t = ai; t > aa; t -= size) {
+ uint8_t *u = t - size;
+ if (chancompar(u, t) <= 0)
+ break;
+ swap(u, t, size);
+ }
}
+#undef swap
/*
- * Add Country Information IE.
+ * Order channels w/ the same frequency so that
+ * b < g < htg and a < hta. This is used to optimize
+ * channel table lookups and some user applications
+ * may also depend on it (though they should not).
*/
-uint8_t *
-ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
- enum ISOCountryCode cc, int location)
+void
+ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans)
+{
+ if (nchans > 0)
+ sort_channels(chans, nchans, sizeof(struct ieee80211_channel));
+}
+
+/*
+ * Allocate and construct a Country Information IE.
+ */
+struct ieee80211_appie *
+ieee80211_alloc_countryie(struct ieee80211com *ic)
{
#define CHAN_UNINTERESTING \
(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
@@ -131,35 +226,46 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */
CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */
};
- struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
- const char *iso_name;
- uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
- int i, skip;
+ const struct ieee80211_regdomain *rd = &ic->ic_regdomain;
+ uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm;
+ struct ieee80211_appie *aie;
+ struct ieee80211_country_ie *ie;
+ int i, skip, nruns;
+ aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
+ M_NOWAIT | M_ZERO);
+ if (aie == NULL) {
+ if_printf(ic->ic_ifp,
+ "%s: unable to allocate memory for country ie\n", __func__);
+ /* XXX stat */
+ return NULL;
+ }
+ ie = (struct ieee80211_country_ie *) aie->ie_data;
ie->ie = IEEE80211_ELEMID_COUNTRY;
- iso_name = ieee80211_cctoiso(cc);
- if (iso_name == NULL) {
- if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
- iso_name = " ";
+ if (rd->isocc[0] == '\0') {
+ if_printf(ic->ic_ifp, "no ISO country string for cc %d; "
+ "using blanks\n", rd->country);
+ ie->cc[0] = ie->cc[1] = ' ';
+ } else {
+ ie->cc[0] = rd->isocc[0];
+ ie->cc[1] = rd->isocc[1];
}
- ie->cc[0] = iso_name[0];
- ie->cc[1] = iso_name[1];
/*
- * Indoor/Outdoor portion of country string.
- * NB: this is not quite right, since we should have one of:
+ * Indoor/Outdoor portion of country string:
* 'I' indoor only
* 'O' outdoor only
* ' ' all enviroments
*/
- ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
-
+ ie->cc[2] = (rd->location == 'I' ? 'I' :
+ rd->location == 'O' ? 'O' : ' ');
/*
* Run-length encoded channel+max tx power info.
*/
frm = (uint8_t *)&ie->band[0];
nextchan = 0; /* NB: impossible channel # */
+ nruns = 0;
memset(chans, 0, sizeof(chans));
- skip = skipflags[ic->ic_curmode];
+ skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)];
for (i = 0; i < ic->ic_nchans; i++) {
const struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -170,12 +276,19 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
setbit(chans, c->ic_ieee);
if (c->ic_ieee != nextchan ||
c->ic_maxregpower != frm[-1]) { /* new run */
- /* XXX max of 83 runs */
+ if (nruns == IEEE80211_COUNTRY_MAX_BANDS) {
+ if_printf(ic->ic_ifp, "%s: country ie too big, "
+ "runs > max %d, truncating\n",
+ __func__, IEEE80211_COUNTRY_MAX_BANDS);
+ /* XXX stat? fail? */
+ break;
+ }
frm[0] = c->ic_ieee; /* starting channel # */
frm[1] = 1; /* # channels in run */
frm[2] = c->ic_maxregpower; /* tx power cap */
frm += 3;
nextchan = c->ic_ieee + 1; /* overflow? */
+ nruns++;
} else { /* extend run */
frm[-2]++;
nextchan++;
@@ -186,154 +299,114 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
ie->len++;
*frm++ = 0;
}
- return frm;
+ aie->ie_len = frm - aie->ie_data;
+
+ return aie;
#undef CHAN_UNINTERESTING
}
-/*
- * Country Code Table for code-to-string conversion.
- */
-static const struct {
- enum ISOCountryCode iso_code;
- const char* iso_name;
-} country_strings[] = {
- { CTRY_DEBUG, "DB" }, /* NB: nonstandard */
- { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */
- { CTRY_ALBANIA, "AL" },
- { CTRY_ALGERIA, "DZ" },
- { CTRY_ARGENTINA, "AR" },
- { CTRY_ARMENIA, "AM" },
- { CTRY_AUSTRALIA, "AU" },
- { CTRY_AUSTRIA, "AT" },
- { CTRY_AZERBAIJAN, "AZ" },
- { CTRY_BAHRAIN, "BH" },
- { CTRY_BELARUS, "BY" },
- { CTRY_BELGIUM, "BE" },
- { CTRY_BELIZE, "BZ" },
- { CTRY_BOLIVIA, "BO" },
- { CTRY_BRAZIL, "BR" },
- { CTRY_BRUNEI_DARUSSALAM, "BN" },
- { CTRY_BULGARIA, "BG" },
- { CTRY_CANADA, "CA" },
- { CTRY_CHILE, "CL" },
- { CTRY_CHINA, "CN" },
- { CTRY_COLOMBIA, "CO" },
- { CTRY_COSTA_RICA, "CR" },
- { CTRY_CROATIA, "HR" },
- { CTRY_CYPRUS, "CY" },
- { CTRY_CZECH, "CZ" },
- { CTRY_DENMARK, "DK" },
- { CTRY_DOMINICAN_REPUBLIC, "DO" },
- { CTRY_ECUADOR, "EC" },
- { CTRY_EGYPT, "EG" },
- { CTRY_EL_SALVADOR, "SV" },
- { CTRY_ESTONIA, "EE" },
- { CTRY_FINLAND, "FI" },
- { CTRY_FRANCE, "FR" },
- { CTRY_FRANCE2, "F2" },
- { CTRY_GEORGIA, "GE" },
- { CTRY_GERMANY, "DE" },
- { CTRY_GREECE, "GR" },
- { CTRY_GUATEMALA, "GT" },
- { CTRY_HONDURAS, "HN" },
- { CTRY_HONG_KONG, "HK" },
- { CTRY_HUNGARY, "HU" },
- { CTRY_ICELAND, "IS" },
- { CTRY_INDIA, "IN" },
- { CTRY_INDONESIA, "ID" },
- { CTRY_IRAN, "IR" },
- { CTRY_IRELAND, "IE" },
- { CTRY_ISRAEL, "IL" },
- { CTRY_ITALY, "IT" },
- { CTRY_JAMAICA, "JM" },
- { CTRY_JAPAN, "JP" },
- { CTRY_JAPAN1, "J1" },
- { CTRY_JAPAN2, "J2" },
- { CTRY_JAPAN3, "J3" },
- { CTRY_JAPAN4, "J4" },
- { CTRY_JAPAN5, "J5" },
- { CTRY_JORDAN, "JO" },
- { CTRY_KAZAKHSTAN, "KZ" },
- { CTRY_KOREA_NORTH, "KP" },
- { CTRY_KOREA_ROC, "KR" },
- { CTRY_KOREA_ROC2, "K2" },
- { CTRY_KUWAIT, "KW" },
- { CTRY_LATVIA, "LV" },
- { CTRY_LEBANON, "LB" },
- { CTRY_LIECHTENSTEIN, "LI" },
- { CTRY_LITHUANIA, "LT" },
- { CTRY_LUXEMBOURG, "LU" },
- { CTRY_MACAU, "MO" },
- { CTRY_MACEDONIA, "MK" },
- { CTRY_MALAYSIA, "MY" },
- { CTRY_MEXICO, "MX" },
- { CTRY_MONACO, "MC" },
- { CTRY_MOROCCO, "MA" },
- { CTRY_NETHERLANDS, "NL" },
- { CTRY_NEW_ZEALAND, "NZ" },
- { CTRY_NORWAY, "NO" },
- { CTRY_OMAN, "OM" },
- { CTRY_PAKISTAN, "PK" },
- { CTRY_PANAMA, "PA" },
- { CTRY_PERU, "PE" },
- { CTRY_PHILIPPINES, "PH" },
- { CTRY_POLAND, "PL" },
- { CTRY_PORTUGAL, "PT" },
- { CTRY_PUERTO_RICO, "PR" },
- { CTRY_QATAR, "QA" },
- { CTRY_ROMANIA, "RO" },
- { CTRY_RUSSIA, "RU" },
- { CTRY_SAUDI_ARABIA, "SA" },
- { CTRY_SINGAPORE, "SG" },
- { CTRY_SLOVAKIA, "SK" },
- { CTRY_SLOVENIA, "SI" },
- { CTRY_SOUTH_AFRICA, "ZA" },
- { CTRY_SPAIN, "ES" },
- { CTRY_SWEDEN, "SE" },
- { CTRY_SWITZERLAND, "CH" },
- { CTRY_SYRIA, "SY" },
- { CTRY_TAIWAN, "TW" },
- { CTRY_THAILAND, "TH" },
- { CTRY_TRINIDAD_Y_TOBAGO, "TT" },
- { CTRY_TUNISIA, "TN" },
- { CTRY_TURKEY, "TR" },
- { CTRY_UKRAINE, "UA" },
- { CTRY_UAE, "AE" },
- { CTRY_UNITED_KINGDOM, "GB" },
- { CTRY_UNITED_STATES, "US" },
- { CTRY_URUGUAY, "UY" },
- { CTRY_UZBEKISTAN, "UZ" },
- { CTRY_VENEZUELA, "VE" },
- { CTRY_VIET_NAM, "VN" },
- { CTRY_YEMEN, "YE" },
- { CTRY_ZIMBABWE, "ZW" }
-};
-
-const char *
-ieee80211_cctoiso(enum ISOCountryCode cc)
+static int
+allvapsdown(struct ieee80211com *ic)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211vap *vap;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_code == cc)
- return country_strings[i].iso_name;
- }
- return NULL;
-#undef N
+ IEEE80211_LOCK_ASSERT(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state != IEEE80211_S_INIT)
+ return 0;
+ return 1;
}
int
-ieee80211_isotocc(const char iso[2])
+ieee80211_setregdomain(struct ieee80211vap *vap,
+ struct ieee80211_regdomain_req *reg)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+ int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */
+ int error, i;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_name[0] == iso[0] &&
- country_strings[i].iso_name[1] == iso[1])
- return country_strings[i].iso_code;
+ if (reg->rd.location != 'I' && reg->rd.location != 'O' &&
+ reg->rd.location != ' ')
+ return EINVAL;
+ if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0')
+ return EINVAL;
+ if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX)
+ return EINVAL;
+ /*
+ * Calculate freq<->IEEE mapping and default max tx power
+ * for channels not setup. The driver can override these
+ * setting to reflect device properties/requirements.
+ */
+ for (i = 0; i < reg->chaninfo.ic_nchans; i++) {
+ c = &reg->chaninfo.ic_chans[i];
+ if (c->ic_freq == 0 || c->ic_flags == 0)
+ return EINVAL;
+ if (c->ic_maxregpower == 0)
+ return EINVAL;
+ if (c->ic_ieee == 0)
+ c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
+ c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
+ (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
+ c->ic_flags);
+ if (c->ic_maxpower == 0)
+ c->ic_maxpower = 2*c->ic_maxregpower;
+ }
+ IEEE80211_LOCK(ic);
+ error = ic->ic_setregdomain(ic, &reg->rd,
+ reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
+ if (error != 0) {
+ IEEE80211_UNLOCK(ic);
+ return error;
}
- return -1;
-#undef N
+ /* XXX bandaid; a running vap will likely crash */
+ if (!allvapsdown(ic)) {
+ IEEE80211_UNLOCK(ic);
+ return EBUSY;
+ }
+ /*
+ * Commit: copy in new channel table and reset media state.
+ * On return the state machines will be clocked so all vaps
+ * will reset their state.
+ *
+ * XXX ic_bsschan is marked undefined, must have vap's in
+ * INIT state or we blow up forcing stations off
+ */
+ /*
+ * Save any desired channel for restore below. Note this
+ * needs to be done for all vaps but for now we only do
+ * the one where the ioctl is issued.
+ */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ desfreq = vap->iv_des_chan->ic_freq;
+ desflags = vap->iv_des_chan->ic_flags;
+ }
+ /* regdomain parameters */
+ memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
+ /* channel table */
+ memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
+ reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
+ ic->ic_nchans = reg->chaninfo.ic_nchans;
+ memset(&ic->ic_channels[ic->ic_nchans], 0,
+ (IEEE80211_CHAN_MAX - ic->ic_nchans) *
+ sizeof(struct ieee80211_channel));
+ ieee80211_media_init(ic);
+
+ /*
+ * Invalidate channel-related state.
+ */
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+ ieee80211_scan_flush(vap);
+ ieee80211_dfs_reset(ic);
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ /* NB: may be NULL if not present in new channel list */
+ vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags);
+ }
+ IEEE80211_UNLOCK(ic);
+
+ return 0;
}
diff --git a/sys/net80211/ieee80211_regdomain.h b/sys/net80211/ieee80211_regdomain.h
index 9c1345e..c9c0823 100644
--- a/sys/net80211/ieee80211_regdomain.h
+++ b/sys/net80211/ieee80211_regdomain.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,42 +43,92 @@ enum ISOCountryCode {
CTRY_ANDORRA = 20,
CTRY_ANGOLA = 24,
CTRY_ANGUILLA = 660,
- /* XXX correct remainder */
+ CTRY_ANTARTICA = 10,
+ CTRY_ANTIGUA = 28, /* Antigua and Barbuda */
CTRY_ARGENTINA = 32, /* Argentina */
CTRY_ARMENIA = 51, /* Armenia */
+ CTRY_ARUBA = 533, /* Aruba */
CTRY_AUSTRALIA = 36, /* Australia */
CTRY_AUSTRIA = 40, /* Austria */
CTRY_AZERBAIJAN = 31, /* Azerbaijan */
+ CTRY_BAHAMAS = 44, /* Bahamas */
CTRY_BAHRAIN = 48, /* Bahrain */
+ CTRY_BANGLADESH = 50, /* Bangladesh */
+ CTRY_BARBADOS = 52,
CTRY_BELARUS = 112, /* Belarus */
CTRY_BELGIUM = 56, /* Belgium */
- CTRY_BELIZE = 84, /* Belize */
+ CTRY_BELIZE = 84,
+ CTRY_BENIN = 204,
+ CTRY_BERMUDA = 60,
+ CTRY_BHUTAN = 64,
CTRY_BOLIVIA = 68, /* Bolivia */
+ CTRY_BOSNIA_AND_HERZEGOWINA = 70,
+ CTRY_BOTSWANA = 72,
+ CTRY_BOUVET_ISLAND = 74,
CTRY_BRAZIL = 76, /* Brazil */
+ CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86,
CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */
CTRY_BULGARIA = 100, /* Bulgaria */
+ CTRY_BURKINA_FASO = 854,
+ CTRY_BURUNDI = 108,
+ CTRY_CAMBODIA = 116,
+ CTRY_CAMEROON = 120,
CTRY_CANADA = 124, /* Canada */
+ CTRY_CAPE_VERDE = 132,
+ CTRY_CAYMAN_ISLANDS = 136,
+ CTRY_CENTRAL_AFRICAN_REPUBLIC = 140,
+ CTRY_CHAD = 148,
CTRY_CHILE = 152, /* Chile */
CTRY_CHINA = 156, /* People's Republic of China */
+ CTRY_CHRISTMAS_ISLAND = 162,
+ CTRY_COCOS_ISLANDS = 166,
CTRY_COLOMBIA = 170, /* Colombia */
+ CTRY_COMOROS = 174,
+ CTRY_CONGO = 178,
+ CTRY_COOK_ISLANDS = 184,
CTRY_COSTA_RICA = 188, /* Costa Rica */
- CTRY_CROATIA = 191, /* Croatia */
+ CTRY_COTE_DIVOIRE = 384,
+ CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */
CTRY_CYPRUS = 196, /* Cyprus */
CTRY_CZECH = 203, /* Czech Republic */
CTRY_DENMARK = 208, /* Denmark */
+ CTRY_DJIBOUTI = 262,
+ CTRY_DOMINICA = 212,
CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
+ CTRY_EAST_TIMOR = 626,
CTRY_ECUADOR = 218, /* Ecuador */
CTRY_EGYPT = 818, /* Egypt */
CTRY_EL_SALVADOR = 222, /* El Salvador */
+ CTRY_EQUATORIAL_GUINEA = 226,
+ CTRY_ERITREA = 232,
CTRY_ESTONIA = 233, /* Estonia */
+ CTRY_ETHIOPIA = 210,
+ CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */
CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */
+ CTRY_FIJI = 242,
CTRY_FINLAND = 246, /* Finland */
CTRY_FRANCE = 250, /* France */
- CTRY_FRANCE2 = 255, /* France2 */
+ CTRY_FRANCE2 = 255, /* France (Metropolitan) */
+ CTRY_FRENCH_GUIANA = 254,
+ CTRY_FRENCH_POLYNESIA = 258,
+ CTRY_FRENCH_SOUTHERN_TERRITORIES = 260,
+ CTRY_GABON = 266,
+ CTRY_GAMBIA = 270,
CTRY_GEORGIA = 268, /* Georgia */
CTRY_GERMANY = 276, /* Germany */
+ CTRY_GHANA = 288,
+ CTRY_GIBRALTAR = 292,
CTRY_GREECE = 300, /* Greece */
+ CTRY_GREENLAND = 304,
+ CTRY_GRENADA = 308,
+ CTRY_GUADELOUPE = 312,
+ CTRY_GUAM = 316,
CTRY_GUATEMALA = 320, /* Guatemala */
+ CTRY_GUINEA = 324,
+ CTRY_GUINEA_BISSAU = 624,
+ CTRY_GUYANA = 328,
+ /* XXX correct remainder */
+ CTRY_HAITI = 332,
CTRY_HONDURAS = 340, /* Honduras */
CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */
CTRY_HUNGARY = 348, /* Hungary */
@@ -113,9 +163,11 @@ enum ISOCountryCode {
CTRY_MACAU = 446, /* Macau */
CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */
CTRY_MALAYSIA = 458, /* Malaysia */
+ CTRY_MALTA = 470, /* Malta */
CTRY_MEXICO = 484, /* Mexico */
CTRY_MONACO = 492, /* Principality of Monaco */
CTRY_MOROCCO = 504, /* Morocco */
+ CTRY_NEPAL = 524, /* Nepal */
CTRY_NETHERLANDS = 528, /* Netherlands */
CTRY_NEW_ZEALAND = 554, /* New Zealand */
CTRY_NICARAGUA = 558, /* Nicaragua */
@@ -138,6 +190,7 @@ enum ISOCountryCode {
CTRY_SLOVENIA = 705, /* Slovenia */
CTRY_SOUTH_AFRICA = 710, /* South Africa */
CTRY_SPAIN = 724, /* Spain */
+ CTRY_SRILANKA = 144, /* Sri Lanka */
CTRY_SWEDEN = 752, /* Sweden */
CTRY_SWITZERLAND = 756, /* Switzerland */
CTRY_SYRIA = 760, /* Syria */
@@ -158,18 +211,39 @@ enum ISOCountryCode {
CTRY_ZIMBABWE = 716, /* Zimbabwe */
};
+enum RegdomainCode {
+ SKU_FCC = 0x10, /* FCC, aka United States */
+ SKU_CA = 0x20, /* North America, aka Canada */
+ SKU_ETSI = 0x30, /* Europe */
+ SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */
+ SKU_ETSI3 = 0x33, /* Europe - channel 36 */
+ SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */
+ SKU_JAPAN = 0x40,
+ SKU_KOREA = 0x45,
+ SKU_APAC = 0x50, /* Asia Pacific */
+ SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */
+ SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */
+ SKU_ROW = 0x81, /* China/Taiwan/Rest of World */
+ SKU_NONE = 0xf0, /* "Region Free" */
+ SKU_DEBUG = 0x1ff
+};
+
#if defined(__KERNEL__) || defined(_KERNEL)
#define CTRY_DEBUG 0x1ff /* debug */
#define CTRY_DEFAULT 0 /* default */
void ieee80211_regdomain_attach(struct ieee80211com *);
void ieee80211_regdomain_detach(struct ieee80211com *);
+void ieee80211_regdomain_vattach(struct ieee80211vap *);
+void ieee80211_regdomain_vdetach(struct ieee80211vap *);
-void ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm);
-uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *,
- enum ISOCountryCode cc, int location);
-const char *ieee80211_cctoiso(enum ISOCountryCode);
-int ieee80211_isotocc(const char iso[2]);
+int ieee80211_init_channels(struct ieee80211com *,
+ const struct ieee80211_regdomain *, const uint8_t bands[]);
+void ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans);
+struct ieee80211_appie;
+struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *);
+struct ieee80211_regdomain_req;
+int ieee80211_setregdomain(struct ieee80211vap *,
+ struct ieee80211_regdomain_req *);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */
diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c
new file mode 100644
index 0000000..f1fc409
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.c
@@ -0,0 +1,273 @@
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
+ * Young 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.
+ */
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_rssadapt.h>
+
+struct rssadapt_expavgctl {
+ /* RSS threshold decay. */
+ u_int rc_decay_denom;
+ u_int rc_decay_old;
+ /* RSS threshold update. */
+ u_int rc_thresh_denom;
+ u_int rc_thresh_old;
+ /* RSS average update. */
+ u_int rc_avgrssi_denom;
+ u_int rc_avgrssi_old;
+};
+
+static struct rssadapt_expavgctl master_expavgctl = {
+ rc_decay_denom : 16,
+ rc_decay_old : 15,
+ rc_thresh_denom : 8,
+ rc_thresh_old : 4,
+ rc_avgrssi_denom : 8,
+ rc_avgrssi_old : 4
+};
+
+#ifdef interpolate
+#undef interpolate
+#endif
+#define interpolate(parm, old, new) ((parm##_old * (old) + \
+ (parm##_denom - parm##_old) * (new)) / \
+ parm##_denom)
+
+static void rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *rs, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ rs->interval = (t < 1) ? 1 : t;
+}
+
+void
+ieee80211_rssadapt_init(struct ieee80211_rssadapt *rs, struct ieee80211vap *vap, int interval)
+{
+ rs->vap = vap;
+ ieee80211_rssadapt_setinterval(rs, interval);
+
+ rssadapt_sysctlattach(rs, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *rs)
+{
+}
+
+static void
+rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
+{
+ long interval;
+
+ ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
+ ra->ra_nfail = ra->ra_nok = 0;
+
+ /*
+ * A node is eligible for its rate to be raised every 1/10 to 10
+ * seconds, more eligible in proportion to recent packet rates.
+ */
+ interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
+ ra->ra_raise_interval = msecs_to_ticks(interval);
+}
+
+void
+ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *rsa,
+ struct ieee80211_rssadapt_node *ra, struct ieee80211_node *ni)
+{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ ra->ra_rs = rsa;
+ ra->ra_rates = *rs;
+ rssadapt_updatestats(ra);
+
+ /* pick initial rate */
+ for (ra->ra_rix = rs->rs_nrates - 1;
+ ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
+ ra->ra_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
+ ra->ra_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT initial rate %d", ni->ni_txrate);
+}
+
+static __inline int
+bucket(int pktlen)
+{
+ int i, top, thridx;
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thridx = i;
+ if (pktlen <= top)
+ break;
+ }
+ return thridx;
+}
+
+int
+ieee80211_rssadapt_choose(struct ieee80211_node *ni,
+ struct ieee80211_rssadapt_node *ra, u_int pktlen)
+{
+ const struct ieee80211_rateset *rs = &ra->ra_rates;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int rix, rssi;
+
+ if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
+ rssadapt_updatestats(ra);
+ ra->ra_ticks = ticks;
+ }
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ /* XXX this is average rssi, should be using last value */
+ rssi = ni->ni_ic->ic_node_getrssi(ni);
+ for (rix = rs->rs_nrates-1; rix >= 0; rix--)
+ if ((*thrs)[rix] < (rssi << 8))
+ break;
+ if (rix != ra->ra_rix) {
+ /* update public rate */
+ ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ ra->ra_rix = rix;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT new rate %d (pktlen %d rssi %d)",
+ ni->ni_txrate, pktlen, rssi);
+ }
+ return rix;
+}
+
+/*
+ * Adapt the data rate to suit the conditions. When a transmitted
+ * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t last_thr;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ u_int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ last_thr = (*thrs)[rix];
+ (*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
+ last_thr, (rssi << 8));
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ last_thr, (*thrs)[rix], rssi);
+}
+
+void
+ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ uint16_t newthr, oldthr;
+ int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ if ((*thrs)[rix + 1] > (*thrs)[rix]) {
+ oldthr = (*thrs)[rix + 1];
+ if ((*thrs)[rix] == 0)
+ newthr = (rssi << 8);
+ else
+ newthr = (*thrs)[rix];
+ (*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
+ oldthr, newthr);
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ oldthr, newthr, rssi);
+
+ ra->ra_last_raise = ticks;
+ }
+}
+
+static int
+rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211_rssadapt *rs = arg1;
+ int msecs = ticks_to_msecs(rs->interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_rssadapt_setinterval(rs, msecs);
+ return 0;
+}
+
+static void
+rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, rs,
+ 0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
+}
+
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(rssadapt, 1);
diff --git a/sys/dev/ral/if_ralrate.h b/sys/net80211/ieee80211_rssadapt.h
index 50eee44..b454f43 100644
--- a/sys/dev/ral/if_ralrate.h
+++ b/sys/net80211/ieee80211_rssadapt.h
@@ -29,6 +29,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
+#ifndef _NET80211_IEEE80211_RSSADAPT_H_
+#define _NET80211_IEEE80211_RSSADAPT_H_
/* Data-rate adaptation loosely based on "Link Adaptation Strategy
* for IEEE 802.11 WLAN via Received Signal Strength Measurement"
@@ -36,63 +38,64 @@
*/
/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
-#define RAL_RSSADAPT_BKTS 3
-#define RAL_RSSADAPT_BKT0 128
-#define RAL_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
+#define IEEE80211_RSSADAPT_BKTS 3
+#define IEEE80211_RSSADAPT_BKT0 128
+#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
-#define ral_rssadapt_thresh_new \
- (ral_rssadapt_thresh_denom - ral_rssadapt_thresh_old)
-#define ral_rssadapt_decay_new \
- (ral_rssadapt_decay_denom - ral_rssadapt_decay_old)
-#define ral_rssadapt_avgrssi_new \
- (ral_rssadapt_avgrssi_denom - ral_rssadapt_avgrssi_old)
-
-struct ral_rssadapt_expavgctl {
- /* RSS threshold decay. */
- u_int rc_decay_denom;
- u_int rc_decay_old;
- /* RSS threshold update. */
- u_int rc_thresh_denom;
- u_int rc_thresh_old;
- /* RSS average update. */
- u_int rc_avgrssi_denom;
- u_int rc_avgrssi_old;
+struct ieee80211_rssadapt {
+ struct ieee80211vap *vap;
+ int interval; /* update interval (ticks) */
};
-struct ral_rssadapt {
- /* exponential average RSSI << 8 */
- u_int16_t ra_avg_rssi;
+struct ieee80211_rssadapt_node {
+ struct ieee80211_rssadapt *ra_rs; /* backpointer */
+ struct ieee80211_rateset ra_rates; /* negotiated rates */
+ int ra_rix; /* current rate index */
+ int ra_ticks; /* time of last update */
+ int ra_last_raise; /* time of last rate raise */
+ int ra_raise_interval; /* rate raise time threshold */
+
/* Tx failures in this update interval */
- u_int32_t ra_nfail;
+ uint32_t ra_nfail;
/* Tx successes in this update interval */
- u_int32_t ra_nok;
+ uint32_t ra_nok;
/* exponential average packets/second */
- u_int32_t ra_pktrate;
+ uint32_t ra_pktrate;
/* RSSI threshold for each Tx rate */
- u_int16_t ra_rate_thresh[RAL_RSSADAPT_BKTS]
+ uint16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS]
[IEEE80211_RATE_SIZE];
- struct timeval ra_last_raise;
- struct timeval ra_raise_interval;
};
-/* Properties of a Tx packet, for link adaptation. */
-struct ral_rssdesc {
- u_int id_len; /* Tx packet length */
- u_int id_rateidx; /* index into ni->ni_rates */
- struct ieee80211_node *id_node; /* destination STA MAC */
- u_int8_t id_rssi; /* destination STA avg RSS @
- * Tx time
- */
-};
+void ieee80211_rssadapt_init(struct ieee80211_rssadapt *,
+ struct ieee80211vap *, int);
+void ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *);
+void ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *, int);
+void ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *,
+ struct ieee80211_rssadapt_node *, struct ieee80211_node *);
+int ieee80211_rssadapt_choose(struct ieee80211_node *,
+ struct ieee80211_rssadapt_node *, u_int);
+
+/* NB: these are public only for the inline below */
+void ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+void ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+
+#define IEEE80211_RSSADAPT_SUCCESS 1
+#define IEEE80211_RSSADAPT_FAILURE 0
-void ral_rssadapt_updatestats(struct ral_rssadapt *);
-void ral_rssadapt_input(struct ieee80211com *, struct ieee80211_node *,
- struct ral_rssadapt *, int);
-void ral_rssadapt_lower_rate(struct ieee80211com *,
- struct ieee80211_node *, struct ral_rssadapt *,
- struct ral_rssdesc *);
-void ral_rssadapt_raise_rate(struct ieee80211com *,
- struct ral_rssadapt *, struct ral_rssdesc *);
-int ral_rssadapt_choose(struct ral_rssadapt *,
- struct ieee80211_rateset *, struct ieee80211_frame *, u_int,
- const char *, int);
+static __inline void
+ieee80211_rssadapt_tx_complete(struct ieee80211_rssadapt_node *ra,
+ int success, int pktlen, int rssi)
+{
+ if (success) {
+ ra->ra_nok++;
+ if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
+ (ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
+ ieee80211_rssadapt_raise_rate(ra, pktlen, rssi);
+ } else {
+ ra->ra_nfail++;
+ ieee80211_rssadapt_lower_rate(ra, pktlen, rssi);
+ }
+}
+#endif /* _NET80211_IEEE80211_RSSADAPT_H_ */
diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c
index 6f6b9a6..cec9673 100644
--- a/sys/net80211/ieee80211_scan.c
+++ b/sys/net80211/ieee80211_scan.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -72,7 +74,7 @@ struct scan_state {
/*
* Roaming-related defaults. RSSI thresholds are as returned by the
* driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e
- * .5M units).
+ * .5M units) or MCS.
*/
#define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */
#define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */
@@ -80,10 +82,11 @@ struct scan_state {
#define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */
#define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */
#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */
+#define ROAM_MCS_11N_DEFAULT 1 /* tx MCS thresh for 11n bss*/
static void scan_restart_pwrsav(void *);
-static void scan_curchan(struct ieee80211com *, unsigned long);
-static void scan_mindwell(struct ieee80211com *);
+static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void scan_mindwell(struct ieee80211_scan_state *);
static void scan_next(void *);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@@ -93,29 +96,17 @@ ieee80211_scan_attach(struct ieee80211com *ic)
{
struct scan_state *ss;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
-
MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
M_80211_SCAN, M_NOWAIT | M_ZERO);
if (ss == NULL) {
ic->ic_scan = NULL;
return;
}
- callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE);
+ callout_init_mtx(&ss->ss_scan_timer, &ic->ic_comlock, 0);
ic->ic_scan = &ss->base;
ic->ic_scan_curchan = scan_curchan;
ic->ic_scan_mindwell = scan_mindwell;
-
- ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
- ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
- ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
- ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
- ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
- ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
- ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT;
- ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT;
- ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
}
void
@@ -135,31 +126,94 @@ ieee80211_scan_detach(struct ieee80211com *ic)
}
}
+static __inline void
+setparams(struct ieee80211_roamparam *rp, int8_t rssi, uint8_t txrate)
+{
+ rp->rssi = rssi;
+ rp->rate = txrate;
+}
+
+void
+ieee80211_scan_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
+ vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
+ vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
+
+ vap->iv_roaming = IEEE80211_ROAMING_AUTO;
+
+ /* NB: only set supported modes so user apps can identify */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11A))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11A],
+ ROAM_RSSI_11A_DEFAULT, ROAM_RATE_11A_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11G))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11G],
+ ROAM_RSSI_11B_DEFAULT, ROAM_RATE_11B_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11B))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11B],
+ ROAM_RSSI_11BONLY_DEFAULT, ROAM_RATE_11BONLY_DEFAULT);
+ /* NB: default turbo controls to be the same as !turbo */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_G))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_G] =
+ vap->iv_roamparms[IEEE80211_MODE_11G];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_STURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_STURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NA],
+ ROAM_RSSI_11A_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NG],
+ ROAM_RSSI_11B_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+}
+
+void
+ieee80211_scan_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
+ if (ss != NULL && ss->ss_vap == vap) {
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ /* XXX callout_drain */
+ callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ }
+ if (ss->ss_ops != NULL) {
+ ss->ss_ops->scan_detach(ss);
+ ss->ss_ops = NULL;
+ }
+ ss->ss_vap = NULL;
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Simple-minded scanner module support.
*/
-#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)
-
-static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
+static const char *scan_modnames[IEEE80211_OPMODE_MAX] = {
"wlan_scan_sta", /* IEEE80211_M_IBSS */
"wlan_scan_sta", /* IEEE80211_M_STA */
"wlan_scan_wds", /* IEEE80211_M_WDS */
"wlan_scan_sta", /* IEEE80211_M_AHDEMO */
- "wlan_scan_4", /* n/a */
- "wlan_scan_5", /* n/a */
"wlan_scan_ap", /* IEEE80211_M_HOSTAP */
- "wlan_scan_7", /* n/a */
"wlan_scan_monitor", /* IEEE80211_M_MONITOR */
};
-static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
+static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX];
const struct ieee80211_scanner *
ieee80211_scanner_get(enum ieee80211_opmode mode)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return NULL;
- /* NB: avoid monitor mode; there is no scan support */
- if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL)
+ if (scanners[mode] == NULL)
ieee80211_load_module(scan_modnames[mode]);
return scanners[mode];
}
@@ -168,7 +222,7 @@ void
ieee80211_scanner_register(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
scanners[mode] = scan;
}
@@ -177,7 +231,7 @@ void
ieee80211_scanner_unregister(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
if (scanners[mode] == scan)
scanners[mode] = NULL;
@@ -188,7 +242,7 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
{
int m;
- for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
+ for (m = 0; m < IEEE80211_OPMODE_MAX; m++)
if (scanners[m] == scan)
scanners[m] = NULL;
}
@@ -201,35 +255,50 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
* ensure later callbacks find ss_ops set to properly
* reflect current operating mode.
*/
-int
-ieee80211_scan_update(struct ieee80211com *ic)
+static void
+scan_update_locked(struct ieee80211vap *vap,
+ const struct ieee80211_scanner *scan)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- const struct ieee80211_scanner *scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- IEEE80211_LOCK(ic);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
+ IEEE80211_LOCK_ASSERT(ic);
+
+#ifdef IEEE80211_DEBUG
+ if (ss->ss_vap != vap || ss->ss_ops != scan) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: current scanner is <%s:%s>, switch to <%s:%s>\n",
+ __func__,
+ ss->ss_vap != NULL ?
+ ss->ss_vap->iv_ifp->if_xname : "none",
+ ss->ss_vap != NULL ?
+ ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none",
+ vap->iv_ifp->if_xname,
+ ieee80211_opmode_name[vap->iv_opmode]);
}
- ss->ss_ic = ic;
+#endif
+ ss->ss_vap = vap;
if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (scan != NULL && !scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
+ /*
+ * Switch scanners; detach old, attach new. Special
+ * case where a single scan module implements multiple
+ * policies by using different scan ops but a common
+ * core. We assume if the old and new attach methods
+ * are identical then it's ok to just change ss_ops
+ * and not flush the internal state of the module.
+ */
+ if (scan == NULL || ss->ss_ops == NULL ||
+ ss->ss_ops->scan_attach != scan->scan_attach) {
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_detach(ss);
+ if (scan != NULL && !scan->scan_attach(ss)) {
+ /* XXX attach failure */
+ /* XXX stat+msg */
+ scan = NULL;
+ }
+ }
+ ss->ss_ops = scan;
}
- IEEE80211_UNLOCK(ic);
-
- return (scan != NULL);
}
static void
@@ -263,7 +332,7 @@ channel_type(const struct ieee80211_channel *c)
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211com *ic = ss->ss_vap->iv_ic;
const char *sep;
int i;
@@ -277,6 +346,18 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
}
}
+#ifdef IEEE80211_DEBUG
+static void
+scan_dump(struct ieee80211_scan_state *ss)
+{
+ struct ieee80211vap *vap = ss->ss_vap;
+
+ if_printf(vap->iv_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %lu max %lu\n", ss->ss_mindwell, ss->ss_maxdwell);
+}
+#endif /* IEEE80211_DEBUG */
+
/*
* Enable station power save mode and start/restart the scanning thread.
*/
@@ -284,23 +365,24 @@ static void
scan_restart_pwrsav(void *arg)
{
struct scan_state *ss = (struct scan_state *) arg;
- struct ieee80211com *ic = ss->base.ss_ic;
- int delay;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ int ticksdelay;
- ieee80211_sta_pwrsave(ic, 1);
+ ieee80211_sta_pwrsave(vap, 1);
/*
- * Use an initial 1ms delay to insure the null
+ * Use an initial 1ms delay so the null
* data frame has a chance to go out.
* XXX 1ms is a lot, better to trigger scan
* on tx complete.
*/
- delay = hz/1000;
- if (delay < 1)
- delay = 1;
+ ticksdelay = msecs_to_ticks(1);
+ if (ticksdelay < 1)
+ ticksdelay = 1;
ic->ic_scan_start(ic); /* notify driver */
- ss->ss_scanend = ticks + delay + ss->ss_duration;
+ ss->ss_scanend = ticks + ticksdelay + ss->ss_duration;
ss->ss_iflags |= ISCAN_START;
- callout_reset(&ss->ss_scan_timer, delay, scan_next, ss);
+ callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss);
}
/*
@@ -313,17 +395,18 @@ scan_restart_pwrsav(void *arg)
static int
scan_restart(struct scan_state *ss, u_int duration)
{
- struct ieee80211com *ic = ss->base.ss_ic;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int defer = 0;
if (ss->base.ss_next == ss->base.ss_last) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
return 0;
}
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_state == IEEE80211_S_RUN) {
- if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN) {
+ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/*
* Initiate power save before going off-channel.
* Note that we cannot do this directly because
@@ -346,12 +429,12 @@ scan_restart(struct scan_state *ss, u_int duration)
}
static void
-copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
+copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
if (nssid > IEEE80211_SCAN_MAX_SSID) {
/* XXX printf */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: too many ssid %d, ignoring all of them\n",
__func__, nssid);
return;
@@ -363,76 +446,97 @@ copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
/*
* Start a scan unless one is already going.
*/
-int
-ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
+static int
+start_scan_locked(const struct ieee80211_scanner *scan,
+ struct ieee80211vap *vap, int flags, u_int duration,
+ u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
- const struct ieee80211_scanner *scan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_LOCK(ic);
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: scan inhibited by pending channel change\n", __func__);
+ } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
, __func__
, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
- , duration
- , ieee80211_phymode_name[ic->ic_des_mode]
+ , duration, mindwell, maxdwell
+ , ieee80211_phymode_name[vap->iv_des_mode]
, flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
, flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
+ , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
, flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
, flags & IEEE80211_SCAN_ONCE ? ", once" : ""
);
- ss->ss_ic = ic;
- if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (!scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
- }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
if ((flags & IEEE80211_SCAN_NOSSID) == 0)
- copy_ssid(ic, ss, nssid, ssids);
+ copy_ssid(vap, ss, nssid, ssids);
/* NB: top 4 bits for internal use */
ss->ss_flags = flags & 0xfff;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
if (flags & IEEE80211_SCAN_FLUSH)
ss->ss_ops->scan_flush(ss);
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
- ss->ss_ops->scan_start(ss, ic);
+ ss->ss_next = 0;
+ ss->ss_mindwell = mindwell;
+ ss->ss_maxdwell = maxdwell;
+ ss->ss_ops->scan_start(ss, vap);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(vap))
+ scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
if (scan_restart(SCAN_PRIVATE(ss), duration))
ic->ic_flags |= IEEE80211_F_SCAN;
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
+ return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+int
+ieee80211_start_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_scanner *scan;
+ int result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode]);
+ /* XXX stat */
+ return 0;
+ }
+
+ IEEE80211_LOCK(ic);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
IEEE80211_UNLOCK(ic);
- /* NB: racey, does it matter? */
- return (ic->ic_flags & IEEE80211_F_SCAN);
+ return result;
}
/*
@@ -440,11 +544,23 @@ ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
* fails then kick off a new scan.
*/
int
-ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
+ieee80211_check_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- int checkscanlist = 0;
+ const struct ieee80211_scanner *scan;
+ int checkscanlist = 0, result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
/*
* Check if there's a list of scan candidates already.
@@ -452,30 +568,35 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
IEEE80211_LOCK(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan, %s%s%s%s%s\n"
, __func__
, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
- , duration
- , ieee80211_phymode_name[ic->ic_des_mode]
, flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
, flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
, flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
, flags & IEEE80211_SCAN_ONCE ? ", once" : ""
);
+ if (ss->ss_ops != scan) {
+ /* XXX re-use cache contents? e.g. adhoc<->sta */
+ flags |= IEEE80211_SCAN_FLUSH;
+ }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
- /* XXX verify ss_ops matches ic->ic_opmode */
+ /* XXX verify ss_ops matches vap->iv_opmode */
if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
/*
* Update the ssid list and mark flags so if
* we call start_scan it doesn't duplicate work.
*/
- copy_ssid(ic, ss, nssid, ssids);
+ copy_ssid(vap, ss, nssid, ssids);
flags |= IEEE80211_SCAN_NOSSID;
}
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
- time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ (flags & IEEE80211_SCAN_FLUSH) == 0 &&
+ time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* We're not currently scanning and the cache is
* deemed hot enough to consult. Lock out others
@@ -486,30 +607,40 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ic->ic_flags |= IEEE80211_F_SCAN;
+ /* NB: need to use supplied flags in check below */
+ ss->ss_flags = flags & 0xff;
checkscanlist = 1;
}
}
- IEEE80211_UNLOCK(ic);
if (checkscanlist) {
- const struct ieee80211_scanner *scan;
-
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
- if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) {
+ if (ss->ss_ops->scan_end(ss, vap)) {
/* found an ap, just clear the flag */
ic->ic_flags &= ~IEEE80211_F_SCAN;
+ ieee80211_notify_scan_done(vap);
+ IEEE80211_UNLOCK(ic);
return 1;
}
/* no ap, clear the flag before starting a scan */
ic->ic_flags &= ~IEEE80211_F_SCAN;
}
- return ieee80211_start_scan(ic, flags, duration, nssid, ssids);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
+ IEEE80211_UNLOCK(ic);
+
+ return result;
+}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that fails
+ * then kick off a scan using the current settings.
+ */
+int
+ieee80211_check_scan_current(struct ieee80211vap *vap)
+{
+ return ieee80211_check_scan(vap,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ vap->iv_des_nssid, vap->iv_des_ssid);
}
/*
@@ -517,9 +648,20 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
* then we start again using the existing channel list.
*/
int
-ieee80211_bg_scan(struct ieee80211com *ic)
+ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
+ const struct ieee80211_scanner *scan;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
@@ -532,13 +674,14 @@ ieee80211_bg_scan(struct ieee80211com *ic)
*/
duration = IEEE80211_SCAN_OFFCHANNEL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan, ticks %u duration %lu\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
ticks, duration);
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
- ss->ss_ic = ic;
+ ss->ss_vap = vap;
/*
* A background scan does not select a new sta; it
* just refreshes the scan cache. Also, indicate
@@ -554,15 +697,30 @@ ieee80211_bg_scan(struct ieee80211com *ic)
* usual sta power save logic.
*/
ss->ss_flags |= IEEE80211_SCAN_NOPICK
- | IEEE80211_SCAN_BGSCAN;
+ | IEEE80211_SCAN_BGSCAN
+ | flags
+ ;
/* if previous scan completed, restart */
if (ss->ss_next >= ss->ss_last) {
- ss->ss_next = 0;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
- ss->ss_ops->scan_restart(ss, ic);
+ vap->iv_stats.is_scan_passive++;
+ /*
+ * NB: beware of the scan cache being flushed;
+ * if the channel list is empty use the
+ * scan_start method to populate it.
+ */
+ ss->ss_next = 0;
+ if (ss->ss_last != 0)
+ ss->ss_ops->scan_restart(ss, vap);
+ else {
+ ss->ss_ops->scan_start(ss, vap);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(vap))
+ scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
+ }
}
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
@@ -575,7 +733,7 @@ ieee80211_bg_scan(struct ieee80211com *ic)
/* XXX msg+stat */
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
@@ -586,17 +744,46 @@ ieee80211_bg_scan(struct ieee80211com *ic)
}
/*
+ * Cancel any scan currently going on for the specified vap.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ ss->ss_vap == vap &&
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel %s scan\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+ "active" : "passive");
+
+ /* clear bg scan NOPICK and mark cancel request */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+ /* force it to fire asap */
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+ 0, scan_next, ss);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
* Cancel any scan currently going on.
*/
void
-ieee80211_cancel_scan(struct ieee80211com *ic)
+ieee80211_cancel_anyscan(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) &&
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: cancel %s scan\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
"active" : "passive");
@@ -616,15 +803,12 @@ ieee80211_cancel_scan(struct ieee80211com *ic)
* scanning themselves (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_next(struct ieee80211com *ic)
+ieee80211_scan_next(struct ieee80211vap *vap)
{
- /*
- * XXX: We might need/want to decouple context here by either:
- * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
- * or using a taskqueue. Let's see what kind of problems direct
- * dispatch has for now.
- */
- scan_next(ic->ic_scan);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
/*
@@ -632,12 +816,52 @@ ieee80211_scan_next(struct ieee80211com *ic)
* channels (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_done(struct ieee80211com *ic)
+ieee80211_scan_done(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
scan_next(ss);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Probe the curent channel, if allowed, while scanning.
+ * If the channel is not marked passive-only then send
+ * a probe request immediately. Otherwise mark state and
+ * listen for beacons on the channel; if we receive something
+ * then we'll transmit a probe request.
+ */
+void
+ieee80211_probe_curchan(struct ieee80211vap *vap, int force)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) {
+ ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
+ return;
+ }
+ /*
+ * Send directed probe requests followed by any
+ * broadcast probe request.
+ * XXX remove dependence on ic/vap->iv_bss
+ */
+ for (i = 0; i < ss->ss_nssid; i++)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
+ if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ "", 0);
}
/*
@@ -646,37 +870,16 @@ ieee80211_scan_done(struct ieee80211com *ic)
* Arrange for the channel change after maxdwell ticks.
*/
static void
-scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211vap *vap = ss->ss_vap;
- if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
- (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
- struct ifnet *ifp = ic->ic_ifp;
- int i;
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
- /*
- * Send a broadcast probe request followed by
- * any specified directed probe requests.
- * XXX suppress broadcast probe req?
- * XXX remove dependence on ic/ic->ic_bss
- * XXX move to policy code?
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- "", 0,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- for (i = 0; i < ss->ss_nssid; i++)
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ss->ss_ssid[i].ssid,
- ss->ss_ssid[i].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- }
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ieee80211_probe_curchan(vap, 0);
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- maxdwell, scan_next, ss);
+ maxdwell, scan_next, ss);
}
/*
@@ -684,10 +887,8 @@ scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
* change to the next channel asap.
*/
static void
-scan_mindwell(struct ieee80211com *ic)
+scan_mindwell(struct ieee80211_scan_state *ss)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
-
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
@@ -699,17 +900,16 @@ scan_next(void *arg)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
- int scanning, scandone;
+ int scandone;
- IEEE80211_LOCK(ic);
- scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0;
- IEEE80211_UNLOCK(ic);
- if (!scanning) /* canceled */
- return;
+ IEEE80211_LOCK_ASSERT(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
+ return;
again:
scandone = (ss->ss_next >= ss->ss_last) ||
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
@@ -728,7 +928,7 @@ again:
else
maxdwell = ss->ss_maxdwell;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -751,7 +951,7 @@ again:
* sending a probe request (as needed), and arming the
* timeout to switch channels after maxdwell ticks.
*/
- ic->ic_scan_curchan(ic, maxdwell);
+ ic->ic_scan_curchan(ss, maxdwell);
SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
/* clear mindwell lock and initial channel change flush */
@@ -768,7 +968,7 @@ again:
/* return to the bss channel */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
ic->ic_curchan != ic->ic_bsschan)
- change_channel(ic, ic->ic_bsschan);
+ ieee80211_setcurchan(ic, ic->ic_bsschan);
/* clear internal flags and any indication of a pick */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
@@ -781,20 +981,21 @@ again:
* rx frames alter the scan candidate list.
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
- !ss->ss_ops->scan_end(ss, ic) &&
+ !ss->ss_ops->scan_end(ss, vap) &&
(ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
time_before(ticks + ss->ss_mindwell, scanend)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: done, restart "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__,
ticks, ss->ss_mindwell, scanend);
ss->ss_next = 0; /* reset to begining */
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
+ ss->ss_ops->scan_restart(ss, vap); /* XXX? */
ic->ic_scan_start(ic); /* notify driver */
goto again;
} else {
@@ -802,7 +1003,7 @@ again:
if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
scandone = 1;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s, "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__, scandone ? "done" : "stopped",
@@ -825,9 +1026,9 @@ again:
* waiting for us.
*/
if (scandone) {
- ieee80211_sta_pwrsave(ic, 0);
+ ieee80211_sta_pwrsave(vap, 0);
if (ss->ss_next >= ss->ss_last) {
- ieee80211_notify_scan_done(ic);
+ ieee80211_notify_scan_done(vap);
ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
}
}
@@ -841,32 +1042,48 @@ again:
#ifdef IEEE80211_DEBUG
static void
+dump_country(const uint8_t *ie)
+{
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) ie;
+ int i, nbands, schan, nchan;
+
+ if (cie->len < 3) {
+ printf(" <bogus country ie, len %d>", cie->len);
+ return;
+ }
+ printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]);
+ nbands = (cie->len - 3) / sizeof(cie->band[0]);
+ for (i = 0; i < nbands; i++) {
+ schan = cie->band[i].schan;
+ nchan = cie->band[i].nchan;
+ if (nchan != 1)
+ printf(" %u-%u,%u", schan, schan + nchan-1,
+ cie->band[i].maxtxpwr);
+ else
+ printf(" %u,%u", schan, cie->band[i].maxtxpwr);
+ }
+ printf("]");
+}
+
+static void
dump_probe_beacon(uint8_t subtype, int isnew,
const uint8_t mac[IEEE80211_ADDR_LEN],
- const struct ieee80211_scanparams *sp)
+ const struct ieee80211_scanparams *sp, int rssi)
{
printf("[%s] %s%s on chan %u (bss chan %u) ",
ether_sprintf(mac), isnew ? "new " : "",
ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
- IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan);
+ sp->chan, sp->bchan);
ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
- printf("\n");
+ printf(" rssi %d\n", rssi);
if (isnew) {
printf("[%s] caps 0x%x bintval %u erp 0x%x",
ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
- if (sp->country != NULL) {
-#ifdef __FreeBSD__
- printf(" country info %*D",
- sp->country[1], sp->country+2, " ");
-#else
- int i;
- printf(" country info");
- for (i = 0; i < sp->country[1]; i++)
- printf(" %02x", sp->country[i+2]);
-#endif
- }
+ if (sp->country != NULL)
+ dump_country(sp->country);
printf("\n");
}
}
@@ -876,26 +1093,27 @@ dump_probe_beacon(uint8_t subtype, int isnew,
* Process a beacon or probe response frame.
*/
void
-ieee80211_add_scan(struct ieee80211com *ic,
+ieee80211_add_scan(struct ieee80211vap *vap,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int noise, int rstamp)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
+ /* XXX locking */
/*
* Frames received during startup are discarded to avoid
* using scan state setup on the initial entry to the timer
* callback. This can occur because the device may enable
* rx prior to our doing the initial channel change in the
- * timer routine (we defer the channel change to the timer
- * code to simplify locking on linux).
+ * timer routine.
*/
if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
return;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
- dump_probe_beacon(subtype, 1, wh->i_addr2, sp);
+ if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
+ dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
#endif
if (ss->ss_ops != NULL &&
ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) {
@@ -905,7 +1123,7 @@ ieee80211_add_scan(struct ieee80211com *ic,
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c min dwell met (%u > %lu)\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -914,9 +1132,9 @@ ieee80211_add_scan(struct ieee80211com *ic,
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
/*
* NB: trigger at next clock tick or wait for the
- * hardware
+ * hardware.
*/
- ic->ic_scan_mindwell(ic);
+ ic->ic_scan_mindwell(ss);
}
}
}
@@ -938,12 +1156,12 @@ ieee80211_scan_timeout(struct ieee80211com *ic)
* Mark a scan cache entry after a successful associate.
*/
void
-ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
+ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[])
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN,
mac, "%s", __func__);
ss->ss_ops->scan_assoc_success(ss, mac);
}
@@ -953,13 +1171,13 @@ ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
* Demerit a scan cache entry after failing to associate.
*/
void
-ieee80211_scan_assoc_fail(struct ieee80211com *ic,
+ieee80211_scan_assoc_fail(struct ieee80211vap *vap,
const uint8_t mac[], int reason)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac,
"%s: reason %u", __func__, reason);
ss->ss_ops->scan_assoc_fail(ss, mac, reason);
}
@@ -969,10 +1187,10 @@ ieee80211_scan_assoc_fail(struct ieee80211com *ic,
* Iterate over the contents of the scan cache.
*/
void
-ieee80211_scan_iterate(struct ieee80211com *ic,
+ieee80211_scan_iterate(struct ieee80211vap *vap,
ieee80211_scan_iter_func *f, void *arg)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL)
ss->ss_ops->scan_iterate(ss, f, arg);
@@ -982,13 +1200,36 @@ ieee80211_scan_iterate(struct ieee80211com *ic,
* Flush the contents of the scan cache.
*/
void
-ieee80211_scan_flush(struct ieee80211com *ic)
+ieee80211_scan_flush(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
- if (ss->ss_ops != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s\n", __func__);
+ if (ss->ss_ops != NULL && ss->ss_vap == vap) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__);
ss->ss_ops->scan_flush(ss);
}
}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that
+ * fails then kick off a new scan.
+ */
+struct ieee80211_channel *
+ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
+ /* XXX printf? */
+ return NULL;
+ }
+ if (ss->ss_ops->scan_pickchan == NULL) {
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
+ "%s: scan module does not support picking a channel, "
+ "opmode %s\n", __func__, ss->ss_vap->iv_opmode);
+ return NULL;
+ }
+ return ss->ss_ops->scan_pickchan(ss, flags);
+}
diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h
index fed5e0b..df6ce1f7 100644
--- a/sys/net80211/ieee80211_scan.h
+++ b/sys/net80211/ieee80211_scan.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,18 +27,69 @@
#ifndef _NET80211_IEEE80211_SCAN_H_
#define _NET80211_IEEE80211_SCAN_H_
+/*
+ * 802.11 scanning support.
+ *
+ * Scanning is the procedure by which a station locates a bss to join
+ * (infrastructure/ibss mode), or a channel to use (when operating as
+ * an ap or ibss master). Scans are either "active" or "passive". An
+ * active scan causes one or more probe request frames to be sent on
+ * visiting each channel. A passive request causes each channel in the
+ * scan set to be visited but no frames to be transmitted; the station
+ * only listens for traffic. Note that active scanning may still need
+ * to listen for traffic before sending probe request frames depending
+ * on regulatory constraints; the 802.11 layer handles this by generating
+ * a callback when scanning on a ``passive channel'' when the
+ * IEEE80211_FEXT_PROBECHAN flag is set.
+ *
+ * A scan operation involves constructing a set of channels to inspec
+ * (the scan set), visiting each channel and collecting information
+ * (e.g. what bss are present), and then analyzing the results to make
+ * decisions like which bss to join. This process needs to be as fast
+ * as possible so we do things like intelligently construct scan sets
+ * and dwell on a channel only as long as necessary. The scan code also
+ * maintains a cache of recent scan results and uses it to bypass scanning
+ * whenever possible. The scan cache is also used to enable roaming
+ * between access points when operating in infrastructure mode.
+ *
+ * Scanning is handled with pluggable modules that implement "policy"
+ * per-operating mode. The core scanning support provides an
+ * instrastructure to support these modules and exports a common api
+ * to the rest of the 802.11 layer. Policy modules decide what
+ * channels to visit, what state to record to make decisions (e.g. ap
+ * mode scanning for auto channel selection keeps significantly less
+ * state than sta mode scanning for an ap to associate to), and selects
+ * the final station/channel to return as the result of a scan.
+ *
+ * Scanning is done synchronously when initially bringing a vap to an
+ * operational state and optionally in the background to maintain the
+ * scan cache for doing roaming and rogue ap monitoring. Scanning is
+ * not tied to the 802.11 state machine that governs vaps though there
+ * is linkage to the IEEE80211_SCAN state. Only one vap at a time may
+ * be scanning; this scheduling policy is handled in ieee80211_new_state
+ * and is invisible to the scanning code.
+*/
#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
-struct ieee80211_scanner;
+struct ieee80211_scanner; /* scan policy state */
struct ieee80211_scan_ssid {
- int len; /* length in bytes */
- uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
};
-#define IEEE80211_SCAN_MAX_SSID 1
+#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */
+/*
+ * Scan state visible to the 802.11 layer. Scan parameters and
+ * results are stored in this data structure. The ieee80211_scan_state
+ * structure is extended with space that is maintained private to
+ * the core scanning support. We allocate one instance and link it
+ * to the ieee80211com structure; then share it between all associated
+ * vaps. We could allocate multiple of these, e.g. to hold multiple
+ * scan results, but this is sufficient for current needs.
+ */
struct ieee80211_scan_state {
- struct ieee80211com *ss_ic;
+ struct ieee80211vap *ss_vap;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;
@@ -47,6 +98,8 @@ struct ieee80211_scan_state {
#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */
#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */
#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */
+#define IEEE80211_SCAN_NOBCAST 0x0020 /* no broadcast probe req */
+#define IEEE80211_SCAN_NOJOIN 0x0040 /* no auto-sequencing */
#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */
uint8_t ss_nssid; /* # ssid's to probe/match */
struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
@@ -65,48 +118,64 @@ struct ieee80211_scan_state {
* ss_flags. It might be better to split this stuff out into
* a separate variable to avoid confusion.
*/
-#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */
-#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */
+#define IEEE80211_SCAN_FLUSH 0x00010000 /* flush candidate table */
+#define IEEE80211_SCAN_NOSSID 0x80000000 /* don't update ssid list */
struct ieee80211com;
void ieee80211_scan_attach(struct ieee80211com *);
void ieee80211_scan_detach(struct ieee80211com *);
+void ieee80211_scan_vattach(struct ieee80211vap *);
+void ieee80211_scan_vdetach(struct ieee80211vap *);
void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
-int ieee80211_scan_update(struct ieee80211com *);
#define IEEE80211_SCAN_FOREVER 0x7fffffff
-int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_start_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_check_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_bg_scan(struct ieee80211com *);
-void ieee80211_cancel_scan(struct ieee80211com *);
-void ieee80211_scan_next(struct ieee80211com *);
-void ieee80211_scan_done(struct ieee80211com *);
+int ieee80211_check_scan_current(struct ieee80211vap *);
+int ieee80211_bg_scan(struct ieee80211vap *, int);
+void ieee80211_cancel_scan(struct ieee80211vap *);
+void ieee80211_cancel_anyscan(struct ieee80211vap *);
+void ieee80211_scan_next(struct ieee80211vap *);
+void ieee80211_scan_done(struct ieee80211vap *);
+void ieee80211_probe_curchan(struct ieee80211vap *, int);
+struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int);
struct ieee80211_scanparams;
-void ieee80211_add_scan(struct ieee80211com *,
+void ieee80211_add_scan(struct ieee80211vap *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise, int rstamp);
void ieee80211_scan_timeout(struct ieee80211com *);
-void ieee80211_scan_assoc_success(struct ieee80211com *,
+void ieee80211_scan_assoc_success(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
enum {
IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */
IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */
};
-void ieee80211_scan_assoc_fail(struct ieee80211com *,
+void ieee80211_scan_assoc_fail(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
-void ieee80211_scan_flush(struct ieee80211com *);
+void ieee80211_scan_flush(struct ieee80211vap *);
struct ieee80211_scan_entry;
typedef void ieee80211_scan_iter_func(void *,
const struct ieee80211_scan_entry *);
-void ieee80211_scan_iterate(struct ieee80211com *,
+void ieee80211_scan_iterate(struct ieee80211vap *,
ieee80211_scan_iter_func, void *);
+enum {
+ IEEE80211_BPARSE_BADIELEN = 0x01, /* ie len past end of frame */
+ IEEE80211_BPARSE_RATES_INVALID = 0x02, /* invalid RATES ie */
+ IEEE80211_BPARSE_XRATES_INVALID = 0x04, /* invalid XRATES ie */
+ IEEE80211_BPARSE_SSID_INVALID = 0x08, /* invalid SSID ie */
+ IEEE80211_BPARSE_CHAN_INVALID = 0x10, /* invalid FH/DSPARMS chan */
+ IEEE80211_BPARSE_OFFCHAN = 0x20, /* DSPARMS chan != curchan */
+ IEEE80211_BPARSE_BINTVAL_INVALID= 0x40, /* invalid beacon interval */
+};
/*
* Parameters supplied when adding/updating an entry in a
@@ -116,14 +185,17 @@ void ieee80211_scan_iterate(struct ieee80211com *,
* All multi-byte values must be in host byte order.
*/
struct ieee80211_scanparams {
- uint16_t capinfo; /* 802.11 capabilities */
- uint16_t fhdwell; /* FHSS dwell interval */
- struct ieee80211_channel *curchan;
- uint8_t bchan; /* chan# advertised inside beacon */
+ uint8_t status; /* bitmask of IEEE80211_BPARSE_* */
+ uint8_t chan; /* channel # from FH/DSPARMS */
+ uint8_t bchan; /* curchan's channel # */
uint8_t fhindex;
- uint8_t erp;
+ uint16_t fhdwell; /* FHSS dwell interval */
+ uint16_t capinfo; /* 802.11 capabilities */
+ uint16_t erp; /* NB: 0x100 indicates ie present */
uint16_t bintval;
uint8_t timoff;
+ uint8_t *ies; /* all captured ies */
+ size_t ies_len; /* length of all captured ies */
uint8_t *tim;
uint8_t *tstamp;
uint8_t *country;
@@ -146,13 +218,14 @@ struct ieee80211_scanparams {
struct ieee80211_scan_entry {
uint8_t se_macaddr[IEEE80211_ADDR_LEN];
uint8_t se_bssid[IEEE80211_ADDR_LEN];
+ /* XXX can point inside se_ies */
uint8_t se_ssid[2+IEEE80211_NWID_LEN];
uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE];
uint32_t se_rstamp; /* recv timestamp */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} se_tstamp; /* from last rcv'd beacon */
uint16_t se_intval; /* beacon interval (host byte order) */
uint16_t se_capinfo; /* capabilities (host byte order) */
@@ -160,16 +233,12 @@ struct ieee80211_scan_entry {
uint16_t se_timoff; /* byte offset to TIM ie */
uint16_t se_fhdwell; /* FH only (host byte order) */
uint8_t se_fhindex; /* FH only */
- uint8_t se_erp; /* ERP from beacon/probe resp */
+ uint8_t se_dtimperiod; /* DTIM period */
+ uint16_t se_erp; /* ERP from beacon/probe resp */
int8_t se_rssi; /* avg'd recv ssi */
int8_t se_noise; /* noise floor */
- uint8_t se_dtimperiod; /* DTIM period */
- uint8_t *se_wpa_ie; /* captured WPA ie */
- uint8_t *se_rsn_ie; /* captured RSN ie */
- uint8_t *se_wme_ie; /* captured WME ie */
- uint8_t *se_htcap_ie; /* captured HTP cap ie */
- uint8_t *se_htinfo_ie; /* captured HTP info ie */
- uint8_t *se_ath_ie; /* captured Atheros ie */
+ uint8_t se_cc[2]; /* captured country code */
+ struct ieee80211_ies se_ies; /* captured ie's */
u_int se_age; /* age of entry (0 on create) */
};
MALLOC_DECLARE(M_80211_SCAN);
@@ -184,14 +253,16 @@ struct ieee80211_scanner {
int (*scan_attach)(struct ieee80211_scan_state *);
int (*scan_detach)(struct ieee80211_scan_state *);
int (*scan_start)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_restart)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_cancel)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_end)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_flush)(struct ieee80211_scan_state *);
+ struct ieee80211_channel *(*scan_pickchan)(
+ struct ieee80211_scan_state *, int);
/* add an entry to the cache */
int (*scan_add)(struct ieee80211_scan_state *,
const struct ieee80211_scanparams *,
diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c
deleted file mode 100644
index 300f440..0000000
--- a/sys/net80211/ieee80211_scan_ap.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR 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.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * IEEE 802.11 ap scanning support.
- */
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-
-#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
-#include <net/ethernet.h>
-
-#include <net80211/ieee80211_var.h>
-
-#include <net/bpf.h>
-
-struct ap_state {
- int as_maxrssi[IEEE80211_CHAN_MAX];
-};
-
-static int ap_flush(struct ieee80211_scan_state *);
-
-/* number of references from net80211 layer */
-static int nrefs = 0;
-
-/*
- * Attach prior to any scanning work.
- */
-static int
-ap_attach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as;
-
- MALLOC(as, struct ap_state *, sizeof(struct ap_state),
- M_80211_SCAN, M_NOWAIT);
- ss->ss_priv = as;
- ap_flush(ss);
- nrefs++; /* NB: we assume caller locking */
- return 1;
-}
-
-/*
- * Cleanup any private state.
- */
-static int
-ap_detach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- if (as != NULL) {
- KASSERT(nrefs > 0, ("imbalanced attach/detach"));
- nrefs--; /* NB: we assume caller locking */
- FREE(as, M_80211_SCAN);
- }
- return 1;
-}
-
-/*
- * Flush all per-scan state.
- */
-static int
-ap_flush(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi));
- ss->ss_last = 0; /* insure no channel will be picked */
- return 0;
-}
-
-static int
-find11gchannel(struct ieee80211com *ic, int i, int freq)
-{
- const struct ieee80211_channel *c;
- int j;
-
- /*
- * The normal ordering in the channel list is b channel
- * immediately followed by g so optimize the search for
- * this. We'll still do a full search just in case.
- */
- for (j = i+1; j < ic->ic_nchans; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- for (j = 0; j < i; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- return 0;
-}
-
-/*
- * Start an ap scan by populating the channel list.
- */
-static int
-ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ieee80211_channel *c;
- int i;
-
- ss->ss_last = 0;
- if (ic->ic_des_mode == IEEE80211_MODE_AUTO) {
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if (IEEE80211_IS_CHAN_TURBO(c)) {
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (ic->ic_flags & IEEE80211_F_XR)
- continue;
-#endif
- /* dynamic channels are scanned in base mode */
- if (!IEEE80211_IS_CHAN_ST(c))
- continue;
- } else if (IEEE80211_IS_CHAN_HT(c)) {
- /* HT channels are scanned in legacy */
- continue;
- } else {
- /*
- * Use any 11g channel instead of 11b one.
- */
- if (IEEE80211_IS_CHAN_B(c) &&
- find11gchannel(ic, i, c->ic_freq))
- continue;
- }
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- ss->ss_chans[ss->ss_last++] = c;
- }
- } else {
- static const u_int chanflags[IEEE80211_MODE_MAX] = {
- 0, /* IEEE80211_MODE_AUTO */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
- IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
- IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
- IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
- };
- u_int modeflags;
-
- modeflags = chanflags[ic->ic_des_mode];
- if ((ic->ic_flags & IEEE80211_F_TURBOP) &&
- modeflags != IEEE80211_CHAN_ST) {
- if (ic->ic_des_mode == IEEE80211_MODE_11G)
- modeflags = IEEE80211_CHAN_108G;
- else
- modeflags = IEEE80211_CHAN_108A;
- }
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if ((c->ic_flags & modeflags) != modeflags)
- continue;
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- (ic->ic_flags & IEEE80211_F_XR))
- continue;
-#endif
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- /*
- * Do not select static turbo channels if
- * the mode is not static turbo.
- */
- if (IEEE80211_IS_CHAN_STURBO(c) &&
- ic->ic_des_mode != IEEE80211_MODE_STURBO_A)
- continue;
- ss->ss_chans[ss->ss_last++] = c;
- }
- }
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */
-
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
-
- return 0;
-}
-
-/*
- * Restart a bg scan.
- */
-static int
-ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Cancel an ongoing scan.
- */
-static int
-ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Record max rssi on channel.
- */
-static int
-ap_add(struct ieee80211_scan_state *ss,
- const struct ieee80211_scanparams *sp,
- const struct ieee80211_frame *wh,
- int subtype, int rssi, int noise, int rstamp)
-{
- struct ap_state *as = ss->ss_priv;
- struct ieee80211com *ic = ss->ss_ic;
- int chan;
-
- chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
- /* XXX better quantification of channel use? */
- /* XXX count bss's? */
- if (rssi > as->as_maxrssi[chan])
- as->as_maxrssi[chan] = rssi;
- /* XXX interference, turbo requirements */
- return 1;
-}
-
-/*
- * Pick a quiet channel to use for ap operation.
- */
-static int
-ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ap_state *as = ss->ss_priv;
- int i, chan, bestchan, bestchanix;
-
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
- ("wrong opmode %u", ic->ic_opmode));
- /* XXX select channel more intelligently, e.g. channel spread, power */
- bestchan = -1;
- bestchanix = 0; /* NB: silence compiler */
- /* NB: use scan list order to preserve channel preference */
- for (i = 0; i < ss->ss_last; i++) {
- /*
- * If the channel is unoccupied the max rssi
- * should be zero; just take it. Otherwise
- * track the channel with the lowest rssi and
- * use that when all channels appear occupied.
- */
- /* XXX channel have interference? */
- chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n",
- __func__, chan, as->as_maxrssi[chan],
- bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0);
-
- if (as->as_maxrssi[chan] == 0) {
- bestchan = chan;
- bestchanix = i;
- /* XXX use other considerations */
- break;
- }
- if (bestchan == -1 ||
- as->as_maxrssi[chan] < as->as_maxrssi[bestchan])
- bestchan = chan;
- }
- if (bestchan == -1) {
- /* no suitable channel, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no suitable channel! (should not happen)\n", __func__);
- /* XXX print something? */
- return 0; /* restart scan */
- } else {
- struct ieee80211_channel *c;
-
- /* XXX notify all vap's? */
- /*
- * If this is a dynamic turbo frequency,
- * start with normal mode first.
- */
- c = ss->ss_chans[bestchanix];
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- !IEEE80211_IS_CHAN_STURBO(c)) {
- c = ieee80211_find_channel(ic, c->ic_freq,
- c->ic_flags & ~IEEE80211_CHAN_TURBO);
- if (c == NULL) {
- /* should never happen ?? */
- return 0;
- }
- }
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
- return 1;
- }
-}
-
-static void
-ap_age(struct ieee80211_scan_state *ss)
-{
- /* XXX is there anything meaningful to do? */
-}
-
-static void
-ap_iterate(struct ieee80211_scan_state *ss,
- ieee80211_scan_iter_func *f, void *arg)
-{
- /* NB: nothing meaningful we can do */
-}
-
-static void
-ap_assoc_success(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN])
-{
- /* should not be called */
-}
-
-static void
-ap_assoc_fail(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
-{
- /* should not be called */
-}
-
-static const struct ieee80211_scanner ap_default = {
- .scan_name = "default",
- .scan_attach = ap_attach,
- .scan_detach = ap_detach,
- .scan_start = ap_start,
- .scan_restart = ap_restart,
- .scan_cancel = ap_cancel,
- .scan_end = ap_end,
- .scan_flush = ap_flush,
- .scan_add = ap_add,
- .scan_age = ap_age,
- .scan_iterate = ap_iterate,
- .scan_assoc_success = ap_assoc_success,
- .scan_assoc_fail = ap_assoc_fail,
-};
-
-/*
- * Module glue.
- */
-static int
-wlan_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_ap: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == MOD_UNLOAD)
- ieee80211_scanner_unregister_all(&ap_default);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_mod = {
- "wlan_scan_ap",
- wlan_modevent,
- 0
-};
-DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_ap, 1);
-MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index d152605..d1dc060 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 station scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -41,6 +43,8 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -61,20 +65,6 @@ __FBSDID("$FreeBSD$");
#define STA_RSSI_MIN 8 /* min acceptable rssi */
#define STA_RSSI_MAX 40 /* max rssi for comparison */
-#define RSSI_LPF_LEN 10
-#define RSSI_DUMMY_MARKER 0x127
-#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */
-#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER)
-#define LPF_RSSI(x, y, len) \
- ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
-#define RSSI_LPF(x, y) do { \
- if ((y) >= -20) \
- x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \
-} while (0)
-#define EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul))
-#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER)
-
struct sta_entry {
struct ieee80211_scan_entry base;
TAILQ_ENTRY(sta_entry) se_list;
@@ -83,11 +73,14 @@ struct sta_entry {
uint8_t se_seen; /* seen during current scan */
uint8_t se_notseen; /* not seen in previous scans */
uint8_t se_flags;
+#define STA_SSID_MATCH 0x01
+#define STA_BSSID_MATCH 0x02
uint32_t se_avgrssi; /* LPF rssi state */
unsigned long se_lastupdate; /* time of last update */
unsigned long se_lastfail; /* time of last failure */
unsigned long se_lastassoc; /* time of last association */
u_int se_scangen; /* iterator scan gen# */
+ u_int se_countrygen; /* gen# of last cc notify */
};
#define STA_HASHSIZE 32
@@ -99,9 +92,12 @@ struct sta_table {
struct mtx st_lock; /* on scan table */
TAILQ_HEAD(, sta_entry) st_entry; /* all entries */
LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE];
- struct mtx st_scanlock; /* on st_scangen */
- u_int st_scangen; /* gen# for iterator */
+ struct mtx st_scanlock; /* on st_scaniter */
+ u_int st_scaniter; /* gen# for iterator */
+ u_int st_scangen; /* scan generation # */
int st_newscan;
+ /* ap-related state */
+ int st_maxrssi[IEEE80211_CHAN_MAX];
};
static void sta_flush_table(struct sta_table *);
@@ -120,8 +116,16 @@ static void sta_flush_table(struct sta_table *);
#define MATCH_FAILS 0x040 /* too many failed auth attempts */
#define MATCH_NOTSEEN 0x080 /* not seen in recent scans */
#define MATCH_RSSI 0x100 /* rssi deemed too low to use */
-static int match_bss(struct ieee80211com *,
+#define MATCH_CC 0x200 /* country code mismatch */
+static int match_bss(struct ieee80211vap *,
const struct ieee80211_scan_state *, struct sta_entry *, int);
+static void adhoc_age(struct ieee80211_scan_state *);
+
+static __inline int
+isocmp(const uint8_t cc1[], const uint8_t cc2[])
+{
+ return (cc1[0] == cc2[0] && cc1[1] == cc2[1]);
+}
/* number of references from net80211 layer */
static int nrefs = 0;
@@ -191,18 +195,10 @@ sta_flush_table(struct sta_table *st)
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
-}
-
-static void
-saveie(uint8_t **iep, const uint8_t *ie)
-{
-
- if (ie == NULL)
- *iep = NULL;
- else
- ieee80211_saveie(iep, ie);
+ memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi));
}
/*
@@ -221,10 +217,11 @@ sta_add(struct ieee80211_scan_state *ss,
IEEE80211_SCAN_PICK1ST)
struct sta_table *st = ss->ss_priv;
const uint8_t *macaddr = wh->i_addr2;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct sta_entry *se;
struct ieee80211_scan_entry *ise;
- int hash, offchan;
+ int hash;
hash = STA_HASH(macaddr);
@@ -238,8 +235,8 @@ sta_add(struct ieee80211_scan_state *ss,
mtx_unlock(&st->st_lock);
return 0;
}
- se->se_scangen = st->st_scangen-1;
- se->se_avgrssi = RSSI_DUMMY_MARKER;
+ se->se_scangen = st->st_scaniter-1;
+ se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
@@ -254,23 +251,21 @@ found:
memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
if (sp->xrates != NULL) {
/* XXX validate xrates[1] */
- KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+ KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE,
("xrate set too large: %u", sp->xrates[1]));
memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
} else
ise->se_xrates[1] = 0;
IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
- offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan &&
- ic->ic_phytype != IEEE80211_T_FH);
- if (!offchan) {
+ if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) {
/*
* Record rssi data using extended precision LPF filter.
*
* NB: use only on-channel data to insure we get a good
* estimate of the signal we'll see when associated.
*/
- RSSI_LPF(se->se_avgrssi, rssi);
- ise->se_rssi = RSSI_GET(se->se_avgrssi);
+ IEEE80211_RSSI_LPF(se->se_avgrssi, rssi);
+ ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi);
ise->se_noise = noise;
}
ise->se_rstamp = rstamp;
@@ -280,24 +275,26 @@ found:
/*
* Beware of overriding se_chan for frames seen
* off-channel; this can cause us to attempt an
- * assocation on the wrong channel.
+ * association on the wrong channel.
*/
- if (offchan) {
+ if (sp->status & IEEE80211_BPARSE_OFFCHAN) {
struct ieee80211_channel *c;
/*
* Off-channel, locate the home/bss channel for the sta
- * using the value broadcast in the DSPARMS ie.
+ * using the value broadcast in the DSPARMS ie. We know
+ * sp->chan has this value because it's used to calculate
+ * IEEE80211_BPARSE_OFFCHAN.
*/
- c = ieee80211_find_channel_byieee(ic, sp->bchan,
- sp->curchan->ic_flags);
+ c = ieee80211_find_channel_byieee(ic, sp->chan,
+ ic->ic_curchan->ic_flags);
if (c != NULL) {
ise->se_chan = c;
} else if (ise->se_chan == NULL) {
/* should not happen, pick something */
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
}
} else
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
@@ -307,17 +304,39 @@ found:
(const struct ieee80211_tim_ie *) sp->tim;
ise->se_dtimperiod = tim->tim_period;
}
- saveie(&ise->se_wme_ie, sp->wme);
- saveie(&ise->se_wpa_ie, sp->wpa);
- saveie(&ise->se_rsn_ie, sp->rsn);
- saveie(&ise->se_ath_ie, sp->ath);
- saveie(&ise->se_htcap_ie, sp->htcap);
- saveie(&ise->se_htinfo_ie, sp->htinfo);
+ if (sp->country != NULL) {
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) sp->country;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then
+ * dispatch an event to user space that identifies the
+ * country code so our regdomain config can be changed.
+ */
+ /* XXX only for STA mode? */
+ if ((IEEE80211_IS_CHAN_11D(ise->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(cie->cc, ic->ic_regdomain.isocc))) {
+ /* only issue one notify event per scan */
+ if (se->se_countrygen != st->st_scangen) {
+ ieee80211_notify_country(vap, ise->se_bssid,
+ cie->cc);
+ se->se_countrygen = st->st_scangen;
+ }
+ }
+ ise->se_cc[0] = cie->cc[0];
+ ise->se_cc[1] = cie->cc[1];
+ }
+ /* NB: no need to setup ie ptrs; they are not (currently) used */
+ (void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len);
/* clear failure count after STA_FAIL_AGE passes */
if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr,
"%s: fails %u", __func__, se->se_fails);
}
@@ -325,13 +344,16 @@ found:
se->se_seen = 1;
se->se_notseen = 0;
+ if (rssi > st->st_maxrssi[sp->bchan])
+ st->st_maxrssi[sp->bchan] = rssi;
+
mtx_unlock(&st->st_lock);
/*
* If looking for a quick choice and nothing's
* been found check here.
*/
- if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0)
+ if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0)
ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
return 1;
@@ -343,11 +365,11 @@ found:
* Check if a channel is excluded by user request.
*/
static int
-isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c)
+isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c)
{
- return (isclr(ic->ic_chan_active, c->ic_ieee) ||
- (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- c->ic_freq != ic->ic_des_chan->ic_freq));
+ return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) ||
+ (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ c->ic_freq != vap->iv_des_chan->ic_freq));
}
static struct ieee80211_channel *
@@ -387,11 +409,12 @@ static const u_int chanflags[IEEE80211_MODE_MAX] = {
};
static void
-add_channels(struct ieee80211com *ic,
+add_channels(struct ieee80211vap *vap,
struct ieee80211_scan_state *ss,
enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c, *cg;
u_int modeflags;
int i;
@@ -403,89 +426,28 @@ add_channels(struct ieee80211com *ic,
break;
c = ieee80211_find_channel(ic, freq[i], modeflags);
- if (c != NULL && isexcluded(ic, c))
+ if (c == NULL || isexcluded(vap, c))
continue;
if (mode == IEEE80211_MODE_AUTO) {
/*
* XXX special-case 11b/g channels so we select
- * the g channel if both are present or there
- * are only g channels.
+ * the g channel if both are present.
*/
- if (c == NULL || IEEE80211_IS_CHAN_B(c)) {
- cg = find11gchannel(ic, i, freq[i]);
- if (cg != NULL)
- c = cg;
- }
+ if (IEEE80211_IS_CHAN_B(c) &&
+ (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
+ c = cg;
}
- if (c == NULL)
- continue;
-
ss->ss_chans[ss->ss_last++] = c;
}
#undef N
}
-static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
-{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
-static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
-{ 5170, 5190, 5210, 5230 };
-static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
-{ 2412, 2437, 2462, 2442, 2472 };
-static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
-{ 5745, 5765, 5785, 5805, 5825 };
-static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
-{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
-static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
-{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
-static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
-{ 2484 };
-static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
-{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
-static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
-{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
-#ifdef ATH_TURBO_SCAN
-static const uint16_t rcl5[] = /* 3 static turbo channels */
-{ 5210, 5250, 5290 };
-static const uint16_t rcl6[] = /* 2 static turbo channels */
-{ 5760, 5800 };
-static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
-{ 5540, 5580, 5620, 5660 };
-static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
-{ 2437 };
-static const uint16_t rcl13[] = /* dynamic Turbo channels */
-{ 5200, 5240, 5280, 5765, 5805 };
-#endif /* ATH_TURBO_SCAN */
-
struct scanlist {
uint16_t mode;
uint16_t count;
const uint16_t *list;
};
-#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
-
-static const struct scanlist staScanTable[] = {
- { IEEE80211_MODE_11B, X(rcl3) },
- { IEEE80211_MODE_11A, X(rcl1) },
- { IEEE80211_MODE_11A, X(rcl2) },
- { IEEE80211_MODE_11B, X(rcl8) },
- { IEEE80211_MODE_11B, X(rcl9) },
- { IEEE80211_MODE_11A, X(rcl4) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_STURBO_A, X(rcl5) },
- { IEEE80211_MODE_STURBO_A, X(rcl6) },
- { IEEE80211_MODE_TURBO_A, X(rcl6x) },
- { IEEE80211_MODE_TURBO_A, X(rcl13) },
-#endif /* ATH_TURBO_SCAN */
- { IEEE80211_MODE_11A, X(rcl7) },
- { IEEE80211_MODE_11B, X(rcl10) },
- { IEEE80211_MODE_11A, X(rcl11) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_TURBO_G, X(rcl12) },
-#endif /* ATH_TURBO_SCAN */
- { .list = NULL }
-};
-
static int
checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
{
@@ -500,16 +462,13 @@ checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
}
static void
-sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
+sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
const struct scanlist table[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
int i;
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
for (i = 0; i < ic->ic_nchans; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
break;
@@ -528,14 +487,14 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
- ic->ic_des_mode != ieee80211_chan2mode(c))
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
+ vap->iv_des_mode != ieee80211_chan2mode(c))
continue;
/*
* Skip channels excluded by user request.
*/
- if (isexcluded(ic, c))
+ if (isexcluded(vap, c))
continue;
/*
@@ -552,14 +511,10 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
}
}
-/*
- * Start a station-mode scan by populating the channel list.
- */
-static int
-sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+static void
+makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
+ const struct scanlist table[])
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- struct sta_table *st = ss->ss_priv;
const struct scanlist *scan;
enum ieee80211_phymode mode;
@@ -569,20 +524,20 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* of channels for scanning. Any channels in the ordered
* list not in the master list will be discarded.
*/
- for (scan = staScanTable; scan->list != NULL; scan++) {
+ for (scan = table; scan->list != NULL; scan++) {
mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
/*
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != mode) {
+ if (vap->iv_des_mode != mode) {
/*
* The scan table marks 2.4Ghz channels as b
* so if the desired mode is 11g, then use
* the 11b channel list but upgrade the mode.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+ if (vap->iv_des_mode != IEEE80211_MODE_11G ||
mode != IEEE80211_MODE_11B)
continue;
mode = IEEE80211_MODE_11G; /* upgrade */
@@ -597,7 +552,7 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
}
#ifdef IEEE80211_F_XR
/* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
+ if ((vap->iv_flags & IEEE80211_F_XR) &&
(mode == IEEE80211_MODE_TURBO_A ||
mode == IEEE80211_MODE_TURBO_G ||
mode == IEEE80211_MODE_STURBO_A))
@@ -607,40 +562,98 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Add the list of the channels; any that are not
* in the master channel list will be discarded.
*/
- add_channels(ic, ss, mode, scan->list, scan->count);
+ add_channels(vap, ss, mode, scan->list, scan->count);
}
/*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
+ * Add the channels from the ic that are not present
+ * in the table.
*/
- sweepchannels(ss, ic, staScanTable);
+ sweepchannels(ss, vap, table);
+}
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+{ 2412, 2437, 2462, 2442, 2472 };
+static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+{ 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
+{ 2484 };
+static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
+{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+#ifdef ATH_TURBO_SCAN
+static const uint16_t rcl5[] = /* 3 static turbo channels */
+{ 5210, 5250, 5290 };
+static const uint16_t rcl6[] = /* 2 static turbo channels */
+{ 5760, 5800 };
+static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
+{ 5540, 5580, 5620, 5660 };
+static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+{ 2437 };
+static const uint16_t rcl13[] = /* dynamic Turbo channels */
+{ 5200, 5240, 5280, 5765, 5805 };
+#endif /* ATH_TURBO_SCAN */
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
+static const struct scanlist staScanTable[] = {
+ { IEEE80211_MODE_11B, X(rcl3) },
+ { IEEE80211_MODE_11A, X(rcl1) },
+ { IEEE80211_MODE_11A, X(rcl2) },
+ { IEEE80211_MODE_11B, X(rcl8) },
+ { IEEE80211_MODE_11B, X(rcl9) },
+ { IEEE80211_MODE_11A, X(rcl4) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_STURBO_A, X(rcl5) },
+ { IEEE80211_MODE_STURBO_A, X(rcl6) },
+ { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+ { IEEE80211_MODE_TURBO_A, X(rcl13) },
+#endif /* ATH_TURBO_SCAN */
+ { IEEE80211_MODE_11A, X(rcl7) },
+ { IEEE80211_MODE_11B, X(rcl10) },
+ { IEEE80211_MODE_11A, X(rcl11) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_TURBO_G, X(rcl12) },
+#endif /* ATH_TURBO_SCAN */
+ { .list = NULL }
+};
+
+/*
+ * Start a station-mode scan by populating the channel list.
+ */
+static int
+sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
- * Restart a bg scan.
+ * Restart a scan, typically a bg scan but can
+ * also be a fg scan that came up empty.
*/
static int
-sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
@@ -652,18 +665,44 @@ sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Cancel an ongoing scan.
*/
static int
-sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
return 0;
}
-static uint8_t
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+
+static int
maxrate(const struct ieee80211_scan_entry *se)
{
- uint8_t rmax, r;
- int i;
+ const struct ieee80211_ie_htcap *htcap =
+ (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie;
+ int rmax, r, i;
+ uint16_t caps;
rmax = 0;
+ if (htcap != NULL) {
+ /*
+ * HT station; inspect supported MCS and then adjust
+ * rate by channel width. Could also include short GI
+ * in this if we want to be extra accurate.
+ */
+ /* XXX assumes MCS15 is max */
+ for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--)
+ ;
+ if (i >= 0) {
+ caps = LE_READ_2(&htcap->hc_cap);
+ /* XXX short/long GI */
+ if (caps & IEEE80211_HTCAP_CHWIDTH40)
+ rmax = ieee80211_htrates[i].ht40_rate_400ns;
+ else
+ rmax = ieee80211_htrates[i].ht40_rate_800ns;
+ }
+ }
for (i = 0; i < se->se_rates[1]; i++) {
r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
if (r > rmax)
@@ -691,7 +730,7 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
if (((_a) ^ (_b)) & (_what)) \
return ((_a) & (_what)) ? 1 : -1; \
} while (0)
- uint8_t maxa, maxb;
+ int maxa, maxb;
int8_t rssia, rssib;
int weight;
@@ -722,12 +761,8 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
return maxa - maxb;
/* XXX use freq for channel preference */
/* for now just prefer 5Ghz band to all other bands */
- if (IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return 1;
- if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return -1;
+ PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan),
+ IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1);
}
/* all things being equal, use signal level */
return a->base.se_rssi - b->base.se_rssi;
@@ -736,20 +771,23 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
/*
* Check rate set suitability and return the best supported rate.
+ * XXX inspect MCS for HT
*/
static int
-check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se)
+check_rate(struct ieee80211vap *vap, const struct ieee80211_scan_entry *se)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
const struct ieee80211_rateset *srs;
- int i, j, nrs, r, okrate, badrate, fixedrate;
+ int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate;
const uint8_t *rs;
- okrate = badrate = fixedrate = 0;
+ okrate = badrate = 0;
- srs = ieee80211_get_suprates(ic, se->se_chan);
+ srs = ieee80211_get_suprates(vap->iv_ic, se->se_chan);
nrs = se->se_rates[1];
rs = se->se_rates+2;
+ /* XXX MCS */
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(se->se_chan)].ucastrate;
fixedrate = IEEE80211_FIXED_RATE_NONE;
again:
for (i = 0; i < nrs; i++) {
@@ -758,7 +796,7 @@ again:
/*
* Check any fixed rate is included.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against our supported rates.
@@ -787,7 +825,7 @@ again:
}
back:
- if (okrate == 0 || ic->ic_fixed_rate != fixedrate)
+ if (okrate == 0 || ucastrate != fixedrate)
return badrate | IEEE80211_RATE_BASIC;
else
return RV(okrate);
@@ -812,13 +850,14 @@ match_ssid(const uint8_t *ie,
* Test a scan candidate for suitability/compatibility.
*/
static int
-match_bss(struct ieee80211com *ic,
+match_bss(struct ieee80211vap *vap,
const struct ieee80211_scan_state *ss, struct sta_entry *se0,
int debug)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_entry *se = &se0->base;
- uint8_t rate;
- int fail;
+ uint8_t rate;
+ int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
@@ -830,18 +869,33 @@ match_bss(struct ieee80211com *ic,
* list so we check the desired mode here to weed them
* out.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
(se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
- chanflags[ic->ic_des_mode])
+ chanflags[vap->iv_des_mode])
fail |= MATCH_CHANNEL;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= MATCH_CAPINFO;
} else {
if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= MATCH_CAPINFO;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then do
+ * not attempt to join the bss. We should have already
+ * dispatched an event to user space that identifies the
+ * new country code so our regdomain config should match.
+ */
+ if ((IEEE80211_IS_CHAN_11D(se->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ se->se_cc[0] != 0 &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(se->se_cc, ic->ic_regdomain.isocc)))
+ fail |= MATCH_CC;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= MATCH_PRIVACY;
} else {
@@ -849,27 +903,27 @@ match_bss(struct ieee80211com *ic,
if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= MATCH_PRIVACY;
}
- rate = check_rate(ic, se);
+ rate = check_rate(vap, se);
if (rate & IEEE80211_RATE_BASIC)
fail |= MATCH_RATE;
if (ss->ss_nssid != 0 &&
!match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
fail |= MATCH_SSID;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid))
- fail |= MATCH_BSSID;
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid))
+ fail |= MATCH_BSSID;
if (se0->se_fails >= STA_FAILS_MAX)
fail |= MATCH_FAILS;
- /* NB: entries may be present awaiting purge, skip */
if (se0->se_notseen >= STA_PURGE_SCANS)
fail |= MATCH_NOTSEEN;
if (se->se_rssi < STA_RSSI_MIN)
fail |= MATCH_RSSI;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg(ic, debug)) {
+ if (ieee80211_msg(vap, debug)) {
printf(" %c %s",
fail & MATCH_FAILS ? '=' :
fail & MATCH_NOTSEEN ? '^' :
+ fail & MATCH_CC ? '$' :
fail ? '-' : '+', ether_sprintf(se->se_macaddr));
printf(" %s%c", ether_sprintf(se->se_bssid),
fail & MATCH_BSSID ? '!' : ' ');
@@ -929,16 +983,17 @@ sta_dec_fails(struct sta_table *st)
}
static struct sta_entry *
-select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
+select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se, *selbs = NULL;
- IEEE80211_DPRINTF(ic, debug, " %s\n",
+ IEEE80211_DPRINTF(vap, debug, " %s\n",
"macaddr bssid chan rssi rate flag wep essid");
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
- if (match_bss(ic, ss, se, debug) == 0) {
+ if (match_bss(vap, ss, se, debug) == 0) {
+ ieee80211_ies_expand(&se->base.se_ies);
if (selbs == NULL)
selbs = se;
else if (sta_compare(se, selbs) > 0)
@@ -955,13 +1010,13 @@ select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
* to use to start an ibss network.
*/
static int
-sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -982,8 +1037,10 @@ sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
/*
* If nothing suitable was found decrement
@@ -996,8 +1053,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1024,12 +1083,14 @@ sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
}
static void
-sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
struct sta_table *st = ss->ss_priv;
+ enum ieee80211_phymode mode;
struct sta_entry *se, *selbs;
- uint8_t roamRate, curRate;
+ uint8_t roamRate, curRate, ucastRate;
int8_t roamRssi, curRssi;
se = sta_lookup(st, ni->ni_macaddr);
@@ -1038,27 +1099,21 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
return;
}
- /* XXX do we need 11g too? */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11b;
- roamRssi = ic->ic_roam.rssi11b;
- } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11bOnly;
- roamRssi = ic->ic_roam.rssi11bOnly;
- } else {
- roamRate = ic->ic_roam.rate11a;
- roamRssi = ic->ic_roam.rssi11a;
- }
+ mode = ieee80211_chan2mode(ic->ic_bsschan);
+ roamRate = vap->iv_roamparms[mode].rate;
+ roamRssi = vap->iv_roamparms[mode].rssi;
+ ucastRate = vap->iv_txparms[mode].ucastrate;
/* NB: the most up to date rssi is in the node, not the scan cache */
curRssi = ic->ic_node_getrssi(ni);
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ if (ucastRate == IEEE80211_FIXED_RATE_NONE) {
+ curRate = ni->ni_txrate;
+ roamRate &= IEEE80211_RATE_VAL;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d currate %u roamrssi %d roamrate %u\n",
__func__, curRssi, curRate, roamRssi, roamRate);
} else {
curRate = roamRate; /* NB: insure compare below fails */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
}
/*
@@ -1066,7 +1121,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX deauth current ap
*/
if (curRate < roamRate || curRssi < roamRssi) {
- if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* Scan cache contents are too old; force a scan now
* if possible so we have current state to make a
@@ -1076,19 +1131,19 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX force immediate switch on scan complete
*/
if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle))
- ieee80211_bg_scan(ic);
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))
+ ieee80211_bg_scan(vap, 0);
return;
}
se->base.se_rssi = curRssi;
- selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM);
+ selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM);
if (selbs != NULL && selbs != se) {
- IEEE80211_DPRINTF(ic,
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
"%s: ROAM: curRate %u, roamRate %u, "
"curRssi %d, roamRssi %d\n", __func__,
curRate, roamRate, curRssi, roamRssi);
- ieee80211_sta_join(ic, &selbs->base);
+ ieee80211_sta_join(vap, &selbs->base);
}
}
}
@@ -1100,19 +1155,9 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
static void
sta_age(struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
- struct sta_table *st = ss->ss_priv;
- struct sta_entry *se, *next;
+ struct ieee80211vap *vap = ss->ss_vap;
- mtx_lock(&st->st_lock);
- TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
- if (se->se_notseen > STA_PURGE_SCANS) {
- TAILQ_REMOVE(&st->st_entry, se, se_list);
- LIST_REMOVE(se, se_hash);
- FREE(se, M_80211_SCAN);
- }
- }
- mtx_unlock(&st->st_lock);
+ adhoc_age(ss);
/*
* If rate control is enabled check periodically to see if
* we should roam from our current connection to one that
@@ -1122,13 +1167,13 @@ sta_age(struct ieee80211_scan_state *ss)
* XXX repeater station
* XXX do when !bgscan?
*/
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
- (ic->ic_flags & IEEE80211_F_BGSCAN) &&
- ic->ic_state >= IEEE80211_S_RUN)
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ vap->iv_state >= IEEE80211_S_RUN)
/* XXX vap is implicit */
- sta_roam_check(ss, ic);
+ sta_roam_check(ss, vap);
}
/*
@@ -1144,7 +1189,7 @@ sta_iterate(struct ieee80211_scan_state *ss,
u_int gen;
mtx_lock(&st->st_scanlock);
- gen = st->st_scangen++;
+ gen = st->st_scaniter++;
restart:
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1173,7 +1218,7 @@ sta_assoc_fail(struct ieee80211_scan_state *ss,
if (se != NULL) {
se->se_fails++;
se->se_lastfail = ticks;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: reason %u fails %u",
__func__, reason, se->se_fails);
}
@@ -1190,7 +1235,7 @@ sta_assoc_success(struct ieee80211_scan_state *ss,
if (se != NULL) {
#if 0
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: fails %u",
__func__, se->se_fails);
#endif
@@ -1240,93 +1285,30 @@ static const struct scanlist adhocScanTable[] = {
* Start an adhoc-mode scan by populating the channel list.
*/
static int
-adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
struct sta_table *st = ss->ss_priv;
- const struct scanlist *scan;
- enum ieee80211_phymode mode;
- ss->ss_last = 0;
- /*
- * Use the table of ordered channels to construct the list
- * of channels for scanning. Any channels in the ordered
- * list not in the master list will be discarded.
- */
- for (scan = adhocScanTable; scan->list != NULL; scan++) {
- mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
- /*
- * If a desired mode was specified, scan only
- * channels that satisfy that constraint.
- */
- if (ic->ic_des_mode != mode) {
- /*
- * The scan table marks 2.4Ghz channels as b
- * so if the desired mode is 11g, then use
- * the 11b channel list but upgrade the mode.
- */
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
- mode != IEEE80211_MODE_11B)
- continue;
- mode = IEEE80211_MODE_11G; /* upgrade */
- }
- } else {
- /*
- * This lets add_channels upgrade an 11b channel
- * to 11g if available.
- */
- if (mode == IEEE80211_MODE_11B)
- mode = IEEE80211_MODE_AUTO;
- }
-#ifdef IEEE80211_F_XR
- /* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
- (mode == IEEE80211_MODE_TURBO_A ||
- mode == IEEE80211_MODE_TURBO_G))
- continue;
-#endif
- /*
- * Add the list of the channels; any that are not
- * in the master channel list will be discarded.
- */
- add_channels(ic, ss, mode, scan->list, scan->count);
- }
-
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
- sweepchannels(ss, ic, adhocScanTable);
-
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ makescanlist(ss, vap, adhocScanTable);
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
* Select a channel to start an adhoc network on.
* The channel list was populated with appropriate
* channels so select one that looks least occupied.
- * XXX need regulatory domain constraints
*/
static struct ieee80211_channel *
-adhoc_pick_channel(struct ieee80211_scan_state *ss)
+adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
@@ -1339,7 +1321,14 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
mtx_lock(&st->st_lock);
for (i = 0; i < ss->ss_last; i++) {
c = ss->ss_chans[i];
- if (!checktable(adhocScanTable, c))
+ /* never consider a channel with radar */
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ continue;
+ /* skip channels disallowed by regulatory settings */
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (c->ic_flags & flags) != flags)
continue;
maxrssi = 0;
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1361,15 +1350,15 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
* to use to start an ibss network.
*/
static int
-adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
- KASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO,
- ("wrong opmode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO,
+ ("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -1390,22 +1379,26 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
- if (ic->ic_des_nssid) {
+ if (vap->iv_des_nssid) {
/*
* No existing adhoc network to join and we have
* an ssid; start one up. If no channel was
* specified, try to select a channel.
*/
- if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
- chan = ieee80211_ht_adjust_channel(ic,
- adhoc_pick_channel(ss), ic->ic_flags_ext);
- else
- chan = ic->ic_des_chan;
+ if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
+ IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ chan = ieee80211_ht_adjust_channel(vap->iv_ic,
+ adhoc_pick_channel(ss, 0),
+ vap->iv_flags_ext);
+ } else
+ chan = vap->iv_des_chan;
if (chan != NULL) {
- ieee80211_create_ibss(ic, chan);
+ ieee80211_create_ibss(vap, chan);
return 1;
}
}
@@ -1420,8 +1413,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1440,6 +1435,7 @@ adhoc_age(struct ieee80211_scan_state *ss)
if (se->se_notseen > STA_PURGE_SCANS) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
}
@@ -1455,6 +1451,7 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_cancel = sta_cancel,
.scan_end = adhoc_pick_bss,
.scan_flush = sta_flush,
+ .scan_pickchan = adhoc_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
@@ -1462,39 +1459,161 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_assoc_success = sta_assoc_success,
};
+static void
+ap_force_promisc(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ IEEE80211_LOCK(ic);
+ /* set interface into promiscuous mode */
+ ifp->if_flags |= IFF_PROMISC;
+ ic->ic_update_promisc(ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+ap_reset_promisc(struct ieee80211com *ic)
+{
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ IEEE80211_UNLOCK(ic);
+}
+
+static int
+ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
+ st->st_newscan = 1;
+
+ ap_force_promisc(vap->iv_ic);
+ return 0;
+}
+
/*
- * Module glue.
+ * Cancel an ongoing scan.
*/
static int
-wlan_modevent(module_t mod, int type, void *unused)
+ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_STA, &sta_default);
- ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default);
- ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_sta: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
+ ap_reset_promisc(vap->iv_ic);
+ return 0;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static struct ieee80211_channel *
+ap_pick_channel(struct ieee80211_scan_state *ss, int flags)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct ieee80211_channel *bestchan = NULL;
+ int i;
+
+ /* XXX select channel more intelligently, e.g. channel spread, power */
+ /* NB: use scan list order to preserve channel preference */
+ for (i = 0; i < ss->ss_last; i++) {
+ struct ieee80211_channel *chan = ss->ss_chans[i];
+ /*
+ * If the channel is unoccupied the max rssi
+ * should be zero; just take it. Otherwise
+ * track the channel with the lowest rssi and
+ * use that when all channels appear occupied.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(chan))
+ continue;
+ if (IEEE80211_IS_CHAN_NOHOSTAP(chan))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (chan->ic_flags & flags) != flags)
+ continue;
+ /* XXX channel have interference */
+ if (st->st_maxrssi[chan->ic_ieee] == 0) {
+ /* XXX use other considerations */
+ return chan;
}
- if (type == MOD_UNLOAD) {
- ieee80211_scanner_unregister_all(&sta_default);
- ieee80211_scanner_unregister_all(&adhoc_default);
+ if (bestchan == NULL ||
+ st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee])
+ bestchan = chan;
+ }
+ return bestchan;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static int
+ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *bestchan;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("wrong opmode %u", vap->iv_opmode));
+ bestchan = ap_pick_channel(ss, 0);
+ if (bestchan == NULL) {
+ /* no suitable channel, should not happen */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no suitable channel! (should not happen)\n", __func__);
+ /* XXX print something? */
+ return 0; /* restart scan */
+ }
+ /*
+ * If this is a dynamic turbo channel, start with the unboosted one.
+ */
+ if (IEEE80211_IS_CHAN_TURBO(bestchan)) {
+ bestchan = ieee80211_find_channel(ic, bestchan->ic_freq,
+ bestchan->ic_flags & ~IEEE80211_CHAN_TURBO);
+ if (bestchan == NULL) {
+ /* should never happen ?? */
+ return 0;
}
- return 0;
}
- return EINVAL;
+ ap_reset_promisc(ic);
+ if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ext));
+ return 1;
}
-static moduledata_t wlan_mod = {
- "wlan_scan_sta",
- wlan_modevent,
- 0
+static const struct ieee80211_scanner ap_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = ap_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = ap_cancel,
+ .scan_end = ap_end,
+ .scan_flush = sta_flush,
+ .scan_pickchan = ap_pick_channel,
+ .scan_add = sta_add,
+ .scan_age = adhoc_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_success = sta_assoc_success,
+ .scan_assoc_fail = sta_assoc_fail,
};
-DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_sta, 1);
-MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1);
+
+/*
+ * Module glue.
+ */
+IEEE80211_SCANNER_MODULE(sta, 1);
+IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
+IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
+IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
+IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
new file mode 100644
index 0000000..e3c57ad
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.c
@@ -0,0 +1,1647 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Station mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_input.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void sta_vattach(struct ieee80211vap *);
+static void sta_beacon_miss(struct ieee80211vap *);
+static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int sta_input(struct ieee80211_node *, struct mbuf *,
+ int rssi, int noise, uint32_t rstamp);
+static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_sta_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
+}
+
+void
+ieee80211_sta_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+sta_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+sta_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = sta_newstate;
+ vap->iv_input = sta_input;
+ vap->iv_recv_mgmt = sta_recv_mgmt;
+ vap->iv_opdetach = sta_vdetach;
+ vap->iv_bmiss = sta_beacon_miss;
+}
+
+/*
+ * Handle a beacon miss event. The common code filters out
+ * spurious events that can happen when scanning and/or before
+ * reaching RUN state.
+ */
+static void
+sta_beacon_miss(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
+ KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ ("wrong state %d", vap->iv_state));
+
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "beacon miss, mode %u state %s\n",
+ vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
+
+ if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
+ /*
+ * Send a directed probe req before falling back to a
+ * scan; if we receive a response ic_bmiss_count will
+ * be reset. Some cards mistakenly report beacon miss
+ * so this avoids the expensive scan if the ap is
+ * still there.
+ */
+ ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
+ vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
+ vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
+ return;
+ }
+ vap->iv_bmiss_count = 0;
+ vap->iv_stats.is_beacon_miss++;
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /*
+ * If we receive a beacon miss interrupt when using
+ * dynamic turbo, attempt to switch modes before
+ * reassociating.
+ */
+ if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
+ ieee80211_dturbo_switch(vap,
+ ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
+ /*
+ * Try to reassociate before scanning for a new ap.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
+ } else {
+ /*
+ * Somebody else is controlling state changes (e.g.
+ * a user-mode app) don't do anything that would
+ * confuse them; just drop into scan mode so they'll
+ * notified of the state change and given control.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+}
+
+/*
+ * Handle deauth with reason. We retry only for
+ * the cases where we might succeed. Otherwise
+ * we downgrade the ap and scan.
+ */
+static void
+sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
+{
+ switch (reason) {
+ case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */
+ case IEEE80211_STATUS_TIMEOUT:
+ case IEEE80211_REASON_ASSOC_EXPIRE:
+ case IEEE80211_REASON_NOT_AUTHED:
+ case IEEE80211_REASON_NOT_ASSOCED:
+ case IEEE80211_REASON_ASSOC_LEAVE:
+ case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ default:
+ ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ }
+}
+
+/*
+ * IEEE80211_M_STA vap state machine handler.
+ * This routine handles the main states in the 802.11 protocol.
+ */
+static int
+sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SLEEP:
+ /* XXX wakeup */
+ case IEEE80211_S_RUN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_sta_leave(ni);
+ break;
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ break;
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ /*
+ * These can happen either because of a timeout
+ * on an assoc/auth response or because of a
+ * change in state that requires a reset. For
+ * the former we're called with a non-zero arg
+ * that is the cause for the failure; pass this
+ * to the scan code so it can update state.
+ * Otherwise trigger a new scan unless we're in
+ * manual roaming mode in which case an application
+ * must issue an explicit scan request.
+ */
+ if (arg != 0)
+ ieee80211_scan_assoc_fail(vap,
+ vap->iv_bss->ni_macaddr, arg);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_RUN: /* beacon miss */
+ /*
+ * Beacon miss. Notify user space and if not
+ * under control of a user application (roaming
+ * manual) kick off a scan to re-connect.
+ */
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ /* ??? */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ sta_authretry(vap, ni, arg>>8);
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ vap->iv_state = ostate; /* stay RUN */
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /* try to reauth */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ }
+ break;
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_SLEEP: /* cannot happen */
+ case IEEE80211_S_RUN:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ IEEE80211_SEND_MGMT(ni, arg ?
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_RUN:
+ break;
+ case IEEE80211_S_AUTH: /* when join is done in fw */
+ case IEEE80211_S_ASSOC:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap, "%s with %s ssid ",
+ (vap->iv_opmode == IEEE80211_M_STA ?
+ "associated" : "synchronized"),
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
+ ieee80211_notify_node_join(ni,
+ arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ goto invalid;
+ }
+ ieee80211_sync_curchan(ic);
+ if (ostate != IEEE80211_S_RUN &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ /*
+ * Start s/w beacon miss timer for devices w/o
+ * hardware support. We fudge a bit here since
+ * we're doing this in software.
+ */
+ vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
+ 2 * vap->iv_bmissthreshold * ni->ni_intval);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be DSTODS.
+ */
+static __inline int
+isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh)
+#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh)
+ const uint8_t *sa;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
+ return 0;
+ sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
+ return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
+#undef WH4
+#undef QWH4
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be FROMDS.
+ */
+static __inline int
+isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ return 0;
+ return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+sta_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ bssid = wh->i_addr2;
+ if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ (dir == IEEE80211_FC1_DIR_FROMDS ||
+ dir == IEEE80211_FC1_DIR_DSTODS) &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+ if (dir == IEEE80211_FC1_DIR_FROMDS) {
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isfromds_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_DWDS) &&
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /*
+ * DWDS sta's must drop 3-address mcast frames
+ * as they will be sent separately as a 4-addr
+ * frame. Accepting the 3-addr frame will
+ * confuse the bridge into thinking the sending
+ * sta is located at the end of WDS link.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "3-address data", "%s", "DWDS enabled");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else if (dir == IEEE80211_FC1_DIR_DSTODS) {
+ if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "4-address data",
+ "%s", "DWDS not enabled");
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isdstods_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "4-address data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "bad frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ return;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH ||
+ seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "open auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ } else
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+}
+
+static void
+sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int estatus;
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH)
+ return;
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_PASS:
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
+ "shared key auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ return;
+ }
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ if (!ieee80211_alloc_challenge(ni))
+ return;
+ /* XXX could optimize by passing recvd challenge */
+ memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
+ wh, "shared key auth", "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ return;
+bad:
+ /*
+ * Kick the state machine. This short-circuits
+ * using the mgt frame timeout to trigger the
+ * state transition.
+ */
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+}
+
+static int
+ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
+ u_int len = frm[1], qosinfo;
+ int i;
+
+ if (len < sizeof(struct ieee80211_wme_param)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+ wh, "WME", "too short, len %u", len);
+ return -1;
+ }
+ qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
+ qosinfo &= WME_QOSINFO_COUNT;
+ /* XXX do proper check for wraparound */
+ if (qosinfo == wme->wme_wmeChanParams.cap_info)
+ return 0;
+ frm += __offsetof(struct ieee80211_wme_param, params_acParams);
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct wmeParams *wmep =
+ &wme->wme_wmeChanParams.cap_wmeParams[i];
+ /* NB: ACI not used */
+ wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+ wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+ wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+ wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+ wmep->wmep_txopLimit = LE_READ_2(frm+2);
+ frm += 4;
+ }
+ wme->wme_wmeChanParams.cap_info = qosinfo;
+ return 1;
+#undef MS
+}
+
+static int
+ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_ath_ie *ath;
+ u_int len = frm[1];
+ int capschanged;
+ uint16_t defkeyix;
+
+ if (len < sizeof(struct ieee80211_ath_ie)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
+ wh, "Atheros", "too short, len %u", len);
+ return -1;
+ }
+ ath = (const struct ieee80211_ath_ie *)frm;
+ capschanged = (ni->ni_ath_flags != ath->ath_capability);
+ defkeyix = LE_READ_2(ath->ath_defkeyix);
+ if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
+ ni->ni_ath_flags = ath->ath_capability;
+ ni->ni_ath_defkeyix = defkeyix;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
+ "ath ie change: new caps 0x%x defkeyix 0x%x",
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ }
+ if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
+ uint16_t curflags, newflags;
+
+ /*
+ * Check for turbo mode switch. Calculate flags
+ * for the new mode and effect the switch.
+ */
+ newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
+ /* NB: BOOST is not in ic_flags, so get it from the ie */
+ if (ath->ath_capability & ATHEROS_CAP_BOOST)
+ newflags |= IEEE80211_CHAN_TURBO;
+ else
+ newflags &= ~IEEE80211_CHAN_TURBO;
+ if (newflags != curflags)
+ ieee80211_dturbo_switch(vap, newflags);
+ }
+ return capschanged;
+}
+
+/*
+ * Return non-zero if a background scan may be continued:
+ * o bg scan is active
+ * o no channel switch is pending
+ * o there has not been any traffic recently
+ *
+ * Note we do not check if there is an administrative enable;
+ * this is only done to start the scan. We assume that any
+ * change in state will be accompanied by a request to cancel
+ * active scans which will otherwise cause this test to fail.
+ */
+static __inline int
+contbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ vap->iv_state == IEEE80211_S_RUN && /* XXX? */
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+/*
+ * Return non-zero if a backgrond scan may be started:
+ * o bg scanning is administratively enabled
+ * o no channel switch is pending
+ * o we are not boosted on a dynamic turbo channel
+ * o there has not been a scan recently
+ * o there has not been any traffic recently
+ */
+static __inline int
+startbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+ time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+static void
+sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
+ uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames:
+ * o when scanning, or
+ * o station mode when associated (to collect state
+ * updates such as 802.11g slot time), or
+ * Frames otherwise received are discarded.
+ */
+ if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* XXX probe response in sta mode when !scanning? */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * When operating in station mode, check for state updates.
+ * Be careful to ignore beacons received while doing a
+ * background scan. We consider only 11g/WMM stuff right now.
+ */
+ if (ni->ni_associd != 0 &&
+ ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+ IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
+ /* record tsf of last beacon */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ /* count beacon frame for s/w bmiss handling */
+ vap->iv_swbmiss_count++;
+ vap->iv_bmiss_count = 0;
+ if (ni->ni_erp != scan.erp) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "erp change: was 0x%x, now 0x%x",
+ ni->ni_erp, scan.erp);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ ni->ni_erp = scan.erp;
+ /* XXX statistic */
+ /* XXX driver notification */
+ }
+ if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "capabilities change: was 0x%x, now 0x%x",
+ ni->ni_capinfo, scan.capinfo);
+ /*
+ * NB: we assume short preamble doesn't
+ * change dynamically
+ */
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
+ (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
+ /* XXX statistic */
+ }
+ if (scan.wme != NULL &&
+ (ni->ni_flags & IEEE80211_NODE_QOS) &&
+ ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
+ ieee80211_wme_updateparams(vap);
+ if (scan.ath != NULL)
+ ieee80211_parse_athparams(ni, scan.ath, wh);
+ if (scan.htcap != NULL && scan.htinfo != NULL) {
+ ieee80211_parse_htcap(ni, scan.htcap);
+ ieee80211_parse_htinfo(ni, scan.htinfo);
+ /* XXX state changes? */
+ }
+ if (scan.tim != NULL) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *) scan.tim;
+#if 0
+ int aid = IEEE80211_AID(ni->ni_associd);
+ int ix = aid / NBBY;
+ int min = tim->tim_bitctl &~ 1;
+ int max = tim->tim_len + min - 4;
+ if ((tim->tim_bitctl&1) ||
+ (min <= ix && ix <= max &&
+ isset(tim->tim_bitmap - min, aid))) {
+ /*
+ * XXX Do not let bg scan kick off
+ * we are expecting data.
+ */
+ ic->ic_lastdata = ticks;
+ ieee80211_sta_pwrsave(vap, 0);
+ }
+#endif
+ ni->ni_dtim_count = tim->tim_count;
+ ni->ni_dtim_period = tim->tim_period;
+ }
+ /*
+ * If scanning, pass the info to the scan module.
+ * Otherwise, check if it's the right time to do
+ * a background scan. Background scanning must
+ * be enabled and we must not be operating in the
+ * turbo phase of dynamic turbo mode. Then,
+ * it's been a while since the last background
+ * scan and if no data frames have come through
+ * recently, kick off a scan. Note that this
+ * is the mechanism by which a background scan
+ * is started _and_ continued each time we
+ * return on-channel to receive a beacon from
+ * our ap.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ } else if (contbgscan(vap)) {
+ ieee80211_bg_scan(vap, 0);
+ } else if (startbgscan(vap)) {
+ vap->iv_stats.is_scan_bg++;
+#if 0
+ /* wakeup if we are sleeing */
+ ieee80211_set_pwrsave(vap, 0);
+#endif
+ ieee80211_bg_scan(vap, 0);
+ }
+ return;
+ }
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, "auth", "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ }
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ sta_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ sta_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "auth", "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+ uint16_t capinfo, associd;
+ uint16_t status;
+
+ if (vap->iv_state != IEEE80211_S_ASSOC) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+
+ /*
+ * asresp frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] HT info
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ ni = vap->iv_bss;
+ capinfo = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ status = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ if (status != 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2, "%sassoc failed (reason %d)",
+ ISREASSOC(subtype) ? "re" : "", status);
+ vap->iv_stats.is_rx_auth_fail++; /* XXX */
+ return;
+ }
+ associd = le16toh(*(uint16_t *)frm);
+ frm += 2;
+
+ rates = xrates = wme = htcap = htinfo = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswmeoui(frm))
+ wme = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (htcap == NULL)
+ htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (htinfo == NULL)
+ htcap = frm;
+ }
+ }
+ /* XXX Atheros OUI support */
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_JOIN |
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "%sassoc failed (rate set mismatch)",
+ ISREASSOC(subtype) ? "re" : "");
+ vap->iv_stats.is_rx_assoc_norate++;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ return;
+ }
+
+ ni->ni_capinfo = capinfo;
+ ni->ni_associd = associd;
+ if (ni->ni_jointime == 0)
+ ni->ni_jointime = time_uptime;
+ if (wme != NULL &&
+ ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ ieee80211_wme_updateparams(vap);
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ /*
+ * Setup HT state according to the negotiation.
+ *
+ * NB: shouldn't need to check if HT use is enabled but some
+ * ap's send back HT ie's even when we don't indicate we
+ * are HT capable in our AssocReq.
+ */
+ if (htcap != NULL && htinfo != NULL &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ ieee80211_ht_node_init(ni, htcap);
+ ieee80211_parse_htinfo(ni, htinfo);
+ ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni, htinfo);
+ }
+ /*
+ * Configure state now that we are associated.
+ *
+ * XXX may need different/additional driver callbacks?
+ */
+ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ }
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ /*
+ * Honor ERP protection.
+ *
+ * NB: ni_erp should zero for non-11g operation.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
+ "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
+ ISREASSOC(subtype) ? "re" : "",
+ IEEE80211_NODE_AID(ni),
+ ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
+ ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+ ni->ni_flags & IEEE80211_NODE_HT ?
+ (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+ ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
+ ", fast-frames" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
+ ", turbo" : ""
+ );
+ ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH: {
+ uint16_t reason;
+
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * deauth frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_deauth++;
+ vap->iv_stats.is_rx_deauth_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv deauthenticate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
+ (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_ASSOC &&
+ vap->iv_state != IEEE80211_S_AUTH) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_disassoc++;
+ vap->iv_stats.is_rx_disassoc_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "recv disassociate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+#undef ISREASSOC
+#undef ISPROBE
+}
diff --git a/sys/net80211/ieee80211_sta.h b/sys/net80211/ieee80211_sta.h
new file mode 100644
index 0000000..1508a7c
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_STA_H_
+#define _NET80211_IEEE80211_STA_H_
+
+/*
+ * Station-mode implementation definitions.
+ */
+void ieee80211_sta_attach(struct ieee80211com *);
+void ieee80211_sta_detach(struct ieee80211com *);
+void ieee80211_sta_vattach(struct ieee80211vap *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 3171cef..5ada0a2 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,6 @@
/*
* Definitions for IEEE 802.11 drivers.
*/
-#define IEEE80211_DEBUG
-#undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */
-
/* NB: portability glue must go first */
#ifdef __NetBSD__
#include <net80211/ieee80211_netbsd.h>
@@ -48,6 +45,7 @@
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_dfs.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_power.h>
@@ -75,8 +73,8 @@
#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
-#define IEEE80211_FIXED_RATE_NONE -1
-#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */
+#define IEEE80211_FIXED_RATE_NONE 0xff
+#define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */
#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX
#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX
@@ -85,40 +83,57 @@
#define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000)
#define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
-struct ieee80211_aclator;
-struct sysctl_ctx_list;
+/*
+ * 802.11 control state is split into a common portion that maps
+ * 1-1 to a physical device and one or more "Virtual AP's" (VAP)
+ * that are bound to an ieee80211com instance and share a single
+ * underlying device. Each VAP has a corresponding OS device
+ * entity through which traffic flows and that applications use
+ * for issuing ioctls, etc.
+ */
+
+/*
+ * Data common to one or more virtual AP's. State shared by
+ * the underlying device and the net80211 layer is exposed here;
+ * e.g. device-specific callbacks.
+ */
+struct ieee80211vap;
+typedef void (*ieee80211vap_attach)(struct ieee80211vap *);
+
+struct ieee80211_appie {
+ uint16_t ie_len; /* size of ie_data */
+ uint8_t ie_data[]; /* user-specified IE's */
+};
struct ieee80211com {
- SLIST_ENTRY(ieee80211com) ic_next;
struct ifnet *ic_ifp; /* associated device */
ieee80211_com_lock_t ic_comlock; /* state update lock */
- ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
+ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */
struct ieee80211_stats ic_stats; /* statistics */
- struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */
- uint32_t ic_debug; /* debug msg flags */
- int ic_vap; /* virtual AP index */
int ic_headroom; /* driver tx headroom needs */
enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
enum ieee80211_opmode ic_opmode; /* operation mode */
struct ifmedia ic_media; /* interface media config */
uint8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct callout ic_inact; /* inactivity processing */
+ struct task ic_parent_task; /* deferred parent processing */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
uint32_t ic_flags_ven; /* vendor state flags */
uint32_t ic_caps; /* capabilities */
uint32_t ic_htcaps; /* HT capabilities */
+ uint32_t ic_cryptocaps; /* crypto capabilities */
uint8_t ic_modecaps[2]; /* set of mode capabilities */
- uint16_t ic_curmode; /* current mode */
- struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ uint8_t ic_promisc; /* vap's needing promisc mode */
+ uint8_t ic_allmulti; /* vap's needing all multicast*/
+ uint8_t ic_nrunning; /* vap's marked running */
+ uint8_t ic_curmode; /* current mode */
uint16_t ic_bintval; /* beacon interval */
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
- int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
- int ic_ampdu_density;/* A-MPDU density */
- int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
- int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
/*
* Channel state:
@@ -148,30 +163,27 @@ struct ieee80211com {
struct ieee80211_channel *ic_curchan; /* current channel */
struct ieee80211_channel *ic_bsschan; /* bss channel */
struct ieee80211_channel *ic_prevchan; /* previous channel */
- int ic_countrycode; /* ISO country code */
- uint16_t ic_regdomain; /* regulatory domain */
- uint8_t ic_location; /* unknown, indoor, outdoor */
+ struct ieee80211_regdomain ic_regdomain;/* regulatory data */
+ struct ieee80211_appie *ic_countryie; /* calculated country ie */
+ struct ieee80211_channel *ic_countryie_chan;
+
+ /* 802.11h/DFS state */
+ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */
+ int ic_csa_count; /* count for doing CSA */
+ struct ieee80211_dfs_state ic_dfs; /* DFS state */
struct ieee80211_scan_state *ic_scan; /* scan state */
- enum ieee80211_roamingmode ic_roaming; /* roaming mode */
int ic_lastdata; /* time of last data frame */
int ic_lastscan; /* time last scan completed */
- int ic_des_nssid; /* # desired ssids */
- struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */
- uint8_t ic_des_bssid[IEEE80211_ADDR_LEN];
- struct ieee80211_channel *ic_des_chan; /* desired channel */
- int ic_des_mode; /* desired phymode */
- u_int ic_bgscanidle; /* bg scan idle threshold */
- u_int ic_bgscanintvl; /* bg scan min interval */
- u_int ic_scanvalid; /* scan cache valid threshold */
- struct ieee80211_roam ic_roam; /* sta-mode roaming state */
+ /* NB: this is the union of all vap stations/neighbors */
+ int ic_max_keyix; /* max h/w key index */
struct ieee80211_node_table ic_sta; /* stations/neighbors */
+ /* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
- const struct ieee80211_aclator *ic_acl; /* aclator glue */
- void *ic_as; /* private aclator state */
+ /* XXX multi-bss: can per-vap be done/make sense? */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
@@ -183,91 +195,56 @@ struct ieee80211com {
int ic_lastnonerp; /* last time non-ERP sta noted*/
int ic_lastnonht; /* last time non-HT sta noted */
- struct ifqueue ic_mgtq;
- enum ieee80211_state ic_state; /* 802.11 state */
- struct callout ic_mgtsend; /* mgmt frame response timer */
- uint32_t *ic_aid_bitmap; /* association id map */
- uint16_t ic_max_aid;
- uint16_t ic_ps_sta; /* stations in power save */
- uint16_t ic_ps_pending; /* ps sta's w/ pending frames */
- uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/
- uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */
- uint8_t ic_dtim_period; /* DTIM period */
- uint8_t ic_dtim_count; /* DTIM count for last bcn */
- struct bpf_if *ic_rawbpf; /* packet filter structure */
- struct ieee80211_node *ic_bss; /* information for this node */
- int ic_fixed_rate; /* 802.11 rate or -1 */
- int ic_mcast_rate; /* rate for mcast frames */
- uint16_t ic_rtsthreshold;
- uint16_t ic_fragthreshold;
- uint8_t ic_bmissthreshold;
- uint8_t ic_bmiss_count; /* current beacon miss count */
- int ic_bmiss_max; /* max bmiss before scan */
- uint16_t ic_swbmiss_count;/* beacons in last period */
- uint16_t ic_swbmiss_period;/* s/w bmiss period */
- struct callout ic_swbmiss; /* s/w beacon miss timer */
-
- uint16_t ic_txmin; /* min tx retry count */
- uint16_t ic_txmax; /* max tx retry count */
- uint16_t ic_txlifetime; /* tx lifetime */
- struct callout ic_inact; /* inactivity timer wait */
- void *ic_opt_ie; /* user-specified IE's */
- uint16_t ic_opt_ie_len; /* length of ni_opt_ie */
- int ic_inact_init; /* initial setting */
- int ic_inact_auth; /* auth but not assoc setting */
- int ic_inact_run; /* authorized setting */
- int ic_inact_probe; /* inactive probe time */
-
- /*
- * Cipher state/configuration.
- */
- struct ieee80211_crypto_state ic_crypto;
-#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */
-#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */
- /*
- * 802.1x glue. When an authenticator attaches it
- * fills in this section. We assume that when ic_ec
- * is setup that the methods are safe to call.
- */
- const struct ieee80211_authenticator *ic_auth;
- struct eapolcom *ic_ec;
-
+ /* virtual ap create/delete */
+ struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit,
+ int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+ void (*ic_vap_delete)(struct ieee80211vap *);
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+ /* return hardware/radio capabilities */
+ void (*ic_getradiocaps)(struct ieee80211com *,
+ int *, struct ieee80211_channel []);
+ /* check and/or prepare regdomain state change */
+ int (*ic_setregdomain)(struct ieee80211com *,
+ struct ieee80211_regdomain *,
+ int, struct ieee80211_channel []);
/* send/recv 802.11 management frame */
- int (*ic_send_mgmt)(struct ieee80211com *,
- struct ieee80211_node *, int, int);
- void (*ic_recv_mgmt)(struct ieee80211com *,
- struct mbuf *, struct ieee80211_node *,
- int, int, int, uint32_t);
+ int (*ic_send_mgmt)(struct ieee80211_node *,
+ int, int);
/* send raw 802.11 frame */
int (*ic_raw_xmit)(struct ieee80211_node *,
struct mbuf *,
const struct ieee80211_bpf_params *);
- /* reset device state after 802.11 parameter/state change */
- int (*ic_reset)(struct ifnet *);
- /* [schedule] beacon frame update */
- void (*ic_update_beacon)(struct ieee80211com *, int);
/* update device state for 802.11 slot time change */
void (*ic_updateslot)(struct ifnet *);
+ /* handle multicast state changes */
+ void (*ic_update_mcast)(struct ifnet *);
+ /* handle promiscuous mode changes */
+ void (*ic_update_promisc)(struct ifnet *);
/* new station association callback/notification */
void (*ic_newassoc)(struct ieee80211_node *, int);
/* node state management */
- struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*);
+ struct ieee80211_node* (*ic_node_alloc)(struct ieee80211_node_table *);
void (*ic_node_free)(struct ieee80211_node *);
void (*ic_node_cleanup)(struct ieee80211_node *);
+ void (*ic_node_age)(struct ieee80211_node *);
+ void (*ic_node_drain)(struct ieee80211_node *);
int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
void (*ic_node_getsignal)(const struct ieee80211_node*,
int8_t *, int8_t *);
+ void (*ic_node_getmimoinfo)(
+ const struct ieee80211_node*,
+ struct ieee80211_mimo_info *);
/* scanning support */
void (*ic_scan_start)(struct ieee80211com *);
void (*ic_scan_end)(struct ieee80211com *);
void (*ic_set_channel)(struct ieee80211com *);
- void (*ic_scan_curchan)(struct ieee80211com *,
+ void (*ic_scan_curchan)(struct ieee80211_scan_state *,
unsigned long);
- void (*ic_scan_mindwell)(struct ieee80211com *);
- /* per-vap eventually... */
- int (*ic_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- void (*ic_set_tim)(struct ieee80211_node *, int);
+ void (*ic_scan_mindwell)(struct ieee80211_scan_state *);
/*
* 802.11n ADDBA support. A simple/generic implementation
@@ -282,6 +259,9 @@ struct ieee80211com {
int (*ic_send_action)(struct ieee80211_node *,
int category, int action,
uint16_t args[4]);
+ /* check if A-MPDU should be enabled this station+ac */
+ int (*ic_ampdu_enable)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
/* start/stop doing A-MPDU tx aggregation for a station */
int (*ic_addba_request)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *,
@@ -294,11 +274,154 @@ struct ieee80211com {
struct ieee80211_tx_ampdu *);
};
+struct ieee80211_aclator;
+
+struct ieee80211vap {
+ struct ifmedia iv_media; /* interface media config */
+ struct ifnet *iv_ifp; /* associated device */
+ struct bpf_if *iv_rawbpf; /* packet filter structure */
+ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */
+ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */
+
+ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
+ struct ieee80211com *iv_ic; /* back ptr to common state */
+ uint32_t iv_debug; /* debug msg flags */
+ struct ieee80211_stats iv_stats; /* statistics */
+
+ uint8_t iv_myaddr[IEEE80211_ADDR_LEN];
+ uint32_t iv_flags; /* state flags */
+ uint32_t iv_flags_ext; /* extended state flags */
+ uint32_t iv_flags_ven; /* vendor state flags */
+ uint32_t iv_caps; /* capabilities */
+ uint32_t iv_htcaps; /* HT capabilities */
+ enum ieee80211_opmode iv_opmode; /* operation mode */
+ enum ieee80211_state iv_state; /* state machine state */
+ void (*iv_newstate_cb)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout iv_mgtsend; /* mgmt frame response timer */
+ /* inactivity timer settings */
+ int iv_inact_init; /* setting for new station */
+ int iv_inact_auth; /* auth but not assoc setting */
+ int iv_inact_run; /* authorized setting */
+ int iv_inact_probe; /* inactive probe time */
+
+ int iv_des_nssid; /* # desired ssids */
+ struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */
+ uint8_t iv_des_bssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_channel *iv_des_chan; /* desired channel */
+ uint16_t iv_des_mode; /* desired mode */
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+ u_int iv_bgscanidle; /* bg scan idle threshold */
+ u_int iv_bgscanintvl; /* bg scan min interval */
+ u_int iv_scanvalid; /* scan cache valid threshold */
+ u_int iv_scanreq_duration;
+ u_int iv_scanreq_mindwell;
+ u_int iv_scanreq_maxdwell;
+ uint16_t iv_scanreq_flags;/* held scan request params */
+ uint8_t iv_scanreq_nssid;
+ struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID];
+ /* sta-mode roaming state */
+ enum ieee80211_roamingmode iv_roaming; /* roaming mode */
+ struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX];
+
+ uint8_t iv_bmissthreshold;
+ uint8_t iv_bmiss_count; /* current beacon miss count */
+ int iv_bmiss_max; /* max bmiss before scan */
+ uint16_t iv_swbmiss_count;/* beacons in last period */
+ uint16_t iv_swbmiss_period;/* s/w bmiss period */
+ struct callout iv_swbmiss; /* s/w beacon miss timer */
+
+ int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
+ int iv_ampdu_density;/* A-MPDU density */
+ int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */
+ int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ u_int iv_ampdu_mintraffic[WME_NUM_AC];
+
+ uint32_t *iv_aid_bitmap; /* association id map */
+ uint16_t iv_max_aid;
+ uint16_t iv_sta_assoc; /* stations associated */
+ uint16_t iv_ps_sta; /* stations in power save */
+ uint16_t iv_ps_pending; /* ps sta's w/ pending frames */
+ uint16_t iv_txseq; /* mcast xmit seq# space */
+ uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */
+ uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/
+ uint8_t iv_dtim_period; /* DTIM period */
+ uint8_t iv_dtim_count; /* DTIM count from last bcn */
+ /* set/unset aid pwrsav state */
+ int iv_csa_count; /* count for doing CSA */
+
+ struct ieee80211_node *iv_bss; /* information for this node */
+ struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX];
+ uint16_t iv_rtsthreshold;
+ uint16_t iv_fragthreshold;
+ int iv_inact_timer; /* inactivity timer wait */
+ /* application-specified IE's to attach to mgt frames */
+ struct ieee80211_appie *iv_appie_beacon;
+ struct ieee80211_appie *iv_appie_probereq;
+ struct ieee80211_appie *iv_appie_proberesp;
+ struct ieee80211_appie *iv_appie_assocreq;
+ struct ieee80211_appie *iv_appie_assocresp;
+ struct ieee80211_appie *iv_appie_wpa;
+ uint8_t *iv_wpa_ie;
+ uint8_t *iv_rsn_ie;
+ uint16_t iv_max_keyix; /* max h/w key index */
+ ieee80211_keyix iv_def_txkey; /* default/group tx key index */
+ struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID];
+ int (*iv_key_alloc)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
+ int (*iv_key_delete)(struct ieee80211vap *,
+ const struct ieee80211_key *);
+ int (*iv_key_set)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+ void (*iv_key_update_begin)(struct ieee80211vap *);
+ void (*iv_key_update_end)(struct ieee80211vap *);
+
+ const struct ieee80211_authenticator *iv_auth; /* authenticator glue */
+ void *iv_ec; /* private auth state */
+
+ const struct ieee80211_aclator *iv_acl; /* acl glue */
+ void *iv_as; /* private aclator state */
+
+ /* operate-mode detach hook */
+ void (*iv_opdetach)(struct ieee80211vap *);
+ /* receive processing */
+ int (*iv_input)(struct ieee80211_node *,
+ struct mbuf *, int rssi, int noise,
+ uint32_t rstamp);
+ void (*iv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, uint32_t);
+ void (*iv_deliver_data)(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+#if 0
+ /* send processing */
+ int (*iv_send_mgmt)(struct ieee80211_node *,
+ int, int);
+#endif
+ /* beacon miss processing */
+ void (*iv_bmiss)(struct ieee80211vap *);
+ /* reset device state after 802.11 parameter/state change */
+ int (*iv_reset)(struct ieee80211vap *, u_long);
+ /* [schedule] beacon frame update */
+ void (*iv_update_beacon)(struct ieee80211vap *, int);
+ /* power save handling */
+ void (*iv_update_ps)(struct ieee80211vap *, int);
+ int (*iv_set_tim)(struct ieee80211_node *, int);
+ /* state machine processing */
+ int (*iv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ /* 802.3 output method for raw frame xmit */
+ int (*iv_output)(struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *);
+};
+MALLOC_DECLARE(M_80211_VAP);
+
#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN)
-/* ic_flags */
-/* NB: bits 0x4c available */
+/* ic_flags/iv_flags */
#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/
#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */
#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */
@@ -322,6 +445,7 @@ struct ieee80211com {
#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
+#define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/
#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */
#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */
#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */
@@ -329,22 +453,32 @@ struct ieee80211com {
#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */
#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */
#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */
+#define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */
#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */
+#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */
/* Atheros protocol-specific flags */
#define IEEE80211_F_ATHEROS \
(IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
/* Check if an Atheros capability was negotiated for use */
-#define IEEE80211_ATH_CAP(ic, ni, bit) \
- ((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
+#define IEEE80211_ATH_CAP(vap, ni, bit) \
+ ((vap)->iv_flags & (ni)->ni_ath_flags & (bit))
-/* ic_flags_ext */
+/* ic_flags_ext/iv_flags_ext */
#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
+#define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
+#define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */
+#define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */
+#define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */
+#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
+#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
+/* NB: immutable: should be set only when creating a vap */
+#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */
#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
@@ -357,12 +491,8 @@ struct ieee80211com {
#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
-/* ic_caps */
-#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
-#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */
-#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */
-#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */
-#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */
+/* ic_caps/iv_caps: device driver capabilities */
+/* 0x2f available */
#define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */
#define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/
#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */
@@ -374,7 +504,7 @@ struct ieee80211com {
#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */
#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */
#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */
-#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */
+/* 0x20000 available */
#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/
@@ -386,10 +516,8 @@ struct ieee80211com {
#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */
/* XXX protection/barker? */
-#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */
-
/*
- * ic_htcaps: HT-specific device/driver capabilities
+ * ic_htcaps/iv_htcaps: HT-specific device/driver capabilities
*
* NB: the low 16-bits are the 802.11 definitions, the upper
* 16-bits are used to define s/w/driver capabilities.
@@ -401,18 +529,23 @@ struct ieee80211com {
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
+int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+int ieee80211_vap_attach(struct ieee80211vap *,
+ ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_vap_detach(struct ieee80211vap *);
const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
void ieee80211_announce_channels(struct ieee80211com *);
-void ieee80211_media_init(struct ieee80211com *,
- ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_drain(struct ieee80211com *);
+void ieee80211_media_init(struct ieee80211com *);
struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t);
+int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
int ieee80211_rate2media(struct ieee80211com *, int,
enum ieee80211_phymode);
int ieee80211_media2rate(int);
@@ -431,14 +564,14 @@ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
* Key update synchronization methods. XXX should not be visible.
*/
static __inline void
-ieee80211_key_update_begin(struct ieee80211com *ic)
+ieee80211_key_update_begin(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_begin(ic);
+ vap->iv_key_update_begin(vap);
}
static __inline void
-ieee80211_key_update_end(struct ieee80211com *ic)
+ieee80211_key_update_end(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_end(ic);
+ vap->iv_key_update_end(vap);
}
/*
@@ -472,13 +605,25 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
}
/*
- * Notify a driver that beacon state has been updated.
+ * Notify a vap that beacon state has been updated.
*/
static __inline void
-ieee80211_beacon_notify(struct ieee80211com *ic, int what)
+ieee80211_beacon_notify(struct ieee80211vap *vap, int what)
+{
+ if (vap->iv_state == IEEE80211_S_RUN)
+ vap->iv_update_beacon(vap, what);
+}
+
+/*
+ * Calculate HT channel promotion flags for a channel.
+ * XXX belongs in ieee80211_ht.h but needs IEEE80211_FEXT_*
+ */
+static __inline int
+ieee80211_htchanflags(const struct ieee80211_channel *c)
{
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_update_beacon(ic, what);
+ return IEEE80211_IS_CHAN_HT40(c) ?
+ IEEE80211_FEXT_HT | IEEE80211_FEXT_USEHT40 :
+ IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FEXT_HT : 0;
}
/*
@@ -525,44 +670,44 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what)
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
#ifdef IEEE80211_DEBUG
-#define ieee80211_msg(_ic, _m) ((_ic)->ic_debug & (_m))
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note(_ic, _fmt, __VA_ARGS__); \
+#define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m))
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note(_vap, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \
} while (0)
-void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
-void ieee80211_note_mac(struct ieee80211com *ic,
- const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
-void ieee80211_note_frame(struct ieee80211com *ic,
- const struct ieee80211_frame *wh, const char *fmt, ...);
-#define ieee80211_msg_debug(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DEBUG)
-#define ieee80211_msg_dumppkts(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS)
-#define ieee80211_msg_input(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_INPUT)
-#define ieee80211_msg_radius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADIUS)
-#define ieee80211_msg_dumpradius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADDUMP)
-#define ieee80211_msg_dumpradkeys(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADKEYS)
-#define ieee80211_msg_scan(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_SCAN)
-#define ieee80211_msg_assoc(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
+void ieee80211_note(struct ieee80211vap *, const char *, ...);
+void ieee80211_note_mac(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...);
+void ieee80211_note_frame(struct ieee80211vap *,
+ const struct ieee80211_frame *, const char *, ...);
+#define ieee80211_msg_debug(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DEBUG)
+#define ieee80211_msg_dumppkts(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS)
+#define ieee80211_msg_input(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_INPUT)
+#define ieee80211_msg_radius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADIUS)
+#define ieee80211_msg_dumpradius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP)
+#define ieee80211_msg_dumpradkeys(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS)
+#define ieee80211_msg_scan(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_SCAN)
+#define ieee80211_msg_assoc(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_ASSOC)
/*
* Emit a debug message about discarding a frame or information
@@ -570,37 +715,37 @@ void ieee80211_note_frame(struct ieee80211com *ic,
* the frame header; the other is for when a header is not
* available or otherwise appropriate.
*/
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\
} while (0)
-void ieee80211_discard_frame(struct ieee80211com *,
+void ieee80211_discard_frame(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_ie(struct ieee80211com *,
+void ieee80211_discard_ie(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_mac(struct ieee80211com *,
+void ieee80211_discard_mac(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
const char *fmt, ...);
#else
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
-#define ieee80211_msg_dumppkts(_ic) 0
-#define ieee80211_msg(_ic, _m) 0
-
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...)
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...)
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...)
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...)
+#define ieee80211_msg_dumppkts(_vap) 0
+#define ieee80211_msg(_vap, _m) 0
+
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...)
#endif
#endif /* _NET80211_IEEE80211_VAR_H_ */
diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c
new file mode 100644
index 0000000..f3d90f9
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 WDS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_input.h>
+
+static void wds_vattach(struct ieee80211vap *);
+static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, u_int32_t rstamp);
+
+void
+ieee80211_wds_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
+}
+
+void
+ieee80211_wds_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+wds_vdetach(struct ieee80211vap *vap)
+{
+ if (vap->iv_bss != NULL) {
+ /* XXX locking? */
+ if (vap->iv_bss->ni_wdsvap == vap)
+ vap->iv_bss->ni_wdsvap = NULL;
+ }
+}
+
+static void
+wds_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = wds_newstate;
+ vap->iv_input = wds_input;
+ vap->iv_recv_mgmt = wds_recv_mgmt;
+ vap->iv_opdetach = wds_vdetach;
+}
+
+static int
+ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni, *obss;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: creating link to %s on channel %u\n", __func__,
+ ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
+
+ /* NB: vap create must specify the bssid for the link */
+ KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
+ /* NB: we should only be called on RUN transition */
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
+
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ /*
+ * Dynamic/non-legacy WDS. Reference the associated
+ * station specified by the desired bssid setup at vap
+ * create. Point ni_wdsvap at the WDS vap so 4-address
+ * frames received through the associated AP vap will
+ * be dispatched upward (e.g. to a bridge) as though
+ * they arrived on the WDS vap.
+ */
+ IEEE80211_NODE_LOCK(nt);
+ obss = NULL;
+ ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
+ if (ni == NULL) {
+ /*
+ * Node went away before we could hookup. This
+ * should be ok; no traffic will flow and a leave
+ * event will be dispatched that should cause
+ * the vap to be destroyed.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s went away\n",
+ __func__, ether_sprintf(vap->iv_des_bssid));
+ /* XXX stat? */
+ } else if (ni->ni_wdsvap != NULL) {
+ /*
+ * Node already setup with a WDS vap; we cannot
+ * allow multiple references so disallow. If
+ * ni_wdsvap points at us that's ok; we should
+ * do nothing anyway.
+ */
+ /* XXX printf instead? */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s in use with %s\n",
+ __func__, ether_sprintf(vap->iv_des_bssid),
+ ni->ni_wdsvap->iv_ifp->if_xname);
+ /* XXX stat? */
+ } else {
+ /*
+ * Committed to new node, setup state.
+ */
+ obss = vap->iv_bss;
+ vap->iv_bss = ni;
+ ni->ni_wdsvap = vap;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ if (obss != NULL) {
+ /* NB: deferred to avoid recursive lock */
+ ieee80211_free_node(obss);
+ }
+ } else {
+ /*
+ * Legacy WDS vap setup.
+ */
+ /*
+ * The far end does not associate so we just create
+ * create a new node and install it as the vap's
+ * bss node. We must simulate an association and
+ * authorize the port for traffic to flow.
+ * XXX check if node already in sta table?
+ */
+ ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
+ if (ni != NULL) {
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ if (obss != NULL)
+ ieee80211_free_node(obss);
+ /* give driver a chance to setup state like ni_txrate */
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
+ /* tell the authenticator about new station */
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+
+ ieee80211_notify_node_join(ni, 1 /*newassoc*/);
+ /* XXX inject l2uf frame */
+ }
+ }
+
+ /*
+ * Flush pending frames now that were setup.
+ */
+ if (ni != NULL && IEEE80211_NODE_WDSQ_QLEN(ni) != 0) {
+ int8_t rssi, noise;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "flush wds queue, %u packets queued",
+ IEEE80211_NODE_WDSQ_QLEN(ni));
+ ic->ic_node_getsignal(ni, &rssi, &noise);
+ for (;;) {
+ struct mbuf *m;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+ if (m == NULL)
+ break;
+ /* XXX cheat and re-use last rstamp */
+ ieee80211_input(ni, m, rssi, noise, ni->ni_rstamp);
+ }
+ }
+ return (ni == NULL ? ENOENT : 0);
+}
+
+/*
+ * Propagate multicast frames of an ap vap to all DWDS links.
+ * The caller is assumed to have verified this frame is multicast.
+ */
+void
+ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ const struct ether_header *eh = mtod(m, const struct ether_header *);
+ struct ieee80211_node *ni;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct mbuf *mcopy;
+ int err;
+
+ KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
+ ("%s not mcast", ether_sprintf(eh->ether_dhost)));
+
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ /* only DWDS vaps are interesting */
+ if (vap->iv_opmode != IEEE80211_M_WDS ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
+ continue;
+ /* if it came in this interface, don't send it back out */
+ ifp = vap->iv_ifp;
+ if (ifp == m->m_pkthdr.rcvif)
+ continue;
+ /*
+ * Duplicate the frame and send it. We don't need
+ * to classify or lookup the tx node; this was already
+ * done by the caller so we can just re-use the info.
+ */
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ ifp->if_oerrors++;
+ /* XXX stat + msg */
+ continue;
+ }
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ continue;
+ }
+ if (ieee80211_classify(ni, mcopy)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
+ eh->ether_dhost, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ mcopy->m_flags |= M_MCAST | M_WDS;
+ mcopy->m_pkthdr.rcvif = (void *) ni;
+
+ IFQ_HANDOFF(parent, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ } else
+ ifp->if_opackets++;
+ }
+}
+
+/*
+ * Handle DWDS discovery on receipt of a 4-address frame in
+ * ap mode. Queue the frame and post an event for someone
+ * to plumb the necessary WDS vap for this station. Frames
+ * received prior to the vap set running will then be reprocessed
+ * as if they were just received.
+ */
+void
+ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ int qlen, age;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ if (!_IF_QFULL(&ni->ni_wdsq)) {
+ /*
+ * Tag the frame with it's expiry time and insert
+ * it in the queue. The aging interval is 4 times
+ * the listen interval specified by the station.
+ * Frames that sit around too long are reclaimed
+ * using this information.
+ */
+ /* XXX handle overflow? */
+ /* XXX per/vap beacon interval? */
+ /* NB: TU -> secs */
+ age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024;
+ _IEEE80211_NODE_WDSQ_ENQUEUE(ni, m, qlen, age);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "save frame, %u now queued", qlen);
+ } else {
+ vap->iv_stats.is_dwds_qdrop++;
+ _IF_DROP(&ni->ni_wdsq);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_WDS,
+ mtod(m, struct ieee80211_frame *), "wds data",
+ "pending q overflow, drops %d (len %d)",
+ ni->ni_wdsq.ifq_drops, ni->ni_wdsq.ifq_len);
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
+#endif
+ /* XXX tail drop? */
+ m_freem(m);
+ }
+ ieee80211_notify_wds_discover(ni);
+}
+
+/*
+ * Age frames on the WDS pending queue. The aging interval is
+ * 4 times the listen interval specified by the station. This
+ * number is factored into the age calculations when the frame
+ * is placed on the queue. We store ages as time differences
+ * so we can check and/or adjust only the head of the list.
+ * If a frame's age exceeds the threshold then discard it.
+ * The number of frames discarded is returned to the caller.
+ */
+int
+ieee80211_node_wdsq_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
+ struct mbuf *m;
+ int discard = 0;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ while (_IF_POLL(&ni->ni_wdsq, m) != NULL &&
+ M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard frame, age %u", M_AGE_GET(m));
+
+ /* XXX could be optimized */
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ m_freem(m);
+ discard++;
+ }
+ if (m != NULL)
+ M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard %u frames for age", discard);
+#if 0
+ IEEE80211_NODE_STAT_ADD(ni, wds_discard, discard);
+#endif
+ return discard;
+}
+
+/*
+ * IEEE80211_M_WDS vap state machine handler.
+ */
+static int
+wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+ int error;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ error = 0;
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (ostate == IEEE80211_S_INIT) {
+ /*
+ * Already have a channel; bypass the scan
+ * and startup immediately.
+ */
+ error = ieee80211_create_wds(vap, ic->ic_curchan);
+ }
+ break;
+ default:
+ break;
+ }
+ return error;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ /* NB: WDS vap's do not scan */
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
+ "too short (3): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /* NB: the TA is implicitly verified by finding the wds peer node */
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
+ !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /*
+ * Only legacy WDS traffic should take this path.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "not legacy wds");
+ vap->iv_stats.is_rx_wrongdir++;/*XXX*/
+ goto out;
+ }
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ ni->ni_inact = ni->ni_inact_reload;
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, u_int32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ u_int8_t *frm, *efrm;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ ni->ni_inact = ni->ni_inact_reload;
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
diff --git a/sys/net80211/ieee80211_wds.h b/sys/net80211/ieee80211_wds.h
new file mode 100644
index 0000000..c34fb6e
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_WDS_H_
+#define _NET80211_IEEE80211_WDS_H_
+
+/*
+ * WDS implementation definitions.
+ */
+void ieee80211_wds_attach(struct ieee80211com *);
+void ieee80211_wds_detach(struct ieee80211com *);
+
+void ieee80211_dwds_mcast(struct ieee80211vap *, struct mbuf *);
+void ieee80211_dwds_discover(struct ieee80211_node *, struct mbuf *);
+int ieee80211_node_wdsq_age(struct ieee80211_node *);
+#endif /* !_NET80211_IEEE80211_WDS_H_ */
diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c
index c829b6e..2341ffb 100644
--- a/sys/net80211/ieee80211_xauth.c
+++ b/sys/net80211/ieee80211_xauth.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
* of the available callbacks--the user mode authenticator process works
* entirely from messages about stations joining and leaving.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -54,6 +56,9 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+/* XXX number of references from net80211 layer; needed for module code */
+static int nrefs = 0;
+
/*
* One module handles everything for now. May want
* to split things up for embedded applications.
@@ -66,30 +71,6 @@ static const struct ieee80211_authenticator xauth = {
.ia_node_leave = NULL,
};
-/*
- * Module glue.
- */
-static int
-wlan_xauth_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth);
- ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth);
- return 0;
- case MOD_UNLOAD:
- ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
- ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_xauth_mod = {
- "wlan_xauth",
- wlan_xauth_modevent,
- 0
-};
-DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_xauth, 1);
-MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1);
+IEEE80211_AUTH_MODULE(xauth, 1);
+IEEE80211_AUTH_ALG(x8021x, IEEE80211_AUTH_8021X, xauth);
+IEEE80211_AUTH_ALG(wpa, IEEE80211_AUTH_WPA, xauth);
diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC
index 91d582d8..80ac05e 100644
--- a/sys/pc98/conf/GENERIC
+++ b/sys/pc98/conf/GENERIC
@@ -215,8 +215,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC
index c5715af..f43731e 100644
--- a/sys/sparc64/conf/GENERIC
+++ b/sys/sparc64/conf/GENERIC
@@ -195,8 +195,6 @@ device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
device ath_rate_sample # SampleRate tx rate control for ath
OpenPOWER on IntegriCloud